aboutsummaryrefslogtreecommitdiff
path: root/drivers/char
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/Kconfig34
-rw-r--r--drivers/char/Makefile3
-rw-r--r--drivers/char/agp/Kconfig5
-rw-r--r--drivers/char/agp/Makefile2
-rw-r--r--drivers/char/agp/agp.h1
-rw-r--r--drivers/char/agp/ali-agp.c4
-rw-r--r--drivers/char/agp/alpha-agp.c2
-rw-r--r--drivers/char/agp/amd-k7-agp.c12
-rw-r--r--drivers/char/agp/amd64-agp.c7
-rw-r--r--drivers/char/agp/ati-agp.c25
-rw-r--r--drivers/char/agp/efficeon-agp.c5
-rw-r--r--drivers/char/agp/frontend.c10
-rw-r--r--drivers/char/agp/generic.c5
-rw-r--r--drivers/char/agp/intel-agp.c53
-rw-r--r--drivers/char/agp/intel-agp.h10
-rw-r--r--drivers/char/agp/intel-gtt.c192
-rw-r--r--drivers/char/agp/nvidia-agp.c15
-rw-r--r--drivers/char/agp/parisc-agp.c8
-rw-r--r--drivers/char/agp/sgi-agp.c1
-rw-r--r--drivers/char/agp/sis-agp.c5
-rw-r--r--drivers/char/agp/via-agp.c13
-rw-r--r--drivers/char/apm-emulation.c11
-rw-r--r--drivers/char/applicom.c5
-rw-r--r--drivers/char/bsr.c19
-rw-r--r--drivers/char/ds1620.c34
-rw-r--r--drivers/char/dsp56k.c8
-rw-r--r--drivers/char/dtlk.c4
-rw-r--r--drivers/char/efirtc.c83
-rw-r--r--drivers/char/genrtc.c48
-rw-r--r--drivers/char/hpet.c56
-rw-r--r--drivers/char/hw_random/Kconfig149
-rw-r--r--drivers/char/hw_random/Makefile5
-rw-r--r--drivers/char/hw_random/atmel-rng.c25
-rw-r--r--drivers/char/hw_random/bcm2835-rng.c112
-rw-r--r--drivers/char/hw_random/bcm63xx-rng.c2
-rw-r--r--drivers/char/hw_random/core.c83
-rw-r--r--drivers/char/hw_random/exynos-rng.c15
-rw-r--r--drivers/char/hw_random/msm-rng.c197
-rw-r--r--drivers/char/hw_random/mxc-rnga.c44
-rw-r--r--drivers/char/hw_random/n2-drv.c31
-rw-r--r--drivers/char/hw_random/nomadik-rng.c16
-rw-r--r--drivers/char/hw_random/octeon-rng.c5
-rw-r--r--drivers/char/hw_random/omap-rng.c401
-rw-r--r--drivers/char/hw_random/omap3-rom-rng.c140
-rw-r--r--drivers/char/hw_random/pasemi-rng.c1
-rw-r--r--drivers/char/hw_random/picoxcell-rng.c198
-rw-r--r--drivers/char/hw_random/powernv-rng.c81
-rw-r--r--drivers/char/hw_random/ppc4xx-rng.c1
-rw-r--r--drivers/char/hw_random/pseries-rng.c19
-rw-r--r--drivers/char/hw_random/timeriomem-rng.c166
-rw-r--r--drivers/char/hw_random/tx4939-rng.c25
-rw-r--r--drivers/char/hw_random/via-rng.c7
-rw-r--r--drivers/char/hw_random/virtio-rng.c130
-rw-r--r--drivers/char/i8k.c369
-rw-r--r--drivers/char/ipmi/Kconfig12
-rw-r--r--drivers/char/ipmi/ipmi_bt_sm.c14
-rw-r--r--drivers/char/ipmi/ipmi_devintf.c15
-rw-r--r--drivers/char/ipmi/ipmi_kcs_sm.c9
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c252
-rw-r--r--drivers/char/ipmi/ipmi_poweroff.c6
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c352
-rw-r--r--drivers/char/ipmi/ipmi_smic_sm.c2
-rw-r--r--drivers/char/lp.c13
-rw-r--r--drivers/char/mem.c106
-rw-r--r--drivers/char/misc.c32
-rw-r--r--drivers/char/msm_smd_pkt.c2
-rw-r--r--drivers/char/mspec.c2
-rw-r--r--drivers/char/mwave/3780i.c1
-rw-r--r--drivers/char/mwave/tp3780i.c1
-rw-r--r--drivers/char/nsc_gpio.c4
-rw-r--r--drivers/char/nwbutton.c7
-rw-r--r--drivers/char/pcmcia/Kconfig8
-rw-r--r--drivers/char/pcmcia/cm4000_cs.c2
-rw-r--r--drivers/char/pcmcia/synclink_cs.c737
-rw-r--r--drivers/char/ppdev.c6
-rw-r--r--drivers/char/ps3flash.c30
-rw-r--r--drivers/char/random.c877
-rw-r--r--drivers/char/raw.c12
-rw-r--r--drivers/char/rtc.c11
-rw-r--r--drivers/char/snsc.c3
-rw-r--r--drivers/char/snsc_event.c3
-rw-r--r--drivers/char/sonypi.c9
-rw-r--r--drivers/char/tb0219.c4
-rw-r--r--drivers/char/tile-srom.c59
-rw-r--r--drivers/char/tlclk.c2
-rw-r--r--drivers/char/tpm/Kconfig47
-rw-r--r--drivers/char/tpm/Makefile13
-rw-r--r--drivers/char/tpm/tpm-dev.c213
-rw-r--r--drivers/char/tpm/tpm-interface.c (renamed from drivers/char/tpm/tpm.c)677
-rw-r--r--drivers/char/tpm/tpm-sysfs.c318
-rw-r--r--drivers/char/tpm/tpm.h139
-rw-r--r--drivers/char/tpm/tpm_acpi.c14
-rw-r--r--drivers/char/tpm/tpm_atmel.c35
-rw-r--r--drivers/char/tpm/tpm_eventlog.c3
-rw-r--r--drivers/char/tpm/tpm_i2c_atmel.c244
-rw-r--r--drivers/char/tpm/tpm_i2c_infineon.c234
-rw-r--r--drivers/char/tpm/tpm_i2c_nuvoton.c669
-rw-r--r--drivers/char/tpm/tpm_i2c_stm_st33.c844
-rw-r--r--drivers/char/tpm/tpm_i2c_stm_st33.h61
-rw-r--r--drivers/char/tpm/tpm_ibmvtpm.c58
-rw-r--r--drivers/char/tpm/tpm_infineon.c28
-rw-r--r--drivers/char/tpm/tpm_nsc.c33
-rw-r--r--drivers/char/tpm/tpm_ppi.c413
-rw-r--r--drivers/char/tpm/tpm_tis.c180
-rw-r--r--drivers/char/tpm/xen-tpmfront.c397
-rw-r--r--drivers/char/ttyprintk.c19
-rw-r--r--drivers/char/virtio_console.c204
-rw-r--r--drivers/char/xilinx_hwicap/xilinx_hwicap.c3
-rw-r--r--drivers/char/xilinx_hwicap/xilinx_hwicap.h10
109 files changed, 6921 insertions, 3430 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 72bedad6bf8..6e9f74a5c09 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -15,18 +15,6 @@ config DEVKMEM
kind of kernel debugging operations.
When in doubt, say "N".
-config STALDRV
- bool "Stallion multiport serial support"
- depends on SERIAL_NONSTANDARD
- help
- Stallion cards give you many serial ports. You would need something
- like this to connect more than two modems to your Linux box, for
- instance in order to become a dial-in server. If you say Y here,
- you will be asked for your specific card model in the next
- questions. Make sure to read <file:Documentation/serial/stallion.txt>
- in this case. If you have never heard about all this, it's safe to
- say N.
-
config SGI_SNSC
bool "SGI Altix system controller communication support"
depends on (IA64_SGI_SN2 || IA64_GENERIC)
@@ -52,8 +40,8 @@ config SGI_MBCS
source "drivers/tty/serial/Kconfig"
config TTY_PRINTK
- bool "TTY driver to output user messages via printk"
- depends on EXPERT
+ tristate "TTY driver to output user messages via printk"
+ depends on EXPERT && TTY
default n
---help---
If you say Y here, the support for writing user messages (i.e.
@@ -159,7 +147,7 @@ source "drivers/tty/hvc/Kconfig"
config VIRTIO_CONSOLE
tristate "Virtio console"
- depends on VIRTIO
+ depends on VIRTIO && TTY
select HVC_DRIVER
help
Virtio console for use with lguest and other hypervisors.
@@ -392,6 +380,7 @@ config XILINX_HWICAP
config R3964
tristate "Siemens R3964 line discipline"
+ depends on TTY
---help---
This driver allows synchronous communication with devices using the
Siemens R3964 packet protocol. Unless you are dealing with special
@@ -419,7 +408,7 @@ config APPLICOM
config SONYPI
tristate "Sony Vaio Programmable I/O Control Device support"
- depends on X86 && PCI && INPUT && !64BIT
+ depends on X86_32 && PCI && INPUT
---help---
This driver enables access to the Sony Programmable I/O Control
Device which can be found in many (all ?) Sony Vaio laptops.
@@ -439,7 +428,7 @@ source "drivers/char/pcmcia/Kconfig"
config MWAVE
tristate "ACP Modem (Mwave) support"
- depends on X86
+ depends on X86 && TTY
select SERIAL_8250
---help---
The ACP modem (Mwave) for Linux is a WinModem. It is composed of a
@@ -510,6 +499,7 @@ config RAW_DRIVER
config MAX_RAW_DEVS
int "Maximum number of RAW devices to support (1-65536)"
depends on RAW_DRIVER
+ range 1 65536
default "256"
help
The maximum number of RAW devices that are supported.
@@ -533,10 +523,16 @@ config HPET_MMAP
If you say Y here, user applications will be able to mmap
the HPET registers.
+config HPET_MMAP_DEFAULT
+ bool "Enable HPET MMAP access by default"
+ default y
+ depends on HPET_MMAP
+ help
In some hardware implementations, the page containing HPET
registers may also contain other things that shouldn't be
- exposed to the user. If this applies to your hardware,
- say N here.
+ exposed to the user. This option selects the default (if
+ kernel parameter hpet_mmap is not set) user access to the
+ registers for applications that require it.
config HANGCHECK_TIMER
tristate "Hangcheck timer"
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 7ff1d0d208a..a324f9303e3 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -13,7 +13,6 @@ obj-$(CONFIG_MSM_SMD_PKT) += msm_smd_pkt.o
obj-$(CONFIG_MSPEC) += mspec.o
obj-$(CONFIG_MMTIMER) += mmtimer.o
obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o
-obj-$(CONFIG_VIOTAPE) += viotape.o
obj-$(CONFIG_IBM_BSR) += bsr.o
obj-$(CONFIG_SGI_MBCS) += mbcs.o
obj-$(CONFIG_BFIN_OTP) += bfin-otp.o
@@ -50,7 +49,7 @@ obj-$(CONFIG_GPIO_TB0219) += tb0219.o
obj-$(CONFIG_TELCLOCK) += tlclk.o
obj-$(CONFIG_MWAVE) += mwave/
-obj-$(CONFIG_AGP) += agp/
+obj-y += agp/
obj-$(CONFIG_PCMCIA) += pcmcia/
obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
diff --git a/drivers/char/agp/Kconfig b/drivers/char/agp/Kconfig
index d8b1b576556..c528f96ee20 100644
--- a/drivers/char/agp/Kconfig
+++ b/drivers/char/agp/Kconfig
@@ -68,6 +68,7 @@ config AGP_AMD64
config AGP_INTEL
tristate "Intel 440LX/BX/GX, I8xx and E7x05 chipset support"
depends on AGP && X86
+ select INTEL_GTT
help
This option gives you AGP support for the GLX component of X
on Intel 440LX/BX/GX, 815, 820, 830, 840, 845, 850, 860, 875,
@@ -155,3 +156,7 @@ config AGP_SGI_TIOCA
This option gives you AGP GART support for the SGI TIO chipset
for IA64 processors.
+config INTEL_GTT
+ tristate
+ depends on X86 && PCI
+
diff --git a/drivers/char/agp/Makefile b/drivers/char/agp/Makefile
index 8eb56e273e7..604489bcdbf 100644
--- a/drivers/char/agp/Makefile
+++ b/drivers/char/agp/Makefile
@@ -13,7 +13,7 @@ obj-$(CONFIG_AGP_HP_ZX1) += hp-agp.o
obj-$(CONFIG_AGP_PARISC) += parisc-agp.o
obj-$(CONFIG_AGP_I460) += i460-agp.o
obj-$(CONFIG_AGP_INTEL) += intel-agp.o
-obj-$(CONFIG_AGP_INTEL) += intel-gtt.o
+obj-$(CONFIG_INTEL_GTT) += intel-gtt.o
obj-$(CONFIG_AGP_NVIDIA) += nvidia-agp.o
obj-$(CONFIG_AGP_SGI_TIOCA) += sgi-agp.o
obj-$(CONFIG_AGP_SIS) += sis-agp.o
diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h
index 923f99df4f1..b709749c863 100644
--- a/drivers/char/agp/agp.h
+++ b/drivers/char/agp/agp.h
@@ -239,6 +239,7 @@ long compat_agp_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
/* Chipset independent registers (from AGP Spec) */
#define AGP_APBASE 0x10
+#define AGP_APERTURE_BAR 0
#define AGPSTAT 0x4
#define AGPCMD 0x8
diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c
index 443cd6751ca..19db0366765 100644
--- a/drivers/char/agp/ali-agp.c
+++ b/drivers/char/agp/ali-agp.c
@@ -85,8 +85,8 @@ static int ali_configure(void)
pci_write_config_dword(agp_bridge->dev, ALI_TLBCTRL, ((temp & 0xffffff00) | 0x00000010));
/* address to map to */
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
+ AGP_APERTURE_BAR);
#if 0
if (agp_bridge->type == ALI_M1541) {
diff --git a/drivers/char/agp/alpha-agp.c b/drivers/char/agp/alpha-agp.c
index dd84af4d4f7..199b8e99f7d 100644
--- a/drivers/char/agp/alpha-agp.c
+++ b/drivers/char/agp/alpha-agp.c
@@ -174,7 +174,7 @@ alpha_core_agp_setup(void)
/*
* Build a fake pci_dev struct
*/
- pdev = alloc_pci_dev();
+ pdev = pci_alloc_dev(NULL);
if (!pdev)
return -ENOMEM;
pdev->vendor = 0xffff;
diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c
index 779f0ab845a..3661a51e93e 100644
--- a/drivers/char/agp/amd-k7-agp.c
+++ b/drivers/char/agp/amd-k7-agp.c
@@ -11,7 +11,7 @@
#include <linux/slab.h>
#include "agp.h"
-#define AMD_MMBASE 0x14
+#define AMD_MMBASE_BAR 1
#define AMD_APSIZE 0xac
#define AMD_MODECNTL 0xb0
#define AMD_MODECNTL2 0xb2
@@ -126,7 +126,6 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge)
unsigned long __iomem *cur_gatt;
unsigned long addr;
int retval;
- u32 temp;
int i;
value = A_SIZE_LVL2(agp_bridge->current_size);
@@ -149,8 +148,7 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge)
* used to program the agp master not the cpu
*/
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ addr = pci_bus_address(agp_bridge->dev, AGP_APERTURE_BAR);
agp_bridge->gart_bus_addr = addr;
/* Calculate the agp offset */
@@ -207,6 +205,7 @@ static int amd_irongate_fetch_size(void)
static int amd_irongate_configure(void)
{
struct aper_size_info_lvl2 *current_size;
+ phys_addr_t reg;
u32 temp;
u16 enable_reg;
@@ -214,9 +213,8 @@ static int amd_irongate_configure(void)
if (!amd_irongate_private.registers) {
/* Get the memory mapped registers */
- pci_read_config_dword(agp_bridge->dev, AMD_MMBASE, &temp);
- temp = (temp & PCI_BASE_ADDRESS_MEM_MASK);
- amd_irongate_private.registers = (volatile u8 __iomem *) ioremap(temp, 4096);
+ reg = pci_resource_start(agp_bridge->dev, AMD_MMBASE_BAR);
+ amd_irongate_private.registers = (volatile u8 __iomem *) ioremap(reg, 4096);
if (!amd_irongate_private.registers)
return -ENOMEM;
}
diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c
index d79d692d05b..3b47ed0310e 100644
--- a/drivers/char/agp/amd64-agp.c
+++ b/drivers/char/agp/amd64-agp.c
@@ -269,7 +269,6 @@ static int agp_aperture_valid(u64 aper, u32 size)
*/
static int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp, u16 cap)
{
- u32 aper_low, aper_hi;
u64 aper, nb_aper;
int order = 0;
u32 nb_order, nb_base;
@@ -295,9 +294,7 @@ static int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp, u16 cap)
apsize |= 0xf00;
order = 7 - hweight16(apsize);
- pci_read_config_dword(agp, 0x10, &aper_low);
- pci_read_config_dword(agp, 0x14, &aper_hi);
- aper = (aper_low & ~((1<<22)-1)) | ((u64)aper_hi << 32);
+ aper = pci_bus_address(agp, AGP_APERTURE_BAR);
/*
* On some sick chips APSIZE is 0. This means it wants 4G
@@ -735,7 +732,7 @@ static struct pci_device_id agp_amd64_pci_table[] = {
MODULE_DEVICE_TABLE(pci, agp_amd64_pci_table);
-static DEFINE_PCI_DEVICE_TABLE(agp_amd64_pci_promisc_table) = {
+static const struct pci_device_id agp_amd64_pci_promisc_table[] = {
{ PCI_DEVICE_CLASS(0, 0) },
{ }
};
diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c
index 0628d7b65c7..18a7a6baa30 100644
--- a/drivers/char/agp/ati-agp.c
+++ b/drivers/char/agp/ati-agp.c
@@ -12,7 +12,7 @@
#include <asm/agp.h>
#include "agp.h"
-#define ATI_GART_MMBASE_ADDR 0x14
+#define ATI_GART_MMBASE_BAR 1
#define ATI_RS100_APSIZE 0xac
#define ATI_RS100_IG_AGPMODE 0xb0
#define ATI_RS300_APSIZE 0xf8
@@ -196,12 +196,12 @@ static void ati_cleanup(void)
static int ati_configure(void)
{
+ phys_addr_t reg;
u32 temp;
/* Get the memory mapped registers */
- pci_read_config_dword(agp_bridge->dev, ATI_GART_MMBASE_ADDR, &temp);
- temp = (temp & 0xfffff000);
- ati_generic_private.registers = (volatile u8 __iomem *) ioremap(temp, 4096);
+ reg = pci_resource_start(agp_bridge->dev, ATI_GART_MMBASE_BAR);
+ ati_generic_private.registers = (volatile u8 __iomem *) ioremap(reg, 4096);
if (!ati_generic_private.registers)
return -ENOMEM;
@@ -211,18 +211,18 @@ static int ati_configure(void)
else
pci_write_config_dword(agp_bridge->dev, ATI_RS300_IG_AGPMODE, 0x20000);
- /* address to map too */
+ /* address to map to */
/*
- pci_read_config_dword(agp_bridge.dev, AGP_APBASE, &temp);
- agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ agp_bridge.gart_bus_addr = pci_bus_address(agp_bridge.dev,
+ AGP_APERTURE_BAR);
printk(KERN_INFO PFX "IGP320 gart_bus_addr: %x\n", agp_bridge.gart_bus_addr);
*/
writel(0x60000, ati_generic_private.registers+ATI_GART_FEATURE_ID);
readl(ati_generic_private.registers+ATI_GART_FEATURE_ID); /* PCI Posting.*/
/* SIGNALED_SYSTEM_ERROR @ NB_STATUS */
- pci_read_config_dword(agp_bridge->dev, 4, &temp);
- pci_write_config_dword(agp_bridge->dev, 4, temp | (1<<14));
+ pci_read_config_dword(agp_bridge->dev, PCI_COMMAND, &temp);
+ pci_write_config_dword(agp_bridge->dev, PCI_COMMAND, temp | (1<<14));
/* Write out the address of the gatt table */
writel(agp_bridge->gatt_bus_addr, ati_generic_private.registers+ATI_GART_BASE);
@@ -236,14 +236,14 @@ static int ati_configure(void)
static int agp_ati_suspend(struct pci_dev *dev, pm_message_t state)
{
pci_save_state(dev);
- pci_set_power_state(dev, 3);
+ pci_set_power_state(dev, PCI_D3hot);
return 0;
}
static int agp_ati_resume(struct pci_dev *dev)
{
- pci_set_power_state(dev, 0);
+ pci_set_power_state(dev, PCI_D0);
pci_restore_state(dev);
return ati_configure();
@@ -385,8 +385,7 @@ static int ati_create_gatt_table(struct agp_bridge_data *bridge)
* This is a bus address even on the alpha, b/c its
* used to program the agp master not the cpu
*/
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ addr = pci_bus_address(agp_bridge->dev, AGP_APERTURE_BAR);
agp_bridge->gart_bus_addr = addr;
/* Calculate the agp offset */
diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c
index 6974d503205..533cb6d229b 100644
--- a/drivers/char/agp/efficeon-agp.c
+++ b/drivers/char/agp/efficeon-agp.c
@@ -128,7 +128,6 @@ static void efficeon_cleanup(void)
static int efficeon_configure(void)
{
- u32 temp;
u16 temp2;
struct aper_size_info_lvl2 *current_size;
@@ -141,8 +140,8 @@ static int efficeon_configure(void)
current_size->size_value);
/* address to map to */
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
+ AGP_APERTURE_BAR);
/* agpctrl */
pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x2280);
diff --git a/drivers/char/agp/frontend.c b/drivers/char/agp/frontend.c
index 2e044338753..b29703324e9 100644
--- a/drivers/char/agp/frontend.c
+++ b/drivers/char/agp/frontend.c
@@ -31,7 +31,6 @@
#include <linux/module.h>
#include <linux/mman.h>
#include <linux/pci.h>
-#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/agp_backend.h>
#include <linux/agpgart.h>
@@ -603,7 +602,8 @@ static int agp_mmap(struct file *file, struct vm_area_struct *vma)
vma->vm_ops = kerninfo.vm_ops;
} else if (io_remap_pfn_range(vma, vma->vm_start,
(kerninfo.aper_base + offset) >> PAGE_SHIFT,
- size, vma->vm_page_prot)) {
+ size,
+ pgprot_writecombine(vma->vm_page_prot))) {
goto out_again;
}
mutex_unlock(&(agp_fe.agp_mutex));
@@ -618,8 +618,9 @@ static int agp_mmap(struct file *file, struct vm_area_struct *vma)
if (kerninfo.vm_ops) {
vma->vm_ops = kerninfo.vm_ops;
} else if (io_remap_pfn_range(vma, vma->vm_start,
- kerninfo.aper_base >> PAGE_SHIFT,
- size, vma->vm_page_prot)) {
+ kerninfo.aper_base >> PAGE_SHIFT,
+ size,
+ pgprot_writecombine(vma->vm_page_prot))) {
goto out_again;
}
mutex_unlock(&(agp_fe.agp_mutex));
@@ -729,6 +730,7 @@ static int agpioc_info_wrap(struct agp_file_private *priv, void __user *arg)
agp_copy_info(agp_bridge, &kerninfo);
+ memset(&userinfo, 0, sizeof(userinfo));
userinfo.version.major = kerninfo.version.major;
userinfo.version.minor = kerninfo.version.minor;
userinfo.bridge_id = kerninfo.device->vendor |
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index a0df182f6f7..0fbccce1cee 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -29,7 +29,6 @@
*/
#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/miscdevice.h>
#include <linux/pm.h>
@@ -1396,8 +1395,8 @@ int agp3_generic_configure(void)
current_size = A_SIZE_16(agp_bridge->current_size);
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
+ AGP_APERTURE_BAR);
/* set aperture size */
pci_write_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, current_size->size_value);
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index a426ee1f57a..f9b9ca5d31b 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -14,9 +14,6 @@
#include "intel-agp.h"
#include <drm/intel-gtt.h>
-int intel_agp_enabled;
-EXPORT_SYMBOL(intel_agp_enabled);
-
static int intel_fetch_size(void)
{
int i;
@@ -118,7 +115,6 @@ static void intel_8xx_cleanup(void)
static int intel_configure(void)
{
- u32 temp;
u16 temp2;
struct aper_size_info_16 *current_size;
@@ -128,8 +124,8 @@ static int intel_configure(void)
pci_write_config_word(agp_bridge->dev, INTEL_APSIZE, current_size->size_value);
/* address to map to */
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
+ AGP_APERTURE_BAR);
/* attbase - aperture base */
pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr);
@@ -148,7 +144,7 @@ static int intel_configure(void)
static int intel_815_configure(void)
{
- u32 temp, addr;
+ u32 addr;
u8 temp2;
struct aper_size_info_8 *current_size;
@@ -167,8 +163,8 @@ static int intel_815_configure(void)
current_size->size_value);
/* address to map to */
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
+ AGP_APERTURE_BAR);
pci_read_config_dword(agp_bridge->dev, INTEL_ATTBASE, &addr);
addr &= INTEL_815_ATTBASE_MASK;
@@ -208,7 +204,6 @@ static void intel_820_cleanup(void)
static int intel_820_configure(void)
{
- u32 temp;
u8 temp2;
struct aper_size_info_8 *current_size;
@@ -218,8 +213,8 @@ static int intel_820_configure(void)
pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value);
/* address to map to */
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
+ AGP_APERTURE_BAR);
/* attbase - aperture base */
pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr);
@@ -239,7 +234,6 @@ static int intel_820_configure(void)
static int intel_840_configure(void)
{
- u32 temp;
u16 temp2;
struct aper_size_info_8 *current_size;
@@ -249,8 +243,8 @@ static int intel_840_configure(void)
pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value);
/* address to map to */
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
+ AGP_APERTURE_BAR);
/* attbase - aperture base */
pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr);
@@ -268,7 +262,6 @@ static int intel_840_configure(void)
static int intel_845_configure(void)
{
- u32 temp;
u8 temp2;
struct aper_size_info_8 *current_size;
@@ -282,9 +275,9 @@ static int intel_845_configure(void)
agp_bridge->apbase_config);
} else {
/* address to map to */
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
- agp_bridge->apbase_config = temp;
+ agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
+ AGP_APERTURE_BAR);
+ agp_bridge->apbase_config = agp_bridge->gart_bus_addr;
}
/* attbase - aperture base */
@@ -303,7 +296,6 @@ static int intel_845_configure(void)
static int intel_850_configure(void)
{
- u32 temp;
u16 temp2;
struct aper_size_info_8 *current_size;
@@ -313,8 +305,8 @@ static int intel_850_configure(void)
pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value);
/* address to map to */
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
+ AGP_APERTURE_BAR);
/* attbase - aperture base */
pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr);
@@ -332,7 +324,6 @@ static int intel_850_configure(void)
static int intel_860_configure(void)
{
- u32 temp;
u16 temp2;
struct aper_size_info_8 *current_size;
@@ -342,8 +333,8 @@ static int intel_860_configure(void)
pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value);
/* address to map to */
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
+ AGP_APERTURE_BAR);
/* attbase - aperture base */
pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr);
@@ -361,7 +352,6 @@ static int intel_860_configure(void)
static int intel_830mp_configure(void)
{
- u32 temp;
u16 temp2;
struct aper_size_info_8 *current_size;
@@ -371,8 +361,8 @@ static int intel_830mp_configure(void)
pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value);
/* address to map to */
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
+ AGP_APERTURE_BAR);
/* attbase - aperture base */
pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr);
@@ -390,7 +380,6 @@ static int intel_830mp_configure(void)
static int intel_7505_configure(void)
{
- u32 temp;
u16 temp2;
struct aper_size_info_8 *current_size;
@@ -400,8 +389,8 @@ static int intel_7505_configure(void)
pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value);
/* address to map to */
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
+ AGP_APERTURE_BAR);
/* attbase - aperture base */
pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr);
@@ -814,8 +803,6 @@ static int agp_intel_probe(struct pci_dev *pdev,
found_gmch:
pci_set_drvdata(pdev, bridge);
err = agp_add_bridge(bridge);
- if (!err)
- intel_agp_enabled = 1;
return err;
}
diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h
index 1042c1b9037..fda073dcd96 100644
--- a/drivers/char/agp/intel-agp.h
+++ b/drivers/char/agp/intel-agp.h
@@ -55,8 +55,8 @@
#define INTEL_I860_ERRSTS 0xc8
/* Intel i810 registers */
-#define I810_GMADDR 0x10
-#define I810_MMADDR 0x14
+#define I810_GMADR_BAR 0
+#define I810_MMADR_BAR 1
#define I810_PTE_BASE 0x10000
#define I810_PTE_MAIN_UNCACHED 0x00000000
#define I810_PTE_LOCAL 0x00000002
@@ -113,9 +113,9 @@
#define INTEL_I850_ERRSTS 0xc8
/* intel 915G registers */
-#define I915_GMADDR 0x18
-#define I915_MMADDR 0x10
-#define I915_PTEADDR 0x1C
+#define I915_GMADR_BAR 2
+#define I915_MMADR_BAR 0
+#define I915_PTE_BAR 3
#define I915_GMCH_GMS_STOLEN_48M (0x6 << 4)
#define I915_GMCH_GMS_STOLEN_64M (0x7 << 4)
#define G33_GMCH_GMS_STOLEN_128M (0x8 << 4)
diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c
index dbd901e94ea..9a024f899dd 100644
--- a/drivers/char/agp/intel-gtt.c
+++ b/drivers/char/agp/intel-gtt.c
@@ -17,7 +17,6 @@
#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/pagemap.h>
#include <linux/agp_backend.h>
@@ -60,12 +59,11 @@ struct intel_gtt_driver {
};
static struct _intel_private {
- struct intel_gtt base;
const struct intel_gtt_driver *driver;
struct pci_dev *pcidev; /* device one */
struct pci_dev *bridge_dev;
u8 __iomem *registers;
- phys_addr_t gtt_bus_addr;
+ phys_addr_t gtt_phys_addr;
u32 PGETBL_save;
u32 __iomem *gtt; /* I915G */
bool clear_fake_agp; /* on first access via agp, fill with scratch */
@@ -75,7 +73,18 @@ static struct _intel_private {
struct resource ifp_resource;
int resource_valid;
struct page *scratch_page;
+ phys_addr_t scratch_page_dma;
int refcount;
+ /* Whether i915 needs to use the dmar apis or not. */
+ unsigned int needs_dmar : 1;
+ phys_addr_t gma_bus_addr;
+ /* Size of memory reserved for graphics by the BIOS */
+ unsigned int stolen_size;
+ /* Total number of gtt entries. */
+ unsigned int gtt_total_entries;
+ /* Part of the gtt that is mappable by the cpu, for those chips where
+ * this is not the full gtt. */
+ unsigned int gtt_mappable_entries;
} intel_private;
#define INTEL_GTT_GEN intel_private.driver->gen
@@ -84,6 +93,7 @@ static struct _intel_private {
#define IS_IRONLAKE intel_private.driver->is_ironlake
#define HAS_PGTBL_EN intel_private.driver->has_pgtbl_enable
+#if IS_ENABLED(CONFIG_AGP_INTEL)
static int intel_gtt_map_memory(struct page **pages,
unsigned int num_entries,
struct sg_table *st)
@@ -158,11 +168,12 @@ static void i8xx_destroy_pages(struct page *page)
__free_pages(page, 2);
atomic_dec(&agp_bridge->current_memory_agp);
}
+#endif
#define I810_GTT_ORDER 4
static int i810_setup(void)
{
- u32 reg_addr;
+ phys_addr_t reg_addr;
char *gtt_table;
/* i81x does not preallocate the gtt. It's always 64kb in size. */
@@ -171,8 +182,7 @@ static int i810_setup(void)
return -ENOMEM;
intel_private.i81x_gtt_table = gtt_table;
- pci_read_config_dword(intel_private.pcidev, I810_MMADDR, &reg_addr);
- reg_addr &= 0xfff80000;
+ reg_addr = pci_resource_start(intel_private.pcidev, I810_MMADR_BAR);
intel_private.registers = ioremap(reg_addr, KB(64));
if (!intel_private.registers)
@@ -181,7 +191,7 @@ static int i810_setup(void)
writel(virt_to_phys(gtt_table) | I810_PGETBL_ENABLED,
intel_private.registers+I810_PGETBL_CTL);
- intel_private.gtt_bus_addr = reg_addr + I810_PTE_BASE;
+ intel_private.gtt_phys_addr = reg_addr + I810_PTE_BASE;
if ((readl(intel_private.registers+I810_DRAM_CTL)
& I810_DRAM_ROW_0) == I810_DRAM_ROW_0_SDRAM) {
@@ -199,6 +209,7 @@ static void i810_cleanup(void)
free_gatt_pages(intel_private.i81x_gtt_table, I810_GTT_ORDER);
}
+#if IS_ENABLED(CONFIG_AGP_INTEL)
static int i810_insert_dcache_entries(struct agp_memory *mem, off_t pg_start,
int type)
{
@@ -279,6 +290,7 @@ static void intel_i810_free_by_type(struct agp_memory *curr)
}
kfree(curr);
}
+#endif
static int intel_gtt_setup_scratch_page(void)
{
@@ -291,15 +303,15 @@ static int intel_gtt_setup_scratch_page(void)
get_page(page);
set_pages_uc(page, 1);
- if (intel_private.base.needs_dmar) {
+ if (intel_private.needs_dmar) {
dma_addr = pci_map_page(intel_private.pcidev, page, 0,
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
if (pci_dma_mapping_error(intel_private.pcidev, dma_addr))
return -EINVAL;
- intel_private.base.scratch_page_dma = dma_addr;
+ intel_private.scratch_page_dma = dma_addr;
} else
- intel_private.base.scratch_page_dma = page_to_phys(page);
+ intel_private.scratch_page_dma = page_to_phys(page);
intel_private.scratch_page = page;
@@ -506,7 +518,7 @@ static unsigned int intel_gtt_total_entries(void)
/* On previous hardware, the GTT size was just what was
* required to map the aperture.
*/
- return intel_private.base.gtt_mappable_entries;
+ return intel_private.gtt_mappable_entries;
}
}
@@ -546,7 +558,7 @@ static unsigned int intel_gtt_mappable_entries(void)
static void intel_gtt_teardown_scratch_page(void)
{
set_pages_wb(intel_private.scratch_page, 1);
- pci_unmap_page(intel_private.pcidev, intel_private.base.scratch_page_dma,
+ pci_unmap_page(intel_private.pcidev, intel_private.scratch_page_dma,
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
put_page(intel_private.scratch_page);
__free_page(intel_private.scratch_page);
@@ -562,18 +574,51 @@ static void intel_gtt_cleanup(void)
intel_gtt_teardown_scratch_page();
}
+/* Certain Gen5 chipsets require require idling the GPU before
+ * unmapping anything from the GTT when VT-d is enabled.
+ */
+static inline int needs_ilk_vtd_wa(void)
+{
+#ifdef CONFIG_INTEL_IOMMU
+ const unsigned short gpu_devid = intel_private.pcidev->device;
+
+ /* Query intel_iommu to see if we need the workaround. Presumably that
+ * was loaded first.
+ */
+ if ((gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB ||
+ gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG) &&
+ intel_iommu_gfx_mapped)
+ return 1;
+#endif
+ return 0;
+}
+
+static bool intel_gtt_can_wc(void)
+{
+ if (INTEL_GTT_GEN <= 2)
+ return false;
+
+ if (INTEL_GTT_GEN >= 6)
+ return false;
+
+ /* Reports of major corruption with ILK vt'd enabled */
+ if (needs_ilk_vtd_wa())
+ return false;
+
+ return true;
+}
+
static int intel_gtt_init(void)
{
- u32 gma_addr;
u32 gtt_map_size;
- int ret;
+ int ret, bar;
ret = intel_private.driver->setup();
if (ret != 0)
return ret;
- intel_private.base.gtt_mappable_entries = intel_gtt_mappable_entries();
- intel_private.base.gtt_total_entries = intel_gtt_total_entries();
+ intel_private.gtt_mappable_entries = intel_gtt_mappable_entries();
+ intel_private.gtt_total_entries = intel_gtt_total_entries();
/* save the PGETBL reg for resume */
intel_private.PGETBL_save =
@@ -585,30 +630,31 @@ static int intel_gtt_init(void)
dev_info(&intel_private.bridge_dev->dev,
"detected gtt size: %dK total, %dK mappable\n",
- intel_private.base.gtt_total_entries * 4,
- intel_private.base.gtt_mappable_entries * 4);
+ intel_private.gtt_total_entries * 4,
+ intel_private.gtt_mappable_entries * 4);
- gtt_map_size = intel_private.base.gtt_total_entries * 4;
+ gtt_map_size = intel_private.gtt_total_entries * 4;
intel_private.gtt = NULL;
- if (INTEL_GTT_GEN < 6 && INTEL_GTT_GEN > 2)
- intel_private.gtt = ioremap_wc(intel_private.gtt_bus_addr,
+ if (intel_gtt_can_wc())
+ intel_private.gtt = ioremap_wc(intel_private.gtt_phys_addr,
gtt_map_size);
if (intel_private.gtt == NULL)
- intel_private.gtt = ioremap(intel_private.gtt_bus_addr,
+ intel_private.gtt = ioremap(intel_private.gtt_phys_addr,
gtt_map_size);
if (intel_private.gtt == NULL) {
intel_private.driver->cleanup();
iounmap(intel_private.registers);
return -ENOMEM;
}
- intel_private.base.gtt = intel_private.gtt;
+#if IS_ENABLED(CONFIG_AGP_INTEL)
global_cache_flush(); /* FIXME: ? */
+#endif
- intel_private.base.stolen_size = intel_gtt_stolen_size();
+ intel_private.stolen_size = intel_gtt_stolen_size();
- intel_private.base.needs_dmar = USE_PCI_DMA_API && INTEL_GTT_GEN > 2;
+ intel_private.needs_dmar = USE_PCI_DMA_API && INTEL_GTT_GEN > 2;
ret = intel_gtt_setup_scratch_page();
if (ret != 0) {
@@ -617,25 +663,22 @@ static int intel_gtt_init(void)
}
if (INTEL_GTT_GEN <= 2)
- pci_read_config_dword(intel_private.pcidev, I810_GMADDR,
- &gma_addr);
+ bar = I810_GMADR_BAR;
else
- pci_read_config_dword(intel_private.pcidev, I915_GMADDR,
- &gma_addr);
-
- intel_private.base.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK);
+ bar = I915_GMADR_BAR;
+ intel_private.gma_bus_addr = pci_bus_address(intel_private.pcidev, bar);
return 0;
}
+#if IS_ENABLED(CONFIG_AGP_INTEL)
static int intel_fake_agp_fetch_size(void)
{
int num_sizes = ARRAY_SIZE(intel_fake_agp_sizes);
unsigned int aper_size;
int i;
- aper_size = (intel_private.base.gtt_mappable_entries << PAGE_SHIFT)
- / MB(1);
+ aper_size = (intel_private.gtt_mappable_entries << PAGE_SHIFT) / MB(1);
for (i = 0; i < num_sizes; i++) {
if (aper_size == intel_fake_agp_sizes[i].size) {
@@ -647,6 +690,7 @@ static int intel_fake_agp_fetch_size(void)
return 0;
}
+#endif
static void i830_cleanup(void)
{
@@ -745,20 +789,20 @@ EXPORT_SYMBOL(intel_enable_gtt);
static int i830_setup(void)
{
- u32 reg_addr;
+ phys_addr_t reg_addr;
- pci_read_config_dword(intel_private.pcidev, I810_MMADDR, &reg_addr);
- reg_addr &= 0xfff80000;
+ reg_addr = pci_resource_start(intel_private.pcidev, I810_MMADR_BAR);
intel_private.registers = ioremap(reg_addr, KB(64));
if (!intel_private.registers)
return -ENOMEM;
- intel_private.gtt_bus_addr = reg_addr + I810_PTE_BASE;
+ intel_private.gtt_phys_addr = reg_addr + I810_PTE_BASE;
return 0;
}
+#if IS_ENABLED(CONFIG_AGP_INTEL)
static int intel_fake_agp_create_gatt_table(struct agp_bridge_data *bridge)
{
agp_bridge->gatt_table_real = NULL;
@@ -779,10 +823,11 @@ static int intel_fake_agp_configure(void)
return -EIO;
intel_private.clear_fake_agp = true;
- agp_bridge->gart_bus_addr = intel_private.base.gma_bus_addr;
+ agp_bridge->gart_bus_addr = intel_private.gma_bus_addr;
return 0;
}
+#endif
static bool i830_check_flags(unsigned int flags)
{
@@ -821,6 +866,7 @@ void intel_gtt_insert_sg_entries(struct sg_table *st,
}
EXPORT_SYMBOL(intel_gtt_insert_sg_entries);
+#if IS_ENABLED(CONFIG_AGP_INTEL)
static void intel_gtt_insert_pages(unsigned int first_entry,
unsigned int num_entries,
struct page **pages,
@@ -841,12 +887,9 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem,
{
int ret = -EINVAL;
- if (intel_private.base.do_idle_maps)
- return -ENODEV;
-
if (intel_private.clear_fake_agp) {
- int start = intel_private.base.stolen_size / PAGE_SIZE;
- int end = intel_private.base.gtt_mappable_entries;
+ int start = intel_private.stolen_size / PAGE_SIZE;
+ int end = intel_private.gtt_mappable_entries;
intel_gtt_clear_range(start, end - start);
intel_private.clear_fake_agp = false;
}
@@ -857,7 +900,7 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem,
if (mem->page_count == 0)
goto out;
- if (pg_start + mem->page_count > intel_private.base.gtt_total_entries)
+ if (pg_start + mem->page_count > intel_private.gtt_total_entries)
goto out_err;
if (type != mem->type)
@@ -869,7 +912,7 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem,
if (!mem->is_flushed)
global_cache_flush();
- if (intel_private.base.needs_dmar) {
+ if (intel_private.needs_dmar) {
struct sg_table st;
ret = intel_gtt_map_memory(mem->pages, mem->page_count, &st);
@@ -889,31 +932,30 @@ out_err:
mem->is_flushed = true;
return ret;
}
+#endif
void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries)
{
unsigned int i;
for (i = first_entry; i < (first_entry + num_entries); i++) {
- intel_private.driver->write_entry(intel_private.base.scratch_page_dma,
+ intel_private.driver->write_entry(intel_private.scratch_page_dma,
i, 0);
}
readl(intel_private.gtt+i-1);
}
EXPORT_SYMBOL(intel_gtt_clear_range);
+#if IS_ENABLED(CONFIG_AGP_INTEL)
static int intel_fake_agp_remove_entries(struct agp_memory *mem,
off_t pg_start, int type)
{
if (mem->page_count == 0)
return 0;
- if (intel_private.base.do_idle_maps)
- return -ENODEV;
-
intel_gtt_clear_range(pg_start, mem->page_count);
- if (intel_private.base.needs_dmar) {
+ if (intel_private.needs_dmar) {
intel_gtt_unmap_memory(mem->sg_list, mem->num_sg);
mem->sg_list = NULL;
mem->num_sg = 0;
@@ -946,6 +988,7 @@ static struct agp_memory *intel_fake_agp_alloc_by_type(size_t pg_count,
/* always return NULL for other allocation types for now */
return NULL;
}
+#endif
static int intel_alloc_chipset_flush_resource(void)
{
@@ -1070,33 +1113,12 @@ static void i965_write_entry(dma_addr_t addr,
writel(addr | pte_flags, intel_private.gtt + entry);
}
-/* Certain Gen5 chipsets require require idling the GPU before
- * unmapping anything from the GTT when VT-d is enabled.
- */
-static inline int needs_idle_maps(void)
-{
-#ifdef CONFIG_INTEL_IOMMU
- const unsigned short gpu_devid = intel_private.pcidev->device;
-
- /* Query intel_iommu to see if we need the workaround. Presumably that
- * was loaded first.
- */
- if ((gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB ||
- gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG) &&
- intel_iommu_gfx_mapped)
- return 1;
-#endif
- return 0;
-}
-
static int i9xx_setup(void)
{
- u32 reg_addr, gtt_addr;
+ phys_addr_t reg_addr;
int size = KB(512);
- pci_read_config_dword(intel_private.pcidev, I915_MMADDR, &reg_addr);
-
- reg_addr &= 0xfff80000;
+ reg_addr = pci_resource_start(intel_private.pcidev, I915_MMADR_BAR);
intel_private.registers = ioremap(reg_addr, size);
if (!intel_private.registers)
@@ -1104,26 +1126,23 @@ static int i9xx_setup(void)
switch (INTEL_GTT_GEN) {
case 3:
- pci_read_config_dword(intel_private.pcidev,
- I915_PTEADDR, &gtt_addr);
- intel_private.gtt_bus_addr = gtt_addr;
+ intel_private.gtt_phys_addr =
+ pci_resource_start(intel_private.pcidev, I915_PTE_BAR);
break;
case 5:
- intel_private.gtt_bus_addr = reg_addr + MB(2);
+ intel_private.gtt_phys_addr = reg_addr + MB(2);
break;
default:
- intel_private.gtt_bus_addr = reg_addr + KB(512);
+ intel_private.gtt_phys_addr = reg_addr + KB(512);
break;
}
- if (needs_idle_maps())
- intel_private.base.do_idle_maps = 1;
-
intel_i9xx_setup_flush();
return 0;
}
+#if IS_ENABLED(CONFIG_AGP_INTEL)
static const struct agp_bridge_driver intel_fake_agp_driver = {
.owner = THIS_MODULE,
.size_type = FIXED_APER_SIZE,
@@ -1145,6 +1164,7 @@ static const struct agp_bridge_driver intel_fake_agp_driver = {
.agp_destroy_page = agp_generic_destroy_page,
.agp_destroy_pages = agp_generic_destroy_pages,
};
+#endif
static const struct intel_gtt_driver i81x_gtt_driver = {
.gen = 1,
@@ -1362,11 +1382,13 @@ int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev,
intel_private.refcount++;
+#if IS_ENABLED(CONFIG_AGP_INTEL)
if (bridge) {
bridge->driver = &intel_fake_agp_driver;
bridge->dev_private_data = &intel_private;
bridge->dev = bridge_pdev;
}
+#endif
intel_private.bridge_dev = pci_dev_get(bridge_pdev);
@@ -1390,9 +1412,13 @@ int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev,
}
EXPORT_SYMBOL(intel_gmch_probe);
-struct intel_gtt *intel_gtt_get(void)
+void intel_gtt_get(size_t *gtt_total, size_t *stolen_size,
+ phys_addr_t *mappable_base, unsigned long *mappable_end)
{
- return &intel_private.base;
+ *gtt_total = intel_private.gtt_total_entries << PAGE_SHIFT;
+ *stolen_size = intel_private.stolen_size;
+ *mappable_base = intel_private.gma_bus_addr;
+ *mappable_end = intel_private.gtt_mappable_entries << PAGE_SHIFT;
}
EXPORT_SYMBOL(intel_gtt_get);
diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c
index 62be3ec0da4..a1861b75eb3 100644
--- a/drivers/char/agp/nvidia-agp.c
+++ b/drivers/char/agp/nvidia-agp.c
@@ -106,6 +106,7 @@ static int nvidia_configure(void)
{
int i, rc, num_dirs;
u32 apbase, aplimit;
+ phys_addr_t apbase_phys;
struct aper_size_info_8 *current_size;
u32 temp;
@@ -115,9 +116,8 @@ static int nvidia_configure(void)
pci_write_config_byte(agp_bridge->dev, NVIDIA_0_APSIZE,
current_size->size_value);
- /* address to map to */
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &apbase);
- apbase &= PCI_BASE_ADDRESS_MEM_MASK;
+ /* address to map to */
+ apbase = pci_bus_address(agp_bridge->dev, AGP_APERTURE_BAR);
agp_bridge->gart_bus_addr = apbase;
aplimit = apbase + (current_size->size * 1024 * 1024) - 1;
pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_APBASE, apbase);
@@ -153,8 +153,9 @@ static int nvidia_configure(void)
pci_write_config_dword(agp_bridge->dev, NVIDIA_0_APSIZE, temp | 0x100);
/* map aperture */
+ apbase_phys = pci_resource_start(agp_bridge->dev, AGP_APERTURE_BAR);
nvidia_private.aperture =
- (volatile u32 __iomem *) ioremap(apbase, 33 * PAGE_SIZE);
+ (volatile u32 __iomem *) ioremap(apbase_phys, 33 * PAGE_SIZE);
if (!nvidia_private.aperture)
return -ENOMEM;
@@ -399,8 +400,8 @@ static void agp_nvidia_remove(struct pci_dev *pdev)
#ifdef CONFIG_PM
static int agp_nvidia_suspend(struct pci_dev *pdev, pm_message_t state)
{
- pci_save_state (pdev);
- pci_set_power_state (pdev, 3);
+ pci_save_state(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
return 0;
}
@@ -408,7 +409,7 @@ static int agp_nvidia_suspend(struct pci_dev *pdev, pm_message_t state)
static int agp_nvidia_resume(struct pci_dev *pdev)
{
/* set power state 0 and restore PCI space */
- pci_set_power_state (pdev, 0);
+ pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
/* reconfigure AGP hardware again */
diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c
index 94821ab01c6..15f2e7025b7 100644
--- a/drivers/char/agp/parisc-agp.c
+++ b/drivers/char/agp/parisc-agp.c
@@ -129,7 +129,8 @@ parisc_agp_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
off_t j, io_pg_start;
int io_pg_count;
- if (type != 0 || mem->type != 0) {
+ if (type != mem->type ||
+ agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) {
return -EINVAL;
}
@@ -175,7 +176,8 @@ parisc_agp_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
struct _parisc_agp_info *info = &parisc_agp_info;
int i, io_pg_start, io_pg_count;
- if (type != 0 || mem->type != 0) {
+ if (type != mem->type ||
+ agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) {
return -EINVAL;
}
@@ -333,7 +335,7 @@ parisc_agp_setup(void __iomem *ioc_hpa, void __iomem *lba_hpa)
struct agp_bridge_data *bridge;
int error = 0;
- fake_bridge_dev = alloc_pci_dev();
+ fake_bridge_dev = pci_alloc_dev(NULL);
if (!fake_bridge_dev) {
error = -ENOMEM;
goto fail;
diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c
index 05b8d0241bd..3051c73bc38 100644
--- a/drivers/char/agp/sgi-agp.c
+++ b/drivers/char/agp/sgi-agp.c
@@ -15,7 +15,6 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/agp_backend.h>
#include <asm/sn/addrs.h>
#include <asm/sn/io.h>
diff --git a/drivers/char/agp/sis-agp.c b/drivers/char/agp/sis-agp.c
index 79c838c434b..2c74038da45 100644
--- a/drivers/char/agp/sis-agp.c
+++ b/drivers/char/agp/sis-agp.c
@@ -50,13 +50,12 @@ static void sis_tlbflush(struct agp_memory *mem)
static int sis_configure(void)
{
- u32 temp;
struct aper_size_info_8 *current_size;
current_size = A_SIZE_8(agp_bridge->current_size);
pci_write_config_byte(agp_bridge->dev, SIS_TLBCNTRL, 0x05);
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
+ AGP_APERTURE_BAR);
pci_write_config_dword(agp_bridge->dev, SIS_ATTBASE,
agp_bridge->gatt_bus_addr);
pci_write_config_byte(agp_bridge->dev, SIS_APSIZE,
diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c
index 74d3aa3773b..228f20cddc0 100644
--- a/drivers/char/agp/via-agp.c
+++ b/drivers/char/agp/via-agp.c
@@ -43,16 +43,15 @@ static int via_fetch_size(void)
static int via_configure(void)
{
- u32 temp;
struct aper_size_info_8 *current_size;
current_size = A_SIZE_8(agp_bridge->current_size);
/* aperture size */
pci_write_config_byte(agp_bridge->dev, VIA_APSIZE,
current_size->size_value);
- /* address to map too */
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ /* address to map to */
+ agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
+ AGP_APERTURE_BAR);
/* GART control register */
pci_write_config_dword(agp_bridge->dev, VIA_GARTCTRL, 0x0000000f);
@@ -132,9 +131,9 @@ static int via_configure_agp3(void)
current_size = A_SIZE_16(agp_bridge->current_size);
- /* address to map too */
- pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
- agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+ /* address to map to */
+ agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
+ AGP_APERTURE_BAR);
/* attbase - aperture GATT base */
pci_write_config_dword(agp_bridge->dev, VIA_AGP3_ATTBASE,
diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c
index 46118f84594..dd9dfa15e9d 100644
--- a/drivers/char/apm-emulation.c
+++ b/drivers/char/apm-emulation.c
@@ -531,6 +531,7 @@ static int apm_suspend_notifier(struct notifier_block *nb,
{
struct apm_user *as;
int err;
+ unsigned long apm_event;
/* short-cut emergency suspends */
if (atomic_read(&userspace_notification_inhibit))
@@ -538,6 +539,9 @@ static int apm_suspend_notifier(struct notifier_block *nb,
switch (event) {
case PM_SUSPEND_PREPARE:
+ case PM_HIBERNATION_PREPARE:
+ apm_event = (event == PM_SUSPEND_PREPARE) ?
+ APM_USER_SUSPEND : APM_USER_HIBERNATION;
/*
* Queue an event to all "writer" users that we want
* to suspend and need their ack.
@@ -550,7 +554,7 @@ static int apm_suspend_notifier(struct notifier_block *nb,
as->writer && as->suser) {
as->suspend_state = SUSPEND_PENDING;
atomic_inc(&suspend_acks_pending);
- queue_add_event(&as->queue, APM_USER_SUSPEND);
+ queue_add_event(&as->queue, apm_event);
}
}
@@ -601,11 +605,14 @@ static int apm_suspend_notifier(struct notifier_block *nb,
return notifier_from_errno(err);
case PM_POST_SUSPEND:
+ case PM_POST_HIBERNATION:
+ apm_event = (event == PM_POST_SUSPEND) ?
+ APM_NORMAL_RESUME : APM_HIBERNATION_RESUME;
/*
* Anyone on the APM queues will think we're still suspended.
* Send a message so everyone knows we're now awake again.
*/
- queue_event(APM_NORMAL_RESUME);
+ queue_event(apm_event);
/*
* Finally, wake up anyone who is sleeping on the suspend.
diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c
index 25373df1dcf..14790304b84 100644
--- a/drivers/char/applicom.c
+++ b/drivers/char/applicom.c
@@ -345,7 +345,6 @@ out:
free_irq(apbs[i].irq, &dummy);
iounmap(apbs[i].RamIO);
}
- pci_disable_device(dev);
return ret;
}
@@ -804,8 +803,8 @@ static long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
printk(KERN_INFO "Prom version board %d ....... V%d.%d %s",
i+1,
- (int)(readb(apbs[IndexCard].RamIO + VERS) >> 4),
- (int)(readb(apbs[IndexCard].RamIO + VERS) & 0xF),
+ (int)(readb(apbs[i].RamIO + VERS) >> 4),
+ (int)(readb(apbs[i].RamIO + VERS) & 0xF),
boardname);
diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c
index 97467053a01..8fedbc25041 100644
--- a/drivers/char/bsr.c
+++ b/drivers/char/bsr.c
@@ -21,6 +21,7 @@
#include <linux/kernel.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/fs.h>
@@ -95,6 +96,7 @@ bsr_size_show(struct device *dev, struct device_attribute *attr, char *buf)
struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", bsr_dev->bsr_bytes);
}
+static DEVICE_ATTR_RO(bsr_size);
static ssize_t
bsr_stride_show(struct device *dev, struct device_attribute *attr, char *buf)
@@ -102,20 +104,23 @@ bsr_stride_show(struct device *dev, struct device_attribute *attr, char *buf)
struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", bsr_dev->bsr_stride);
}
+static DEVICE_ATTR_RO(bsr_stride);
static ssize_t
-bsr_len_show(struct device *dev, struct device_attribute *attr, char *buf)
+bsr_length_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
return sprintf(buf, "%llu\n", bsr_dev->bsr_len);
}
+static DEVICE_ATTR_RO(bsr_length);
-static struct device_attribute bsr_dev_attrs[] = {
- __ATTR(bsr_size, S_IRUGO, bsr_size_show, NULL),
- __ATTR(bsr_stride, S_IRUGO, bsr_stride_show, NULL),
- __ATTR(bsr_length, S_IRUGO, bsr_len_show, NULL),
- __ATTR_NULL
+static struct attribute *bsr_dev_attrs[] = {
+ &dev_attr_bsr_size.attr,
+ &dev_attr_bsr_stride.attr,
+ &dev_attr_bsr_length.attr,
+ NULL,
};
+ATTRIBUTE_GROUPS(bsr_dev);
static int bsr_mmap(struct file *filp, struct vm_area_struct *vma)
{
@@ -308,7 +313,7 @@ static int __init bsr_init(void)
ret = PTR_ERR(bsr_class);
goto out_err_1;
}
- bsr_class->dev_attrs = bsr_dev_attrs;
+ bsr_class->dev_groups = bsr_dev_groups;
ret = alloc_chrdev_region(&bsr_dev, 0, BSR_MAX_DEVS, "bsr");
bsr_major = MAJOR(bsr_dev);
diff --git a/drivers/char/ds1620.c b/drivers/char/ds1620.c
index 24ffd8cec51..0fae5296e31 100644
--- a/drivers/char/ds1620.c
+++ b/drivers/char/ds1620.c
@@ -6,6 +6,7 @@
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <linux/capability.h>
#include <linux/init.h>
#include <linux/mutex.h>
@@ -329,9 +330,7 @@ ds1620_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
#ifdef THERM_USE_PROC
-static int
-proc_therm_ds1620_read(char *buf, char **start, off_t offset,
- int len, int *eof, void *unused)
+static int ds1620_proc_therm_show(struct seq_file *m, void *v)
{
struct therm th;
int temp;
@@ -339,17 +338,25 @@ proc_therm_ds1620_read(char *buf, char **start, off_t offset,
ds1620_read_state(&th);
temp = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
- len = sprintf(buf, "Thermostat: HI %i.%i, LOW %i.%i; "
- "temperature: %i.%i C, fan %s\n",
- th.hi >> 1, th.hi & 1 ? 5 : 0,
- th.lo >> 1, th.lo & 1 ? 5 : 0,
- temp >> 1, temp & 1 ? 5 : 0,
- fan_state[netwinder_get_fan()]);
+ seq_printf(m, "Thermostat: HI %i.%i, LOW %i.%i; temperature: %i.%i C, fan %s\n",
+ th.hi >> 1, th.hi & 1 ? 5 : 0,
+ th.lo >> 1, th.lo & 1 ? 5 : 0,
+ temp >> 1, temp & 1 ? 5 : 0,
+ fan_state[netwinder_get_fan()]);
+ return 0;
+}
- return len;
+static int ds1620_proc_therm_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ds1620_proc_therm_show, NULL);
}
-static struct proc_dir_entry *proc_therm_ds1620;
+static const struct file_operations ds1620_proc_therm_fops = {
+ .open = ds1620_proc_therm_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
#endif
static const struct file_operations ds1620_fops = {
@@ -397,10 +404,7 @@ static int __init ds1620_init(void)
return ret;
#ifdef THERM_USE_PROC
- proc_therm_ds1620 = create_proc_entry("therm", 0, NULL);
- if (proc_therm_ds1620)
- proc_therm_ds1620->read_proc = proc_therm_ds1620_read;
- else
+ if (!proc_create("therm", 0, NULL, &ds1620_proc_therm_fops))
printk(KERN_ERR "therm: unable to register /proc/therm\n");
#endif
diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c
index 052797b32bd..01a5ca7425d 100644
--- a/drivers/char/dsp56k.c
+++ b/drivers/char/dsp56k.c
@@ -181,7 +181,7 @@ static int dsp56k_upload(u_char __user *bin, int len)
static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
- struct inode *inode = file->f_path.dentry->d_inode;
+ struct inode *inode = file_inode(file);
int dev = iminor(inode) & 0x0f;
switch(dev)
@@ -244,7 +244,7 @@ static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,
static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t count,
loff_t *ppos)
{
- struct inode *inode = file->f_path.dentry->d_inode;
+ struct inode *inode = file_inode(file);
int dev = iminor(inode) & 0x0f;
switch(dev)
@@ -306,7 +306,7 @@ static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t co
static long dsp56k_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
- int dev = iminor(file->f_path.dentry->d_inode) & 0x0f;
+ int dev = iminor(file_inode(file)) & 0x0f;
void __user *argp = (void __user *)arg;
switch(dev)
@@ -408,7 +408,7 @@ static long dsp56k_ioctl(struct file *file, unsigned int cmd,
#if 0
static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
{
- int dev = iminor(file->f_path.dentry->d_inode) & 0x0f;
+ int dev = iminor(file_inode(file)) & 0x0f;
switch(dev)
{
diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c
index 85156dd0cae..65a8d96c0e9 100644
--- a/drivers/char/dtlk.c
+++ b/drivers/char/dtlk.c
@@ -125,7 +125,7 @@ static char dtlk_write_tts(char);
static ssize_t dtlk_read(struct file *file, char __user *buf,
size_t count, loff_t * ppos)
{
- unsigned int minor = iminor(file->f_path.dentry->d_inode);
+ unsigned int minor = iminor(file_inode(file));
char ch;
int i = 0, retries;
@@ -177,7 +177,7 @@ static ssize_t dtlk_write(struct file *file, const char __user *buf,
}
#endif
- if (iminor(file->f_path.dentry->d_inode) != DTLK_MINOR)
+ if (iminor(file_inode(file)) != DTLK_MINOR)
return -EINVAL;
while (1) {
diff --git a/drivers/char/efirtc.c b/drivers/char/efirtc.c
index a082d00b0f1..e39e7402e62 100644
--- a/drivers/char/efirtc.c
+++ b/drivers/char/efirtc.c
@@ -34,6 +34,7 @@
#include <linux/init.h>
#include <linux/rtc.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <linux/efi.h>
#include <linux/uaccess.h>
@@ -296,12 +297,10 @@ static struct miscdevice efi_rtc_dev= {
/*
* We export RAW EFI information to /proc/driver/efirtc
*/
-static int
-efi_rtc_get_status(char *buf)
+static int efi_rtc_proc_show(struct seq_file *m, void *v)
{
efi_time_t eft, alm;
efi_time_cap_t cap;
- char *p = buf;
efi_bool_t enabled, pending;
unsigned long flags;
@@ -316,64 +315,63 @@ efi_rtc_get_status(char *buf)
spin_unlock_irqrestore(&efi_rtc_lock,flags);
- p += sprintf(p,
- "Time : %u:%u:%u.%09u\n"
- "Date : %u-%u-%u\n"
- "Daylight : %u\n",
- eft.hour, eft.minute, eft.second, eft.nanosecond,
- eft.year, eft.month, eft.day,
- eft.daylight);
+ seq_printf(m,
+ "Time : %u:%u:%u.%09u\n"
+ "Date : %u-%u-%u\n"
+ "Daylight : %u\n",
+ eft.hour, eft.minute, eft.second, eft.nanosecond,
+ eft.year, eft.month, eft.day,
+ eft.daylight);
if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE)
- p += sprintf(p, "Timezone : unspecified\n");
+ seq_puts(m, "Timezone : unspecified\n");
else
/* XXX fixme: convert to string? */
- p += sprintf(p, "Timezone : %u\n", eft.timezone);
+ seq_printf(m, "Timezone : %u\n", eft.timezone);
- p += sprintf(p,
- "Alarm Time : %u:%u:%u.%09u\n"
- "Alarm Date : %u-%u-%u\n"
- "Alarm Daylight : %u\n"
- "Enabled : %s\n"
- "Pending : %s\n",
- alm.hour, alm.minute, alm.second, alm.nanosecond,
- alm.year, alm.month, alm.day,
- alm.daylight,
- enabled == 1 ? "yes" : "no",
- pending == 1 ? "yes" : "no");
+ seq_printf(m,
+ "Alarm Time : %u:%u:%u.%09u\n"
+ "Alarm Date : %u-%u-%u\n"
+ "Alarm Daylight : %u\n"
+ "Enabled : %s\n"
+ "Pending : %s\n",
+ alm.hour, alm.minute, alm.second, alm.nanosecond,
+ alm.year, alm.month, alm.day,
+ alm.daylight,
+ enabled == 1 ? "yes" : "no",
+ pending == 1 ? "yes" : "no");
if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE)
- p += sprintf(p, "Timezone : unspecified\n");
+ seq_puts(m, "Timezone : unspecified\n");
else
/* XXX fixme: convert to string? */
- p += sprintf(p, "Timezone : %u\n", alm.timezone);
+ seq_printf(m, "Timezone : %u\n", alm.timezone);
/*
* now prints the capabilities
*/
- p += sprintf(p,
- "Resolution : %u\n"
- "Accuracy : %u\n"
- "SetstoZero : %u\n",
- cap.resolution, cap.accuracy, cap.sets_to_zero);
+ seq_printf(m,
+ "Resolution : %u\n"
+ "Accuracy : %u\n"
+ "SetstoZero : %u\n",
+ cap.resolution, cap.accuracy, cap.sets_to_zero);
- return p - buf;
+ return 0;
}
-static int
-efi_rtc_read_proc(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int efi_rtc_proc_open(struct inode *inode, struct file *file)
{
- int len = efi_rtc_get_status(page);
- if (len <= off+count) *eof = 1;
- *start = page + off;
- len -= off;
- if (len>count) len = count;
- if (len<0) len = 0;
- return len;
+ return single_open(file, efi_rtc_proc_show, NULL);
}
+static const struct file_operations efi_rtc_proc_fops = {
+ .open = efi_rtc_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static int __init
efi_rtc_init(void)
{
@@ -389,8 +387,7 @@ efi_rtc_init(void)
return ret;
}
- dir = create_proc_read_entry ("driver/efirtc", 0, NULL,
- efi_rtc_read_proc, NULL);
+ dir = proc_create("driver/efirtc", 0, NULL, &efi_rtc_proc_fops);
if (dir == NULL) {
printk(KERN_ERR "efirtc: can't create /proc/driver/efirtc.\n");
misc_deregister(&efi_rtc_dev);
diff --git a/drivers/char/genrtc.c b/drivers/char/genrtc.c
index 21cb980f115..4f943759d37 100644
--- a/drivers/char/genrtc.c
+++ b/drivers/char/genrtc.c
@@ -52,6 +52,7 @@
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
@@ -386,18 +387,15 @@ static int gen_rtc_release(struct inode *inode, struct file *file)
* Info exported via "/proc/driver/rtc".
*/
-static int gen_rtc_proc_output(char *buf)
+static int gen_rtc_proc_show(struct seq_file *m, void *v)
{
- char *p;
struct rtc_time tm;
unsigned int flags;
struct rtc_pll_info pll;
- p = buf;
-
flags = get_rtc_time(&tm);
- p += sprintf(p,
+ seq_printf(m,
"rtc_time\t: %02d:%02d:%02d\n"
"rtc_date\t: %04d-%02d-%02d\n"
"rtc_epoch\t: %04u\n",
@@ -406,23 +404,23 @@ static int gen_rtc_proc_output(char *buf)
tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
- p += sprintf(p, "alarm\t\t: ");
+ seq_puts(m, "alarm\t\t: ");
if (tm.tm_hour <= 24)
- p += sprintf(p, "%02d:", tm.tm_hour);
+ seq_printf(m, "%02d:", tm.tm_hour);
else
- p += sprintf(p, "**:");
+ seq_puts(m, "**:");
if (tm.tm_min <= 59)
- p += sprintf(p, "%02d:", tm.tm_min);
+ seq_printf(m, "%02d:", tm.tm_min);
else
- p += sprintf(p, "**:");
+ seq_puts(m, "**:");
if (tm.tm_sec <= 59)
- p += sprintf(p, "%02d\n", tm.tm_sec);
+ seq_printf(m, "%02d\n", tm.tm_sec);
else
- p += sprintf(p, "**\n");
+ seq_puts(m, "**\n");
- p += sprintf(p,
+ seq_printf(m,
"DST_enable\t: %s\n"
"BCD\t\t: %s\n"
"24hr\t\t: %s\n"
@@ -442,7 +440,7 @@ static int gen_rtc_proc_output(char *buf)
0L /* freq */,
(flags & RTC_BATT_BAD) ? "bad" : "okay");
if (!get_rtc_pll(&pll))
- p += sprintf(p,
+ seq_printf(m,
"PLL adjustment\t: %d\n"
"PLL max +ve adjustment\t: %d\n"
"PLL max -ve adjustment\t: %d\n"
@@ -455,26 +453,26 @@ static int gen_rtc_proc_output(char *buf)
pll.pll_posmult,
pll.pll_negmult,
pll.pll_clock);
- return p - buf;
+ return 0;
}
-static int gen_rtc_read_proc(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int gen_rtc_proc_open(struct inode *inode, struct file *file)
{
- int len = gen_rtc_proc_output (page);
- if (len <= off+count) *eof = 1;
- *start = page + off;
- len -= off;
- if (len>count) len = count;
- if (len<0) len = 0;
- return len;
+ return single_open(file, gen_rtc_proc_show, NULL);
}
+static const struct file_operations gen_rtc_proc_fops = {
+ .open = gen_rtc_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static int __init gen_rtc_proc_init(void)
{
struct proc_dir_entry *r;
- r = create_proc_read_entry("driver/rtc", 0, NULL, gen_rtc_read_proc, NULL);
+ r = proc_create("driver/rtc", 0, NULL, &gen_rtc_proc_fops);
if (!r)
return -ENOMEM;
return 0;
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index fe6d4be4829..d5d4cd82b9f 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -34,15 +34,12 @@
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/io.h>
-
+#include <linux/acpi.h>
+#include <linux/hpet.h>
#include <asm/current.h>
#include <asm/irq.h>
#include <asm/div64.h>
-#include <linux/acpi.h>
-#include <acpi/acpi_bus.h>
-#include <linux/hpet.h>
-
/*
* The High Precision Event Timer driver.
* This driver is closely modelled after the rtc.c driver.
@@ -367,14 +364,28 @@ static unsigned int hpet_poll(struct file *file, poll_table * wait)
return 0;
}
+#ifdef CONFIG_HPET_MMAP
+#ifdef CONFIG_HPET_MMAP_DEFAULT
+static int hpet_mmap_enabled = 1;
+#else
+static int hpet_mmap_enabled = 0;
+#endif
+
+static __init int hpet_mmap_enable(char *str)
+{
+ get_option(&str, &hpet_mmap_enabled);
+ pr_info("HPET mmap %s\n", hpet_mmap_enabled ? "enabled" : "disabled");
+ return 1;
+}
+__setup("hpet_mmap", hpet_mmap_enable);
+
static int hpet_mmap(struct file *file, struct vm_area_struct *vma)
{
-#ifdef CONFIG_HPET_MMAP
struct hpet_dev *devp;
unsigned long addr;
- if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff)
- return -EINVAL;
+ if (!hpet_mmap_enabled)
+ return -EACCES;
devp = file->private_data;
addr = devp->hd_hpets->hp_hpet_phys;
@@ -382,21 +393,15 @@ static int hpet_mmap(struct file *file, struct vm_area_struct *vma)
if (addr & (PAGE_SIZE - 1))
return -ENOSYS;
- vma->vm_flags |= VM_IO;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-
- if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT,
- PAGE_SIZE, vma->vm_page_prot)) {
- printk(KERN_ERR "%s: io_remap_pfn_range failed\n",
- __func__);
- return -EAGAIN;
- }
-
- return 0;
+ return vm_iomap_memory(vma, addr, PAGE_SIZE);
+}
#else
+static int hpet_mmap(struct file *file, struct vm_area_struct *vma)
+{
return -ENOSYS;
-#endif
}
+#endif
static int hpet_fasync(int fd, struct file *file, int on)
{
@@ -498,8 +503,7 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp)
}
sprintf(devp->hd_name, "hpet%d", (int)(devp - hpetp->hp_dev));
- irq_flags = devp->hd_flags & HPET_SHARED_IRQ
- ? IRQF_SHARED : IRQF_DISABLED;
+ irq_flags = devp->hd_flags & HPET_SHARED_IRQ ? IRQF_SHARED : 0;
if (request_irq(irq, hpet_interrupt, irq_flags,
devp->hd_name, (void *)devp)) {
printk(KERN_ERR "hpet: IRQ %d is not free\n", irq);
@@ -737,7 +741,7 @@ static int hpet_is_known(struct hpet_data *hdp)
return 0;
}
-static ctl_table hpet_table[] = {
+static struct ctl_table hpet_table[] = {
{
.procname = "max-user-freq",
.data = &hpet_max_freq,
@@ -748,7 +752,7 @@ static ctl_table hpet_table[] = {
{}
};
-static ctl_table hpet_root[] = {
+static struct ctl_table hpet_root[] = {
{
.procname = "hpet",
.maxlen = 0,
@@ -758,7 +762,7 @@ static ctl_table hpet_root[] = {
{}
};
-static ctl_table dev_root[] = {
+static struct ctl_table dev_root[] = {
{
.procname = "dev",
.maxlen = 0,
@@ -983,8 +987,6 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data)
struct acpi_resource_fixed_memory32 *fixmem32;
fixmem32 = &res->data.fixed_memory32;
- if (!fixmem32)
- return AE_NO_MEMORY;
hdp->hd_phys_address = fixmem32->address;
hdp->hd_address = ioremap(fixmem32->address,
@@ -1041,7 +1043,7 @@ static int hpet_acpi_add(struct acpi_device *device)
return hpet_alloc(&data);
}
-static int hpet_acpi_remove(struct acpi_device *device, int type)
+static int hpet_acpi_remove(struct acpi_device *device)
{
/* XXX need to unregister clocksource, dealloc mem, etc */
return -EINVAL;
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index c5a0262251b..836b061ced3 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -2,7 +2,7 @@
# Hardware Random Number Generator (RNG) configuration
#
-config HW_RANDOM
+menuconfig HW_RANDOM
tristate "Hardware Random Number Generator Core support"
default m
---help---
@@ -20,9 +20,11 @@ config HW_RANDOM
If unsure, say Y.
+if HW_RANDOM
+
config HW_RANDOM_TIMERIOMEM
tristate "Timer IOMEM HW Random Number Generator support"
- depends on HW_RANDOM && HAS_IOMEM
+ depends on HAS_IOMEM
---help---
This driver provides kernel-side support for a generic Random
Number Generator used by reading a 'dumb' iomem address that
@@ -36,7 +38,7 @@ config HW_RANDOM_TIMERIOMEM
config HW_RANDOM_INTEL
tristate "Intel HW Random Number Generator support"
- depends on HW_RANDOM && (X86 || IA64) && PCI
+ depends on (X86 || IA64) && PCI
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
@@ -49,7 +51,7 @@ config HW_RANDOM_INTEL
config HW_RANDOM_AMD
tristate "AMD HW Random Number Generator support"
- depends on HW_RANDOM && (X86 || PPC_MAPLE) && PCI
+ depends on (X86 || PPC_MAPLE) && PCI
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
@@ -62,8 +64,8 @@ config HW_RANDOM_AMD
config HW_RANDOM_ATMEL
tristate "Atmel Random Number Generator support"
- depends on HW_RANDOM && HAVE_CLK
- default (HW_RANDOM && ARCH_AT91)
+ depends on ARCH_AT91 && HAVE_CLK
+ default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on Atmel AT91 devices.
@@ -75,7 +77,7 @@ config HW_RANDOM_ATMEL
config HW_RANDOM_BCM63XX
tristate "Broadcom BCM63xx Random Number Generator support"
- depends on HW_RANDOM && BCM63XX
+ depends on BCM63XX
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
@@ -86,10 +88,22 @@ config HW_RANDOM_BCM63XX
If unusure, say Y.
+config HW_RANDOM_BCM2835
+ tristate "Broadcom BCM2835 Random Number Generator support"
+ depends on ARCH_BCM2835
+ default HW_RANDOM
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on the Broadcom BCM2835 SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bcm2835-rng
+
+ If unsure, say Y.
config HW_RANDOM_GEODE
tristate "AMD Geode HW Random Number Generator support"
- depends on HW_RANDOM && X86_32 && PCI
+ depends on X86_32 && PCI
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
@@ -102,7 +116,7 @@ config HW_RANDOM_GEODE
config HW_RANDOM_N2RNG
tristate "Niagara2 Random Number Generator support"
- depends on HW_RANDOM && SPARC64
+ depends on SPARC64
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
@@ -115,7 +129,7 @@ config HW_RANDOM_N2RNG
config HW_RANDOM_VIA
tristate "VIA HW Random Number Generator support"
- depends on HW_RANDOM && X86
+ depends on X86
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
@@ -128,7 +142,7 @@ config HW_RANDOM_VIA
config HW_RANDOM_IXP4XX
tristate "Intel IXP4xx NPU HW Pseudo-Random Number Generator support"
- depends on HW_RANDOM && ARCH_IXP4XX
+ depends on ARCH_IXP4XX
default HW_RANDOM
---help---
This driver provides kernel-side support for the Pseudo-Random
@@ -141,21 +155,34 @@ config HW_RANDOM_IXP4XX
config HW_RANDOM_OMAP
tristate "OMAP Random Number Generator support"
- depends on HW_RANDOM && (ARCH_OMAP16XX || ARCH_OMAP2)
+ depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
- Generator hardware found on OMAP16xx and OMAP24xx multimedia
- processors.
+ Generator hardware found on OMAP16xx, OMAP2/3/4/5 and AM33xx/AM43xx
+ multimedia processors.
To compile this driver as a module, choose M here: the
module will be called omap-rng.
If unsure, say Y.
+config HW_RANDOM_OMAP3_ROM
+ tristate "OMAP3 ROM Random Number Generator support"
+ depends on ARCH_OMAP3
+ default HW_RANDOM
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on OMAP34xx processors.
+
+ To compile this driver as a module, choose M here: the
+ module will be called omap3-rom-rng.
+
+ If unsure, say Y.
+
config HW_RANDOM_OCTEON
tristate "Octeon Random Number Generator support"
- depends on HW_RANDOM && CPU_CAVIUM_OCTEON
+ depends on CAVIUM_OCTEON_SOC
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
@@ -168,7 +195,7 @@ config HW_RANDOM_OCTEON
config HW_RANDOM_PASEMI
tristate "PA Semi HW Random Number Generator support"
- depends on HW_RANDOM && PPC_PASEMI
+ depends on PPC_PASEMI
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
@@ -181,7 +208,7 @@ config HW_RANDOM_PASEMI
config HW_RANDOM_VIRTIO
tristate "VirtIO Random Number Generator support"
- depends on HW_RANDOM && VIRTIO
+ depends on VIRTIO
---help---
This driver provides kernel-side support for the virtual Random Number
Generator hardware.
@@ -191,7 +218,7 @@ config HW_RANDOM_VIRTIO
config HW_RANDOM_TX4939
tristate "TX4939 Random Number Generator support"
- depends on HW_RANDOM && SOC_TX4939
+ depends on SOC_TX4939
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
@@ -204,7 +231,8 @@ config HW_RANDOM_TX4939
config HW_RANDOM_MXC_RNGA
tristate "Freescale i.MX RNGA Random Number Generator"
- depends on HW_RANDOM && ARCH_HAS_RNGA
+ depends on ARCH_HAS_RNGA
+ default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on Freescale i.MX processors.
@@ -216,7 +244,8 @@ config HW_RANDOM_MXC_RNGA
config HW_RANDOM_NOMADIK
tristate "ST-Ericsson Nomadik Random Number Generator support"
- depends on HW_RANDOM && ARCH_NOMADIK
+ depends on ARCH_NOMADIK
+ default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on ST-Ericsson SoCs (8815 and 8500).
@@ -226,21 +255,10 @@ config HW_RANDOM_NOMADIK
If unsure, say Y.
-config HW_RANDOM_PICOXCELL
- tristate "Picochip picoXcell true random number generator support"
- depends on HW_RANDOM && ARCH_PICOXCELL && PICOXCELL_PC3X3
- ---help---
- This driver provides kernel-side support for the Random Number
- Generator hardware found on Picochip PC3x3 and later devices.
-
- To compile this driver as a module, choose M here: the
- module will be called picoxcell-rng.
-
- If unsure, say Y.
-
config HW_RANDOM_PPC4XX
tristate "PowerPC 4xx generic true random number generator support"
- depends on HW_RANDOM && PPC && 4xx
+ depends on PPC && 4xx
+ default HW_RANDOM
---help---
This driver provides the kernel-side support for the TRNG hardware
found in the security function of some PowerPC 4xx SoCs.
@@ -250,24 +268,9 @@ config HW_RANDOM_PPC4XX
If unsure, say N.
-config UML_RANDOM
- depends on UML
- tristate "Hardware random number generator"
- help
- This option enables UML's "hardware" random number generator. It
- attaches itself to the host's /dev/random, supplying as much entropy
- as the host has, rather than the small amount the UML gets from its
- own drivers. It registers itself as a standard hardware random number
- generator, major 10, minor 183, and the canonical device name is
- /dev/hwrng.
- The way to make use of this is to install the rng-tools package
- (check your distro, or download from
- http://sourceforge.net/projects/gkernel/). rngd periodically reads
- /dev/hwrng and injects the entropy into /dev/random.
-
config HW_RANDOM_PSERIES
tristate "pSeries HW Random Number Generator support"
- depends on HW_RANDOM && PPC64 && IBMVIO
+ depends on PPC64 && IBMVIO
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
@@ -278,9 +281,23 @@ config HW_RANDOM_PSERIES
If unsure, say Y.
+config HW_RANDOM_POWERNV
+ tristate "PowerNV Random Number Generator support"
+ depends on PPC_POWERNV
+ default HW_RANDOM
+ ---help---
+ This is the driver for Random Number Generator hardware found
+ in POWER7+ and above machines for PowerNV platform.
+
+ To compile this driver as a module, choose M here: the
+ module will be called powernv-rng.
+
+ If unsure, say Y.
+
config HW_RANDOM_EXYNOS
tristate "EXYNOS HW random number generator support"
- depends on HW_RANDOM && HAS_IOMEM && HAVE_CLK
+ depends on ARCH_EXYNOS
+ default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on EXYNOS SOCs.
@@ -292,7 +309,7 @@ config HW_RANDOM_EXYNOS
config HW_RANDOM_TPM
tristate "TPM HW Random Number Generator support"
- depends on HW_RANDOM && TCG_TPM
+ depends on TCG_TPM
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
@@ -302,3 +319,33 @@ config HW_RANDOM_TPM
module will be called tpm-rng.
If unsure, say Y.
+
+config HW_RANDOM_MSM
+ tristate "Qualcomm SoCs Random Number Generator support"
+ depends on HW_RANDOM && ARCH_QCOM
+ default HW_RANDOM
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on Qualcomm SoCs.
+
+ To compile this driver as a module, choose M here. the
+ module will be called msm-rng.
+
+ If unsure, say Y.
+
+endif # HW_RANDOM
+
+config UML_RANDOM
+ depends on UML
+ tristate "Hardware random number generator"
+ help
+ This option enables UML's "hardware" random number generator. It
+ attaches itself to the host's /dev/random, supplying as much entropy
+ as the host has, rather than the small amount the UML gets from its
+ own drivers. It registers itself as a standard hardware random number
+ generator, major 10, minor 183, and the canonical device name is
+ /dev/hwrng.
+ The way to make use of this is to install the rng-tools package
+ (check your distro, or download from
+ http://sourceforge.net/projects/gkernel/). rngd periodically reads
+ /dev/hwrng and injects the entropy into /dev/random.
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index 1fd7eec9fbf..199ed283e14 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -15,14 +15,17 @@ n2-rng-y := n2-drv.o n2-asm.o
obj-$(CONFIG_HW_RANDOM_VIA) += via-rng.o
obj-$(CONFIG_HW_RANDOM_IXP4XX) += ixp4xx-rng.o
obj-$(CONFIG_HW_RANDOM_OMAP) += omap-rng.o
+obj-$(CONFIG_HW_RANDOM_OMAP3_ROM) += omap3-rom-rng.o
obj-$(CONFIG_HW_RANDOM_PASEMI) += pasemi-rng.o
obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o
obj-$(CONFIG_HW_RANDOM_TX4939) += tx4939-rng.o
obj-$(CONFIG_HW_RANDOM_MXC_RNGA) += mxc-rnga.o
obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o
obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o
-obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o
obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o
obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o
+obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o
obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o
obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o
+obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
+obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o
diff --git a/drivers/char/hw_random/atmel-rng.c b/drivers/char/hw_random/atmel-rng.c
index 7c73d4aca36..851bc7e20ad 100644
--- a/drivers/char/hw_random/atmel-rng.c
+++ b/drivers/char/hw_random/atmel-rng.c
@@ -54,29 +54,22 @@ static int atmel_trng_probe(struct platform_device *pdev)
struct resource *res;
int ret;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -EINVAL;
-
trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
if (!trng)
return -ENOMEM;
- if (!devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res), pdev->name))
- return -EBUSY;
-
- trng->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!trng->base)
- return -EBUSY;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ trng->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(trng->base))
+ return PTR_ERR(trng->base);
- trng->clk = clk_get(&pdev->dev, NULL);
+ trng->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(trng->clk))
return PTR_ERR(trng->clk);
ret = clk_enable(trng->clk);
if (ret)
- goto err_enable;
+ return ret;
writel(TRNG_KEY | 1, trng->base + TRNG_CR);
trng->rng.name = pdev->name;
@@ -92,9 +85,6 @@ static int atmel_trng_probe(struct platform_device *pdev)
err_register:
clk_disable(trng->clk);
-err_enable:
- clk_put(trng->clk);
-
return ret;
}
@@ -106,9 +96,6 @@ static int atmel_trng_remove(struct platform_device *pdev)
writel(TRNG_KEY, trng->base + TRNG_CR);
clk_disable(trng->clk);
- clk_put(trng->clk);
-
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/drivers/char/hw_random/bcm2835-rng.c b/drivers/char/hw_random/bcm2835-rng.c
new file mode 100644
index 00000000000..e900961cdd2
--- /dev/null
+++ b/drivers/char/hw_random/bcm2835-rng.c
@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2010-2012 Broadcom. All rights reserved.
+ * Copyright (c) 2013 Lubomir Rintel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License ("GPL")
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+
+#define RNG_CTRL 0x0
+#define RNG_STATUS 0x4
+#define RNG_DATA 0x8
+
+/* enable rng */
+#define RNG_RBGEN 0x1
+
+/* the initial numbers generated are "less random" so will be discarded */
+#define RNG_WARMUP_COUNT 0x40000
+
+static int bcm2835_rng_read(struct hwrng *rng, void *buf, size_t max,
+ bool wait)
+{
+ void __iomem *rng_base = (void __iomem *)rng->priv;
+
+ while ((__raw_readl(rng_base + RNG_STATUS) >> 24) == 0) {
+ if (!wait)
+ return 0;
+ cpu_relax();
+ }
+
+ *(u32 *)buf = __raw_readl(rng_base + RNG_DATA);
+ return sizeof(u32);
+}
+
+static struct hwrng bcm2835_rng_ops = {
+ .name = "bcm2835",
+ .read = bcm2835_rng_read,
+};
+
+static int bcm2835_rng_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ void __iomem *rng_base;
+ int err;
+
+ /* map peripheral */
+ rng_base = of_iomap(np, 0);
+ if (!rng_base) {
+ dev_err(dev, "failed to remap rng regs");
+ return -ENODEV;
+ }
+ bcm2835_rng_ops.priv = (unsigned long)rng_base;
+
+ /* set warm-up count & enable */
+ __raw_writel(RNG_WARMUP_COUNT, rng_base + RNG_STATUS);
+ __raw_writel(RNG_RBGEN, rng_base + RNG_CTRL);
+
+ /* register driver */
+ err = hwrng_register(&bcm2835_rng_ops);
+ if (err) {
+ dev_err(dev, "hwrng registration failed\n");
+ iounmap(rng_base);
+ } else
+ dev_info(dev, "hwrng registered\n");
+
+ return err;
+}
+
+static int bcm2835_rng_remove(struct platform_device *pdev)
+{
+ void __iomem *rng_base = (void __iomem *)bcm2835_rng_ops.priv;
+
+ /* disable rng hardware */
+ __raw_writel(0, rng_base + RNG_CTRL);
+
+ /* unregister driver */
+ hwrng_unregister(&bcm2835_rng_ops);
+ iounmap(rng_base);
+
+ return 0;
+}
+
+static const struct of_device_id bcm2835_rng_of_match[] = {
+ { .compatible = "brcm,bcm2835-rng", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm2835_rng_of_match);
+
+static struct platform_driver bcm2835_rng_driver = {
+ .driver = {
+ .name = "bcm2835-rng",
+ .owner = THIS_MODULE,
+ .of_match_table = bcm2835_rng_of_match,
+ },
+ .probe = bcm2835_rng_probe,
+ .remove = bcm2835_rng_remove,
+};
+module_platform_driver(bcm2835_rng_driver);
+
+MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
+MODULE_DESCRIPTION("BCM2835 Random Number Generator (RNG) driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/hw_random/bcm63xx-rng.c b/drivers/char/hw_random/bcm63xx-rng.c
index f343b7d0dfa..36581ea562c 100644
--- a/drivers/char/hw_random/bcm63xx-rng.c
+++ b/drivers/char/hw_random/bcm63xx-rng.c
@@ -137,7 +137,6 @@ static int bcm63xx_rng_probe(struct platform_device *pdev)
out_clk_disable:
clk_disable(clk);
out_free_rng:
- platform_set_drvdata(pdev, NULL);
kfree(rng);
out_free_priv:
kfree(priv);
@@ -154,7 +153,6 @@ static int bcm63xx_rng_remove(struct platform_device *pdev)
clk_disable(priv->clk);
kfree(priv);
kfree(rng);
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 1bafb40ec8a..c4419ea1ab0 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -37,9 +37,10 @@
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/sched.h>
-#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/random.h>
#include <asm/uaccess.h>
@@ -52,14 +53,43 @@ static struct hwrng *current_rng;
static LIST_HEAD(rng_list);
static DEFINE_MUTEX(rng_mutex);
static int data_avail;
-static u8 rng_buffer[SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES]
- __cacheline_aligned;
+static u8 *rng_buffer;
+
+static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
+ int wait);
+
+static size_t rng_buffer_size(void)
+{
+ return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES;
+}
+
+static void add_early_randomness(struct hwrng *rng)
+{
+ unsigned char bytes[16];
+ int bytes_read;
+
+ /*
+ * Currently only virtio-rng cannot return data during device
+ * probe, and that's handled in virtio-rng.c itself. If there
+ * are more such devices, this call to rng_get_data can be
+ * made conditional here instead of doing it per-device.
+ */
+ bytes_read = rng_get_data(rng, bytes, sizeof(bytes), 1);
+ if (bytes_read > 0)
+ add_device_randomness(bytes, bytes_read);
+}
static inline int hwrng_init(struct hwrng *rng)
{
- if (!rng->init)
- return 0;
- return rng->init(rng);
+ if (rng->init) {
+ int ret;
+
+ ret = rng->init(rng);
+ if (ret)
+ return ret;
+ }
+ add_early_randomness(rng);
+ return 0;
}
static inline void hwrng_cleanup(struct hwrng *rng)
@@ -116,7 +146,7 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,
if (!data_avail) {
bytes_read = rng_get_data(current_rng, rng_buffer,
- sizeof(rng_buffer),
+ rng_buffer_size(),
!(filp->f_flags & O_NONBLOCK));
if (bytes_read < 0) {
err = bytes_read;
@@ -297,7 +327,6 @@ err_misc_dereg:
int hwrng_register(struct hwrng *rng)
{
- int must_register_misc;
int err = -EINVAL;
struct hwrng *old_rng, *tmp;
@@ -307,6 +336,14 @@ int hwrng_register(struct hwrng *rng)
mutex_lock(&rng_mutex);
+ /* kmalloc makes this safe for virt_to_page() in virtio_rng.c */
+ err = -ENOMEM;
+ if (!rng_buffer) {
+ rng_buffer = kmalloc(rng_buffer_size(), GFP_KERNEL);
+ if (!rng_buffer)
+ goto out_unlock;
+ }
+
/* Must not register two RNGs with the same name. */
err = -EEXIST;
list_for_each_entry(tmp, &rng_list, list) {
@@ -314,7 +351,6 @@ int hwrng_register(struct hwrng *rng)
goto out_unlock;
}
- must_register_misc = (current_rng == NULL);
old_rng = current_rng;
if (!old_rng) {
err = hwrng_init(rng);
@@ -323,18 +359,28 @@ int hwrng_register(struct hwrng *rng)
current_rng = rng;
}
err = 0;
- if (must_register_misc) {
+ if (!old_rng) {
err = register_miscdev();
if (err) {
- if (!old_rng) {
- hwrng_cleanup(rng);
- current_rng = NULL;
- }
+ hwrng_cleanup(rng);
+ current_rng = NULL;
goto out_unlock;
}
}
INIT_LIST_HEAD(&rng->list);
list_add_tail(&rng->list, &rng_list);
+
+ if (old_rng && !rng->init) {
+ /*
+ * Use a new device's input to add some randomness to
+ * the system. If this rng device isn't going to be
+ * used right away, its init function hasn't been
+ * called yet; so only use the randomness from devices
+ * that don't need an init callback.
+ */
+ add_early_randomness(rng);
+ }
+
out_unlock:
mutex_unlock(&rng_mutex);
out:
@@ -367,6 +413,15 @@ void hwrng_unregister(struct hwrng *rng)
}
EXPORT_SYMBOL_GPL(hwrng_unregister);
+static void __exit hwrng_exit(void)
+{
+ mutex_lock(&rng_mutex);
+ BUG_ON(current_rng);
+ kfree(rng_buffer);
+ mutex_unlock(&rng_mutex);
+}
+
+module_exit(hwrng_exit);
MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/exynos-rng.c b/drivers/char/hw_random/exynos-rng.c
index 48bbfeca4b5..9f8277cc44b 100644
--- a/drivers/char/hw_random/exynos-rng.c
+++ b/drivers/char/hw_random/exynos-rng.c
@@ -22,7 +22,6 @@
#include <linux/hw_random.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
@@ -104,6 +103,7 @@ static int exynos_read(struct hwrng *rng, void *buf,
static int exynos_rng_probe(struct platform_device *pdev)
{
struct exynos_rng *exynos_rng;
+ struct resource *res;
exynos_rng = devm_kzalloc(&pdev->dev, sizeof(struct exynos_rng),
GFP_KERNEL);
@@ -120,10 +120,10 @@ static int exynos_rng_probe(struct platform_device *pdev)
return -ENOENT;
}
- exynos_rng->mem = devm_request_and_ioremap(&pdev->dev,
- platform_get_resource(pdev, IORESOURCE_MEM, 0));
- if (!exynos_rng->mem)
- return -EBUSY;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ exynos_rng->mem = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(exynos_rng->mem))
+ return PTR_ERR(exynos_rng->mem);
platform_set_drvdata(pdev, exynos_rng);
@@ -143,6 +143,7 @@ static int exynos_rng_remove(struct platform_device *pdev)
return 0;
}
+#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME)
static int exynos_rng_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -160,9 +161,9 @@ static int exynos_rng_runtime_resume(struct device *dev)
return clk_prepare_enable(exynos_rng->clk);
}
+#endif
-
-UNIVERSAL_DEV_PM_OPS(exynos_rng_pm_ops, exynos_rng_runtime_suspend,
+static UNIVERSAL_DEV_PM_OPS(exynos_rng_pm_ops, exynos_rng_runtime_suspend,
exynos_rng_runtime_resume, NULL);
static struct platform_driver exynos_rng_driver = {
diff --git a/drivers/char/hw_random/msm-rng.c b/drivers/char/hw_random/msm-rng.c
new file mode 100644
index 00000000000..148521e51dc
--- /dev/null
+++ b/drivers/char/hw_random/msm-rng.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+/* Device specific register offsets */
+#define PRNG_DATA_OUT 0x0000
+#define PRNG_STATUS 0x0004
+#define PRNG_LFSR_CFG 0x0100
+#define PRNG_CONFIG 0x0104
+
+/* Device specific register masks and config values */
+#define PRNG_LFSR_CFG_MASK 0x0000ffff
+#define PRNG_LFSR_CFG_CLOCKS 0x0000dddd
+#define PRNG_CONFIG_HW_ENABLE BIT(1)
+#define PRNG_STATUS_DATA_AVAIL BIT(0)
+
+#define MAX_HW_FIFO_DEPTH 16
+#define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4)
+#define WORD_SZ 4
+
+struct msm_rng {
+ void __iomem *base;
+ struct clk *clk;
+ struct hwrng hwrng;
+};
+
+#define to_msm_rng(p) container_of(p, struct msm_rng, hwrng)
+
+static int msm_rng_enable(struct hwrng *hwrng, int enable)
+{
+ struct msm_rng *rng = to_msm_rng(hwrng);
+ u32 val;
+ int ret;
+
+ ret = clk_prepare_enable(rng->clk);
+ if (ret)
+ return ret;
+
+ if (enable) {
+ /* Enable PRNG only if it is not already enabled */
+ val = readl_relaxed(rng->base + PRNG_CONFIG);
+ if (val & PRNG_CONFIG_HW_ENABLE)
+ goto already_enabled;
+
+ val = readl_relaxed(rng->base + PRNG_LFSR_CFG);
+ val &= ~PRNG_LFSR_CFG_MASK;
+ val |= PRNG_LFSR_CFG_CLOCKS;
+ writel(val, rng->base + PRNG_LFSR_CFG);
+
+ val = readl_relaxed(rng->base + PRNG_CONFIG);
+ val |= PRNG_CONFIG_HW_ENABLE;
+ writel(val, rng->base + PRNG_CONFIG);
+ } else {
+ val = readl_relaxed(rng->base + PRNG_CONFIG);
+ val &= ~PRNG_CONFIG_HW_ENABLE;
+ writel(val, rng->base + PRNG_CONFIG);
+ }
+
+already_enabled:
+ clk_disable_unprepare(rng->clk);
+ return 0;
+}
+
+static int msm_rng_read(struct hwrng *hwrng, void *data, size_t max, bool wait)
+{
+ struct msm_rng *rng = to_msm_rng(hwrng);
+ size_t currsize = 0;
+ u32 *retdata = data;
+ size_t maxsize;
+ int ret;
+ u32 val;
+
+ /* calculate max size bytes to transfer back to caller */
+ maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, max);
+
+ /* no room for word data */
+ if (maxsize < WORD_SZ)
+ return 0;
+
+ ret = clk_prepare_enable(rng->clk);
+ if (ret)
+ return ret;
+
+ /* read random data from hardware */
+ do {
+ val = readl_relaxed(rng->base + PRNG_STATUS);
+ if (!(val & PRNG_STATUS_DATA_AVAIL))
+ break;
+
+ val = readl_relaxed(rng->base + PRNG_DATA_OUT);
+ if (!val)
+ break;
+
+ *retdata++ = val;
+ currsize += WORD_SZ;
+
+ /* make sure we stay on 32bit boundary */
+ if ((maxsize - currsize) < WORD_SZ)
+ break;
+ } while (currsize < maxsize);
+
+ clk_disable_unprepare(rng->clk);
+
+ return currsize;
+}
+
+static int msm_rng_init(struct hwrng *hwrng)
+{
+ return msm_rng_enable(hwrng, 1);
+}
+
+static void msm_rng_cleanup(struct hwrng *hwrng)
+{
+ msm_rng_enable(hwrng, 0);
+}
+
+static int msm_rng_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct msm_rng *rng;
+ int ret;
+
+ rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
+ if (!rng)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, rng);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rng->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(rng->base))
+ return PTR_ERR(rng->base);
+
+ rng->clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(rng->clk))
+ return PTR_ERR(rng->clk);
+
+ rng->hwrng.name = KBUILD_MODNAME,
+ rng->hwrng.init = msm_rng_init,
+ rng->hwrng.cleanup = msm_rng_cleanup,
+ rng->hwrng.read = msm_rng_read,
+
+ ret = hwrng_register(&rng->hwrng);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register hwrng\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int msm_rng_remove(struct platform_device *pdev)
+{
+ struct msm_rng *rng = platform_get_drvdata(pdev);
+
+ hwrng_unregister(&rng->hwrng);
+ return 0;
+}
+
+static const struct of_device_id msm_rng_of_match[] = {
+ { .compatible = "qcom,prng", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_rng_of_match);
+
+static struct platform_driver msm_rng_driver = {
+ .probe = msm_rng_probe,
+ .remove = msm_rng_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(msm_rng_of_match),
+ }
+};
+module_platform_driver(msm_rng_driver);
+
+MODULE_ALIAS("platform:" KBUILD_MODNAME);
+MODULE_AUTHOR("The Linux Foundation");
+MODULE_DESCRIPTION("Qualcomm MSM random number generator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/hw_random/mxc-rnga.c b/drivers/char/hw_random/mxc-rnga.c
index f05d85713fd..6a86b6f56af 100644
--- a/drivers/char/hw_random/mxc-rnga.c
+++ b/drivers/char/hw_random/mxc-rnga.c
@@ -142,7 +142,7 @@ static void mxc_rnga_cleanup(struct hwrng *rng)
static int __init mxc_rnga_probe(struct platform_device *pdev)
{
int err = -ENODEV;
- struct resource *res, *mem;
+ struct resource *res;
struct mxc_rng *mxc_rng;
mxc_rng = devm_kzalloc(&pdev->dev, sizeof(struct mxc_rng),
@@ -164,23 +164,14 @@ static int __init mxc_rnga_probe(struct platform_device *pdev)
goto out;
}
- clk_prepare_enable(mxc_rng->clk);
+ err = clk_prepare_enable(mxc_rng->clk);
+ if (err)
+ goto out;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- err = -ENOENT;
- goto err_region;
- }
-
- mem = request_mem_region(res->start, resource_size(res), pdev->name);
- if (mem == NULL) {
- err = -EBUSY;
- goto err_region;
- }
-
- mxc_rng->mem = ioremap(res->start, resource_size(res));
- if (!mxc_rng->mem) {
- err = -ENOMEM;
+ mxc_rng->mem = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mxc_rng->mem)) {
+ err = PTR_ERR(mxc_rng->mem);
goto err_ioremap;
}
@@ -195,9 +186,6 @@ static int __init mxc_rnga_probe(struct platform_device *pdev)
return 0;
err_ioremap:
- release_mem_region(res->start, resource_size(res));
-
-err_region:
clk_disable_unprepare(mxc_rng->clk);
out:
@@ -206,15 +194,10 @@ out:
static int __exit mxc_rnga_remove(struct platform_device *pdev)
{
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct mxc_rng *mxc_rng = platform_get_drvdata(pdev);
hwrng_unregister(&mxc_rng->rng);
- iounmap(mxc_rng->mem);
-
- release_mem_region(res->start, resource_size(res));
-
clk_disable_unprepare(mxc_rng->clk);
return 0;
@@ -228,18 +211,7 @@ static struct platform_driver mxc_rnga_driver = {
.remove = __exit_p(mxc_rnga_remove),
};
-static int __init mod_init(void)
-{
- return platform_driver_probe(&mxc_rnga_driver, mxc_rnga_probe);
-}
-
-static void __exit mod_exit(void)
-{
- platform_driver_unregister(&mxc_rnga_driver);
-}
-
-module_init(mod_init);
-module_exit(mod_exit);
+module_platform_driver_probe(mxc_rnga_driver, mxc_rnga_probe);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("H/W RNGA driver for i.MX");
diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c
index 20b962e1d83..292a5889f67 100644
--- a/drivers/char/hw_random/n2-drv.c
+++ b/drivers/char/hw_random/n2-drv.c
@@ -7,7 +7,6 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/delay.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/preempt.h>
@@ -633,7 +632,7 @@ static int n2rng_probe(struct platform_device *op)
multi_capable = (match->data != NULL);
n2rng_driver_version();
- np = kzalloc(sizeof(*np), GFP_KERNEL);
+ np = devm_kzalloc(&op->dev, sizeof(*np), GFP_KERNEL);
if (!np)
goto out;
np->op = op;
@@ -654,7 +653,7 @@ static int n2rng_probe(struct platform_device *op)
&np->hvapi_minor)) {
dev_err(&op->dev, "Cannot register suitable "
"HVAPI version.\n");
- goto out_free;
+ goto out;
}
}
@@ -677,15 +676,16 @@ static int n2rng_probe(struct platform_device *op)
dev_info(&op->dev, "Registered RNG HVAPI major %lu minor %lu\n",
np->hvapi_major, np->hvapi_minor);
- np->units = kzalloc(sizeof(struct n2rng_unit) * np->num_units,
- GFP_KERNEL);
+ np->units = devm_kzalloc(&op->dev,
+ sizeof(struct n2rng_unit) * np->num_units,
+ GFP_KERNEL);
err = -ENOMEM;
if (!np->units)
goto out_hvapi_unregister;
err = n2rng_init_control(np);
if (err)
- goto out_free_units;
+ goto out_hvapi_unregister;
dev_info(&op->dev, "Found %s RNG, units: %d\n",
((np->flags & N2RNG_FLAG_MULTI) ?
@@ -698,30 +698,24 @@ static int n2rng_probe(struct platform_device *op)
err = hwrng_register(&np->hwrng);
if (err)
- goto out_free_units;
+ goto out_hvapi_unregister;
- dev_set_drvdata(&op->dev, np);
+ platform_set_drvdata(op, np);
schedule_delayed_work(&np->work, 0);
return 0;
-out_free_units:
- kfree(np->units);
- np->units = NULL;
-
out_hvapi_unregister:
sun4v_hvapi_unregister(HV_GRP_RNG);
-out_free:
- kfree(np);
out:
return err;
}
static int n2rng_remove(struct platform_device *op)
{
- struct n2rng *np = dev_get_drvdata(&op->dev);
+ struct n2rng *np = platform_get_drvdata(op);
np->flags |= N2RNG_FLAG_SHUTDOWN;
@@ -731,13 +725,6 @@ static int n2rng_remove(struct platform_device *op)
sun4v_hvapi_unregister(HV_GRP_RNG);
- kfree(np->units);
- np->units = NULL;
-
- kfree(np);
-
- dev_set_drvdata(&op->dev, NULL);
-
return 0;
}
diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c
index 96de0249e59..9c858157724 100644
--- a/drivers/char/hw_random/nomadik-rng.c
+++ b/drivers/char/hw_random/nomadik-rng.c
@@ -10,7 +10,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/device.h>
#include <linux/amba/bus.h>
#include <linux/hw_random.h>
@@ -44,46 +43,41 @@ static int nmk_rng_probe(struct amba_device *dev, const struct amba_id *id)
void __iomem *base;
int ret;
- rng_clk = clk_get(&dev->dev, NULL);
+ rng_clk = devm_clk_get(&dev->dev, NULL);
if (IS_ERR(rng_clk)) {
dev_err(&dev->dev, "could not get rng clock\n");
ret = PTR_ERR(rng_clk);
return ret;
}
- clk_enable(rng_clk);
+ clk_prepare_enable(rng_clk);
ret = amba_request_regions(dev, dev->dev.init_name);
if (ret)
goto out_clk;
ret = -ENOMEM;
- base = ioremap(dev->res.start, resource_size(&dev->res));
+ base = devm_ioremap(&dev->dev, dev->res.start,
+ resource_size(&dev->res));
if (!base)
goto out_release;
nmk_rng.priv = (unsigned long)base;
ret = hwrng_register(&nmk_rng);
if (ret)
- goto out_unmap;
+ goto out_release;
return 0;
-out_unmap:
- iounmap(base);
out_release:
amba_release_regions(dev);
out_clk:
clk_disable(rng_clk);
- clk_put(rng_clk);
return ret;
}
static int nmk_rng_remove(struct amba_device *dev)
{
- void __iomem *base = (void __iomem *)nmk_rng.priv;
hwrng_unregister(&nmk_rng);
- iounmap(base);
amba_release_regions(dev);
clk_disable(rng_clk);
- clk_put(rng_clk);
return 0;
}
diff --git a/drivers/char/hw_random/octeon-rng.c b/drivers/char/hw_random/octeon-rng.c
index 1eada566ca7..b5cc3420c65 100644
--- a/drivers/char/hw_random/octeon-rng.c
+++ b/drivers/char/hw_random/octeon-rng.c
@@ -10,7 +10,6 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/hw_random.h>
@@ -96,7 +95,7 @@ static int octeon_rng_probe(struct platform_device *pdev)
rng->ops = ops;
- dev_set_drvdata(&pdev->dev, &rng->ops);
+ platform_set_drvdata(pdev, &rng->ops);
ret = hwrng_register(&rng->ops);
if (ret)
return -ENOENT;
@@ -108,7 +107,7 @@ static int octeon_rng_probe(struct platform_device *pdev)
static int __exit octeon_rng_remove(struct platform_device *pdev)
{
- struct hwrng *rng = dev_get_drvdata(&pdev->dev);
+ struct hwrng *rng = platform_get_drvdata(pdev);
hwrng_unregister(rng);
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index d8c54e25376..f66ea258382 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -24,57 +24,131 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/interrupt.h>
#include <asm/io.h>
-#define RNG_OUT_REG 0x00 /* Output register */
-#define RNG_STAT_REG 0x04 /* Status register
- [0] = STAT_BUSY */
-#define RNG_ALARM_REG 0x24 /* Alarm register
- [7:0] = ALARM_COUNTER */
-#define RNG_CONFIG_REG 0x28 /* Configuration register
- [11:6] = RESET_COUNT
- [5:3] = RING2_DELAY
- [2:0] = RING1_DELAY */
-#define RNG_REV_REG 0x3c /* Revision register
- [7:0] = REV_NB */
-#define RNG_MASK_REG 0x40 /* Mask and reset register
- [2] = IT_EN
- [1] = SOFTRESET
- [0] = AUTOIDLE */
-#define RNG_SYSSTATUS 0x44 /* System status
- [0] = RESETDONE */
+#define RNG_REG_STATUS_RDY (1 << 0)
+
+#define RNG_REG_INTACK_RDY_MASK (1 << 0)
+#define RNG_REG_INTACK_SHUTDOWN_OFLO_MASK (1 << 1)
+#define RNG_SHUTDOWN_OFLO_MASK (1 << 1)
+
+#define RNG_CONTROL_STARTUP_CYCLES_SHIFT 16
+#define RNG_CONTROL_STARTUP_CYCLES_MASK (0xffff << 16)
+#define RNG_CONTROL_ENABLE_TRNG_SHIFT 10
+#define RNG_CONTROL_ENABLE_TRNG_MASK (1 << 10)
+
+#define RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT 16
+#define RNG_CONFIG_MAX_REFIL_CYCLES_MASK (0xffff << 16)
+#define RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT 0
+#define RNG_CONFIG_MIN_REFIL_CYCLES_MASK (0xff << 0)
+
+#define RNG_CONTROL_STARTUP_CYCLES 0xff
+#define RNG_CONFIG_MIN_REFIL_CYCLES 0x21
+#define RNG_CONFIG_MAX_REFIL_CYCLES 0x22
+
+#define RNG_ALARMCNT_ALARM_TH_SHIFT 0x0
+#define RNG_ALARMCNT_ALARM_TH_MASK (0xff << 0)
+#define RNG_ALARMCNT_SHUTDOWN_TH_SHIFT 16
+#define RNG_ALARMCNT_SHUTDOWN_TH_MASK (0x1f << 16)
+#define RNG_ALARM_THRESHOLD 0xff
+#define RNG_SHUTDOWN_THRESHOLD 0x4
+
+#define RNG_REG_FROENABLE_MASK 0xffffff
+#define RNG_REG_FRODETUNE_MASK 0xffffff
+
+#define OMAP2_RNG_OUTPUT_SIZE 0x4
+#define OMAP4_RNG_OUTPUT_SIZE 0x8
+
+enum {
+ RNG_OUTPUT_L_REG = 0,
+ RNG_OUTPUT_H_REG,
+ RNG_STATUS_REG,
+ RNG_INTMASK_REG,
+ RNG_INTACK_REG,
+ RNG_CONTROL_REG,
+ RNG_CONFIG_REG,
+ RNG_ALARMCNT_REG,
+ RNG_FROENABLE_REG,
+ RNG_FRODETUNE_REG,
+ RNG_ALARMMASK_REG,
+ RNG_ALARMSTOP_REG,
+ RNG_REV_REG,
+ RNG_SYSCONFIG_REG,
+};
+
+static const u16 reg_map_omap2[] = {
+ [RNG_OUTPUT_L_REG] = 0x0,
+ [RNG_STATUS_REG] = 0x4,
+ [RNG_CONFIG_REG] = 0x28,
+ [RNG_REV_REG] = 0x3c,
+ [RNG_SYSCONFIG_REG] = 0x40,
+};
+static const u16 reg_map_omap4[] = {
+ [RNG_OUTPUT_L_REG] = 0x0,
+ [RNG_OUTPUT_H_REG] = 0x4,
+ [RNG_STATUS_REG] = 0x8,
+ [RNG_INTMASK_REG] = 0xc,
+ [RNG_INTACK_REG] = 0x10,
+ [RNG_CONTROL_REG] = 0x14,
+ [RNG_CONFIG_REG] = 0x18,
+ [RNG_ALARMCNT_REG] = 0x1c,
+ [RNG_FROENABLE_REG] = 0x20,
+ [RNG_FRODETUNE_REG] = 0x24,
+ [RNG_ALARMMASK_REG] = 0x28,
+ [RNG_ALARMSTOP_REG] = 0x2c,
+ [RNG_REV_REG] = 0x1FE0,
+ [RNG_SYSCONFIG_REG] = 0x1FE4,
+};
+
+struct omap_rng_dev;
/**
- * struct omap_rng_private_data - RNG IP block-specific data
- * @base: virtual address of the beginning of the RNG IP block registers
- * @mem_res: struct resource * for the IP block registers physical memory
+ * struct omap_rng_pdata - RNG IP block-specific data
+ * @regs: Pointer to the register offsets structure.
+ * @data_size: No. of bytes in RNG output.
+ * @data_present: Callback to determine if data is available.
+ * @init: Callback for IP specific initialization sequence.
+ * @cleanup: Callback for IP specific cleanup sequence.
*/
-struct omap_rng_private_data {
- void __iomem *base;
- struct resource *mem_res;
+struct omap_rng_pdata {
+ u16 *regs;
+ u32 data_size;
+ u32 (*data_present)(struct omap_rng_dev *priv);
+ int (*init)(struct omap_rng_dev *priv);
+ void (*cleanup)(struct omap_rng_dev *priv);
+};
+
+struct omap_rng_dev {
+ void __iomem *base;
+ struct device *dev;
+ const struct omap_rng_pdata *pdata;
};
-static inline u32 omap_rng_read_reg(struct omap_rng_private_data *priv, int reg)
+static inline u32 omap_rng_read(struct omap_rng_dev *priv, u16 reg)
{
- return __raw_readl(priv->base + reg);
+ return __raw_readl(priv->base + priv->pdata->regs[reg]);
}
-static inline void omap_rng_write_reg(struct omap_rng_private_data *priv,
- int reg, u32 val)
+static inline void omap_rng_write(struct omap_rng_dev *priv, u16 reg,
+ u32 val)
{
- __raw_writel(val, priv->base + reg);
+ __raw_writel(val, priv->base + priv->pdata->regs[reg]);
}
static int omap_rng_data_present(struct hwrng *rng, int wait)
{
- struct omap_rng_private_data *priv;
+ struct omap_rng_dev *priv;
int data, i;
- priv = (struct omap_rng_private_data *)rng->priv;
+ priv = (struct omap_rng_dev *)rng->priv;
for (i = 0; i < 20; i++) {
- data = omap_rng_read_reg(priv, RNG_STAT_REG) ? 0 : 1;
+ data = priv->pdata->data_present(priv);
if (data || !wait)
break;
/* RNG produces data fast enough (2+ MBit/sec, even
@@ -89,59 +163,240 @@ static int omap_rng_data_present(struct hwrng *rng, int wait)
static int omap_rng_data_read(struct hwrng *rng, u32 *data)
{
- struct omap_rng_private_data *priv;
+ struct omap_rng_dev *priv;
+ u32 data_size, i;
+
+ priv = (struct omap_rng_dev *)rng->priv;
+ data_size = priv->pdata->data_size;
+
+ for (i = 0; i < data_size / sizeof(u32); i++)
+ data[i] = omap_rng_read(priv, RNG_OUTPUT_L_REG + i);
- priv = (struct omap_rng_private_data *)rng->priv;
+ if (priv->pdata->regs[RNG_INTACK_REG])
+ omap_rng_write(priv, RNG_INTACK_REG, RNG_REG_INTACK_RDY_MASK);
+ return data_size;
+}
- *data = omap_rng_read_reg(priv, RNG_OUT_REG);
+static int omap_rng_init(struct hwrng *rng)
+{
+ struct omap_rng_dev *priv;
- return sizeof(u32);
+ priv = (struct omap_rng_dev *)rng->priv;
+ return priv->pdata->init(priv);
+}
+
+static void omap_rng_cleanup(struct hwrng *rng)
+{
+ struct omap_rng_dev *priv;
+
+ priv = (struct omap_rng_dev *)rng->priv;
+ priv->pdata->cleanup(priv);
}
static struct hwrng omap_rng_ops = {
.name = "omap",
.data_present = omap_rng_data_present,
.data_read = omap_rng_data_read,
+ .init = omap_rng_init,
+ .cleanup = omap_rng_cleanup,
};
+static inline u32 omap2_rng_data_present(struct omap_rng_dev *priv)
+{
+ return omap_rng_read(priv, RNG_STATUS_REG) ? 0 : 1;
+}
+
+static int omap2_rng_init(struct omap_rng_dev *priv)
+{
+ omap_rng_write(priv, RNG_SYSCONFIG_REG, 0x1);
+ return 0;
+}
+
+static void omap2_rng_cleanup(struct omap_rng_dev *priv)
+{
+ omap_rng_write(priv, RNG_SYSCONFIG_REG, 0x0);
+}
+
+static struct omap_rng_pdata omap2_rng_pdata = {
+ .regs = (u16 *)reg_map_omap2,
+ .data_size = OMAP2_RNG_OUTPUT_SIZE,
+ .data_present = omap2_rng_data_present,
+ .init = omap2_rng_init,
+ .cleanup = omap2_rng_cleanup,
+};
+
+#if defined(CONFIG_OF)
+static inline u32 omap4_rng_data_present(struct omap_rng_dev *priv)
+{
+ return omap_rng_read(priv, RNG_STATUS_REG) & RNG_REG_STATUS_RDY;
+}
+
+static int omap4_rng_init(struct omap_rng_dev *priv)
+{
+ u32 val;
+
+ /* Return if RNG is already running. */
+ if (omap_rng_read(priv, RNG_CONFIG_REG) & RNG_CONTROL_ENABLE_TRNG_MASK)
+ return 0;
+
+ val = RNG_CONFIG_MIN_REFIL_CYCLES << RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT;
+ val |= RNG_CONFIG_MAX_REFIL_CYCLES << RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT;
+ omap_rng_write(priv, RNG_CONFIG_REG, val);
+
+ omap_rng_write(priv, RNG_FRODETUNE_REG, 0x0);
+ omap_rng_write(priv, RNG_FROENABLE_REG, RNG_REG_FROENABLE_MASK);
+ val = RNG_ALARM_THRESHOLD << RNG_ALARMCNT_ALARM_TH_SHIFT;
+ val |= RNG_SHUTDOWN_THRESHOLD << RNG_ALARMCNT_SHUTDOWN_TH_SHIFT;
+ omap_rng_write(priv, RNG_ALARMCNT_REG, val);
+
+ val = RNG_CONTROL_STARTUP_CYCLES << RNG_CONTROL_STARTUP_CYCLES_SHIFT;
+ val |= RNG_CONTROL_ENABLE_TRNG_MASK;
+ omap_rng_write(priv, RNG_CONTROL_REG, val);
+
+ return 0;
+}
+
+static void omap4_rng_cleanup(struct omap_rng_dev *priv)
+{
+ int val;
+
+ val = omap_rng_read(priv, RNG_CONTROL_REG);
+ val &= ~RNG_CONTROL_ENABLE_TRNG_MASK;
+ omap_rng_write(priv, RNG_CONFIG_REG, val);
+}
+
+static irqreturn_t omap4_rng_irq(int irq, void *dev_id)
+{
+ struct omap_rng_dev *priv = dev_id;
+ u32 fro_detune, fro_enable;
+
+ /*
+ * Interrupt raised by a fro shutdown threshold, do the following:
+ * 1. Clear the alarm events.
+ * 2. De tune the FROs which are shutdown.
+ * 3. Re enable the shutdown FROs.
+ */
+ omap_rng_write(priv, RNG_ALARMMASK_REG, 0x0);
+ omap_rng_write(priv, RNG_ALARMSTOP_REG, 0x0);
+
+ fro_enable = omap_rng_read(priv, RNG_FROENABLE_REG);
+ fro_detune = ~fro_enable & RNG_REG_FRODETUNE_MASK;
+ fro_detune = fro_detune | omap_rng_read(priv, RNG_FRODETUNE_REG);
+ fro_enable = RNG_REG_FROENABLE_MASK;
+
+ omap_rng_write(priv, RNG_FRODETUNE_REG, fro_detune);
+ omap_rng_write(priv, RNG_FROENABLE_REG, fro_enable);
+
+ omap_rng_write(priv, RNG_INTACK_REG, RNG_REG_INTACK_SHUTDOWN_OFLO_MASK);
+
+ return IRQ_HANDLED;
+}
+
+static struct omap_rng_pdata omap4_rng_pdata = {
+ .regs = (u16 *)reg_map_omap4,
+ .data_size = OMAP4_RNG_OUTPUT_SIZE,
+ .data_present = omap4_rng_data_present,
+ .init = omap4_rng_init,
+ .cleanup = omap4_rng_cleanup,
+};
+
+static const struct of_device_id omap_rng_of_match[] = {
+ {
+ .compatible = "ti,omap2-rng",
+ .data = &omap2_rng_pdata,
+ },
+ {
+ .compatible = "ti,omap4-rng",
+ .data = &omap4_rng_pdata,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, omap_rng_of_match);
+
+static int of_get_omap_rng_device_details(struct omap_rng_dev *priv,
+ struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct device *dev = &pdev->dev;
+ int irq, err;
+
+ match = of_match_device(of_match_ptr(omap_rng_of_match), dev);
+ if (!match) {
+ dev_err(dev, "no compatible OF match\n");
+ return -EINVAL;
+ }
+ priv->pdata = match->data;
+
+ if (of_device_is_compatible(dev->of_node, "ti,omap4-rng")) {
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "%s: error getting IRQ resource - %d\n",
+ __func__, irq);
+ return irq;
+ }
+
+ err = devm_request_irq(dev, irq, omap4_rng_irq,
+ IRQF_TRIGGER_NONE, dev_name(dev), priv);
+ if (err) {
+ dev_err(dev, "unable to request irq %d, err = %d\n",
+ irq, err);
+ return err;
+ }
+ omap_rng_write(priv, RNG_INTMASK_REG, RNG_SHUTDOWN_OFLO_MASK);
+ }
+ return 0;
+}
+#else
+static int of_get_omap_rng_device_details(struct omap_rng_dev *omap_rng,
+ struct platform_device *pdev)
+{
+ return -EINVAL;
+}
+#endif
+
+static int get_omap_rng_device_details(struct omap_rng_dev *omap_rng)
+{
+ /* Only OMAP2/3 can be non-DT */
+ omap_rng->pdata = &omap2_rng_pdata;
+ return 0;
+}
+
static int omap_rng_probe(struct platform_device *pdev)
{
- struct omap_rng_private_data *priv;
+ struct omap_rng_dev *priv;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
int ret;
- priv = kzalloc(sizeof(struct omap_rng_private_data), GFP_KERNEL);
- if (!priv) {
- dev_err(&pdev->dev, "could not allocate memory\n");
+ priv = devm_kzalloc(dev, sizeof(struct omap_rng_dev), GFP_KERNEL);
+ if (!priv)
return -ENOMEM;
- };
omap_rng_ops.priv = (unsigned long)priv;
- dev_set_drvdata(&pdev->dev, priv);
-
- priv->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!priv->mem_res) {
- ret = -ENOENT;
- goto err_ioremap;
- }
+ platform_set_drvdata(pdev, priv);
+ priv->dev = dev;
- priv->base = devm_request_and_ioremap(&pdev->dev, priv->mem_res);
- if (!priv->base) {
- ret = -ENOMEM;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
goto err_ioremap;
}
- dev_set_drvdata(&pdev->dev, priv);
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
+ ret = (dev->of_node) ? of_get_omap_rng_device_details(priv, pdev) :
+ get_omap_rng_device_details(priv);
+ if (ret)
+ goto err_ioremap;
+
ret = hwrng_register(&omap_rng_ops);
if (ret)
goto err_register;
dev_info(&pdev->dev, "OMAP Random Number Generator ver. %02x\n",
- omap_rng_read_reg(priv, RNG_REV_REG));
-
- omap_rng_write_reg(priv, RNG_MASK_REG, 0x1);
+ omap_rng_read(priv, RNG_REV_REG));
return 0;
@@ -149,26 +404,21 @@ err_register:
priv->base = NULL;
pm_runtime_disable(&pdev->dev);
err_ioremap:
- kfree(priv);
-
+ dev_err(dev, "initialization failed.\n");
return ret;
}
static int __exit omap_rng_remove(struct platform_device *pdev)
{
- struct omap_rng_private_data *priv = dev_get_drvdata(&pdev->dev);
+ struct omap_rng_dev *priv = platform_get_drvdata(pdev);
hwrng_unregister(&omap_rng_ops);
- omap_rng_write_reg(priv, RNG_MASK_REG, 0x0);
+ priv->pdata->cleanup(priv);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
- release_mem_region(priv->mem_res->start, resource_size(priv->mem_res));
-
- kfree(priv);
-
return 0;
}
@@ -176,9 +426,9 @@ static int __exit omap_rng_remove(struct platform_device *pdev)
static int omap_rng_suspend(struct device *dev)
{
- struct omap_rng_private_data *priv = dev_get_drvdata(dev);
+ struct omap_rng_dev *priv = dev_get_drvdata(dev);
- omap_rng_write_reg(priv, RNG_MASK_REG, 0x0);
+ priv->pdata->cleanup(priv);
pm_runtime_put_sync(dev);
return 0;
@@ -186,10 +436,10 @@ static int omap_rng_suspend(struct device *dev)
static int omap_rng_resume(struct device *dev)
{
- struct omap_rng_private_data *priv = dev_get_drvdata(dev);
+ struct omap_rng_dev *priv = dev_get_drvdata(dev);
pm_runtime_get_sync(dev);
- omap_rng_write_reg(priv, RNG_MASK_REG, 0x1);
+ priv->pdata->init(priv);
return 0;
}
@@ -203,31 +453,18 @@ static SIMPLE_DEV_PM_OPS(omap_rng_pm, omap_rng_suspend, omap_rng_resume);
#endif
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:omap_rng");
-
static struct platform_driver omap_rng_driver = {
.driver = {
.name = "omap_rng",
.owner = THIS_MODULE,
.pm = OMAP_RNG_PM,
+ .of_match_table = of_match_ptr(omap_rng_of_match),
},
.probe = omap_rng_probe,
.remove = __exit_p(omap_rng_remove),
};
-static int __init omap_rng_init(void)
-{
- return platform_driver_register(&omap_rng_driver);
-}
-
-static void __exit omap_rng_exit(void)
-{
- platform_driver_unregister(&omap_rng_driver);
-}
-
-module_init(omap_rng_init);
-module_exit(omap_rng_exit);
-
+module_platform_driver(omap_rng_driver);
+MODULE_ALIAS("platform:omap_rng");
MODULE_AUTHOR("Deepak Saxena (and others)");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/omap3-rom-rng.c b/drivers/char/hw_random/omap3-rom-rng.c
new file mode 100644
index 00000000000..6f2eaffed62
--- /dev/null
+++ b/drivers/char/hw_random/omap3-rom-rng.c
@@ -0,0 +1,140 @@
+/*
+ * omap3-rom-rng.c - RNG driver for TI OMAP3 CPU family
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Juha Yrjola <juha.yrjola@solidboot.com>
+ *
+ * Copyright (C) 2013 Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/random.h>
+#include <linux/hw_random.h>
+#include <linux/timer.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+
+#define RNG_RESET 0x01
+#define RNG_GEN_PRNG_HW_INIT 0x02
+#define RNG_GEN_HW 0x08
+
+/* param1: ptr, param2: count, param3: flag */
+static u32 (*omap3_rom_rng_call)(u32, u32, u32);
+
+static struct timer_list idle_timer;
+static int rng_idle;
+static struct clk *rng_clk;
+
+static void omap3_rom_rng_idle(unsigned long data)
+{
+ int r;
+
+ r = omap3_rom_rng_call(0, 0, RNG_RESET);
+ if (r != 0) {
+ pr_err("reset failed: %d\n", r);
+ return;
+ }
+ clk_disable_unprepare(rng_clk);
+ rng_idle = 1;
+}
+
+static int omap3_rom_rng_get_random(void *buf, unsigned int count)
+{
+ u32 r;
+ u32 ptr;
+
+ del_timer_sync(&idle_timer);
+ if (rng_idle) {
+ clk_prepare_enable(rng_clk);
+ r = omap3_rom_rng_call(0, 0, RNG_GEN_PRNG_HW_INIT);
+ if (r != 0) {
+ clk_disable_unprepare(rng_clk);
+ pr_err("HW init failed: %d\n", r);
+ return -EIO;
+ }
+ rng_idle = 0;
+ }
+
+ ptr = virt_to_phys(buf);
+ r = omap3_rom_rng_call(ptr, count, RNG_GEN_HW);
+ mod_timer(&idle_timer, jiffies + msecs_to_jiffies(500));
+ if (r != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int omap3_rom_rng_data_present(struct hwrng *rng, int wait)
+{
+ return 1;
+}
+
+static int omap3_rom_rng_data_read(struct hwrng *rng, u32 *data)
+{
+ int r;
+
+ r = omap3_rom_rng_get_random(data, 4);
+ if (r < 0)
+ return r;
+ return 4;
+}
+
+static struct hwrng omap3_rom_rng_ops = {
+ .name = "omap3-rom",
+ .data_present = omap3_rom_rng_data_present,
+ .data_read = omap3_rom_rng_data_read,
+};
+
+static int omap3_rom_rng_probe(struct platform_device *pdev)
+{
+ pr_info("initializing\n");
+
+ omap3_rom_rng_call = pdev->dev.platform_data;
+ if (!omap3_rom_rng_call) {
+ pr_err("omap3_rom_rng_call is NULL\n");
+ return -EINVAL;
+ }
+
+ setup_timer(&idle_timer, omap3_rom_rng_idle, 0);
+ rng_clk = devm_clk_get(&pdev->dev, "ick");
+ if (IS_ERR(rng_clk)) {
+ pr_err("unable to get RNG clock\n");
+ return PTR_ERR(rng_clk);
+ }
+
+ /* Leave the RNG in reset state. */
+ clk_prepare_enable(rng_clk);
+ omap3_rom_rng_idle(0);
+
+ return hwrng_register(&omap3_rom_rng_ops);
+}
+
+static int omap3_rom_rng_remove(struct platform_device *pdev)
+{
+ hwrng_unregister(&omap3_rom_rng_ops);
+ clk_disable_unprepare(rng_clk);
+ return 0;
+}
+
+static struct platform_driver omap3_rom_rng_driver = {
+ .driver = {
+ .name = "omap3-rom-rng",
+ .owner = THIS_MODULE,
+ },
+ .probe = omap3_rom_rng_probe,
+ .remove = omap3_rom_rng_remove,
+};
+
+module_platform_driver(omap3_rom_rng_driver);
+
+MODULE_ALIAS("platform:omap3-rom-rng");
+MODULE_AUTHOR("Juha Yrjola");
+MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c
index c6df5b29af0..c66279bb6ef 100644
--- a/drivers/char/hw_random/pasemi-rng.c
+++ b/drivers/char/hw_random/pasemi-rng.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/hw_random.h>
#include <linux/delay.h>
+#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <asm/io.h>
diff --git a/drivers/char/hw_random/picoxcell-rng.c b/drivers/char/hw_random/picoxcell-rng.c
deleted file mode 100644
index 973b95113ed..00000000000
--- a/drivers/char/hw_random/picoxcell-rng.c
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (c) 2010-2011 Picochip Ltd., Jamie Iles
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * All enquiries to support@picochip.com
- */
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/hw_random.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-
-#define DATA_REG_OFFSET 0x0200
-#define CSR_REG_OFFSET 0x0278
-#define CSR_OUT_EMPTY_MASK (1 << 24)
-#define CSR_FAULT_MASK (1 << 1)
-#define TRNG_BLOCK_RESET_MASK (1 << 0)
-#define TAI_REG_OFFSET 0x0380
-
-/*
- * The maximum amount of time in microseconds to spend waiting for data if the
- * core wants us to wait. The TRNG should generate 32 bits every 320ns so a
- * timeout of 20us seems reasonable. The TRNG does builtin tests of the data
- * for randomness so we can't always assume there is data present.
- */
-#define PICO_TRNG_TIMEOUT 20
-
-static void __iomem *rng_base;
-static struct clk *rng_clk;
-struct device *rng_dev;
-
-static inline u32 picoxcell_trng_read_csr(void)
-{
- return __raw_readl(rng_base + CSR_REG_OFFSET);
-}
-
-static inline bool picoxcell_trng_is_empty(void)
-{
- return picoxcell_trng_read_csr() & CSR_OUT_EMPTY_MASK;
-}
-
-/*
- * Take the random number generator out of reset and make sure the interrupts
- * are masked. We shouldn't need to get large amounts of random bytes so just
- * poll the status register. The hardware generates 32 bits every 320ns so we
- * shouldn't have to wait long enough to warrant waiting for an IRQ.
- */
-static void picoxcell_trng_start(void)
-{
- __raw_writel(0, rng_base + TAI_REG_OFFSET);
- __raw_writel(0, rng_base + CSR_REG_OFFSET);
-}
-
-static void picoxcell_trng_reset(void)
-{
- __raw_writel(TRNG_BLOCK_RESET_MASK, rng_base + CSR_REG_OFFSET);
- __raw_writel(TRNG_BLOCK_RESET_MASK, rng_base + TAI_REG_OFFSET);
- picoxcell_trng_start();
-}
-
-/*
- * Get some random data from the random number generator. The hw_random core
- * layer provides us with locking.
- */
-static int picoxcell_trng_read(struct hwrng *rng, void *buf, size_t max,
- bool wait)
-{
- int i;
-
- /* Wait for some data to become available. */
- for (i = 0; i < PICO_TRNG_TIMEOUT && picoxcell_trng_is_empty(); ++i) {
- if (!wait)
- return 0;
-
- udelay(1);
- }
-
- if (picoxcell_trng_read_csr() & CSR_FAULT_MASK) {
- dev_err(rng_dev, "fault detected, resetting TRNG\n");
- picoxcell_trng_reset();
- return -EIO;
- }
-
- if (i == PICO_TRNG_TIMEOUT)
- return 0;
-
- *(u32 *)buf = __raw_readl(rng_base + DATA_REG_OFFSET);
- return sizeof(u32);
-}
-
-static struct hwrng picoxcell_trng = {
- .name = "picoxcell",
- .read = picoxcell_trng_read,
-};
-
-static int picoxcell_trng_probe(struct platform_device *pdev)
-{
- int ret;
- struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- if (!mem) {
- dev_warn(&pdev->dev, "no memory resource\n");
- return -ENOMEM;
- }
-
- if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem),
- "picoxcell_trng")) {
- dev_warn(&pdev->dev, "unable to request io mem\n");
- return -EBUSY;
- }
-
- rng_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
- if (!rng_base) {
- dev_warn(&pdev->dev, "unable to remap io mem\n");
- return -ENOMEM;
- }
-
- rng_clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(rng_clk)) {
- dev_warn(&pdev->dev, "no clk\n");
- return PTR_ERR(rng_clk);
- }
-
- ret = clk_enable(rng_clk);
- if (ret) {
- dev_warn(&pdev->dev, "unable to enable clk\n");
- goto err_enable;
- }
-
- picoxcell_trng_start();
- ret = hwrng_register(&picoxcell_trng);
- if (ret)
- goto err_register;
-
- rng_dev = &pdev->dev;
- dev_info(&pdev->dev, "pixoxcell random number generator active\n");
-
- return 0;
-
-err_register:
- clk_disable(rng_clk);
-err_enable:
- clk_put(rng_clk);
-
- return ret;
-}
-
-static int picoxcell_trng_remove(struct platform_device *pdev)
-{
- hwrng_unregister(&picoxcell_trng);
- clk_disable(rng_clk);
- clk_put(rng_clk);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int picoxcell_trng_suspend(struct device *dev)
-{
- clk_disable(rng_clk);
-
- return 0;
-}
-
-static int picoxcell_trng_resume(struct device *dev)
-{
- return clk_enable(rng_clk);
-}
-
-static const struct dev_pm_ops picoxcell_trng_pm_ops = {
- .suspend = picoxcell_trng_suspend,
- .resume = picoxcell_trng_resume,
-};
-#endif /* CONFIG_PM */
-
-static struct platform_driver picoxcell_trng_driver = {
- .probe = picoxcell_trng_probe,
- .remove = picoxcell_trng_remove,
- .driver = {
- .name = "picoxcell-trng",
- .owner = THIS_MODULE,
-#ifdef CONFIG_PM
- .pm = &picoxcell_trng_pm_ops,
-#endif /* CONFIG_PM */
- },
-};
-
-module_platform_driver(picoxcell_trng_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jamie Iles");
-MODULE_DESCRIPTION("Picochip picoXcell TRNG driver");
diff --git a/drivers/char/hw_random/powernv-rng.c b/drivers/char/hw_random/powernv-rng.c
new file mode 100644
index 00000000000..3f4f6320456
--- /dev/null
+++ b/drivers/char/hw_random/powernv-rng.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2013 Michael Ellerman, Guo Chao, IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+#include <linux/hw_random.h>
+
+static int powernv_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+ unsigned long *buf;
+ int i, len;
+
+ /* We rely on rng_buffer_size() being >= sizeof(unsigned long) */
+ len = max / sizeof(unsigned long);
+
+ buf = (unsigned long *)data;
+
+ for (i = 0; i < len; i++)
+ powernv_get_random_long(buf++);
+
+ return len * sizeof(unsigned long);
+}
+
+static struct hwrng powernv_hwrng = {
+ .name = "powernv-rng",
+ .read = powernv_rng_read,
+};
+
+static int powernv_rng_remove(struct platform_device *pdev)
+{
+ hwrng_unregister(&powernv_hwrng);
+
+ return 0;
+}
+
+static int powernv_rng_probe(struct platform_device *pdev)
+{
+ int rc;
+
+ rc = hwrng_register(&powernv_hwrng);
+ if (rc) {
+ /* We only register one device, ignore any others */
+ if (rc == -EEXIST)
+ rc = -ENODEV;
+
+ return rc;
+ }
+
+ pr_info("Registered powernv hwrng.\n");
+
+ return 0;
+}
+
+static struct of_device_id powernv_rng_match[] = {
+ { .compatible = "ibm,power-rng",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, powernv_rng_match);
+
+static struct platform_driver powernv_rng_driver = {
+ .driver = {
+ .name = "powernv_rng",
+ .of_match_table = powernv_rng_match,
+ },
+ .probe = powernv_rng_probe,
+ .remove = powernv_rng_remove,
+};
+module_platform_driver(powernv_rng_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Bare metal HWRNG driver for POWER7+ and above");
diff --git a/drivers/char/hw_random/ppc4xx-rng.c b/drivers/char/hw_random/ppc4xx-rng.c
index 732c330805f..521f76b0934 100644
--- a/drivers/char/hw_random/ppc4xx-rng.c
+++ b/drivers/char/hw_random/ppc4xx-rng.c
@@ -13,6 +13,7 @@
#include <linux/platform_device.h>
#include <linux/hw_random.h>
#include <linux/delay.h>
+#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <asm/io.h>
diff --git a/drivers/char/hw_random/pseries-rng.c b/drivers/char/hw_random/pseries-rng.c
index 5f1197929f0..ab7ffdec0ec 100644
--- a/drivers/char/hw_random/pseries-rng.c
+++ b/drivers/char/hw_random/pseries-rng.c
@@ -17,18 +17,25 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/hw_random.h>
#include <asm/vio.h>
-#define MODULE_NAME "pseries-rng"
static int pseries_rng_data_read(struct hwrng *rng, u32 *data)
{
- if (plpar_hcall(H_RANDOM, (unsigned long *)data) != H_SUCCESS) {
- printk(KERN_ERR "pseries rng hcall error\n");
- return 0;
+ int rc;
+
+ rc = plpar_hcall(H_RANDOM, (unsigned long *)data);
+ if (rc != H_SUCCESS) {
+ pr_err_ratelimited("H_RANDOM call failed %d\n", rc);
+ return -EIO;
}
+
+ /* The hypervisor interface returns 64 bits */
return 8;
}
@@ -47,7 +54,7 @@ static unsigned long pseries_rng_get_desired_dma(struct vio_dev *vdev)
};
static struct hwrng pseries_rng = {
- .name = MODULE_NAME,
+ .name = KBUILD_MODNAME,
.data_read = pseries_rng_data_read,
};
@@ -70,7 +77,7 @@ static struct vio_device_id pseries_rng_driver_ids[] = {
MODULE_DEVICE_TABLE(vio, pseries_rng_driver_ids);
static struct vio_driver pseries_rng_driver = {
- .name = MODULE_NAME,
+ .name = KBUILD_MODNAME,
.probe = pseries_rng_probe,
.remove = pseries_rng_remove,
.get_desired_dma = pseries_rng_get_desired_dma,
diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c
index 849db199c02..b6ab9ac3f34 100644
--- a/drivers/char/hw_random/timeriomem-rng.c
+++ b/drivers/char/hw_random/timeriomem-rng.c
@@ -23,127 +23,185 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
#include <linux/hw_random.h>
#include <linux/io.h>
+#include <linux/slab.h>
#include <linux/timeriomem-rng.h>
#include <linux/jiffies.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/completion.h>
-static struct timeriomem_rng_data *timeriomem_rng_data;
+struct timeriomem_rng_private_data {
+ void __iomem *io_base;
+ unsigned int expires;
+ unsigned int period;
+ unsigned int present:1;
-static void timeriomem_rng_trigger(unsigned long);
-static DEFINE_TIMER(timeriomem_rng_timer, timeriomem_rng_trigger, 0, 0);
+ struct timer_list timer;
+ struct completion completion;
+
+ struct hwrng timeriomem_rng_ops;
+};
+
+#define to_rng_priv(rng) \
+ ((struct timeriomem_rng_private_data *)rng->priv)
/*
* have data return 1, however return 0 if we have nothing
*/
static int timeriomem_rng_data_present(struct hwrng *rng, int wait)
{
- if (rng->priv == 0)
- return 1;
+ struct timeriomem_rng_private_data *priv = to_rng_priv(rng);
- if (!wait || timeriomem_rng_data->present)
- return timeriomem_rng_data->present;
+ if (!wait || priv->present)
+ return priv->present;
- wait_for_completion(&timeriomem_rng_data->completion);
+ wait_for_completion(&priv->completion);
return 1;
}
static int timeriomem_rng_data_read(struct hwrng *rng, u32 *data)
{
+ struct timeriomem_rng_private_data *priv = to_rng_priv(rng);
unsigned long cur;
s32 delay;
- *data = readl(timeriomem_rng_data->address);
+ *data = readl(priv->io_base);
- if (rng->priv != 0) {
- cur = jiffies;
+ cur = jiffies;
- delay = cur - timeriomem_rng_timer.expires;
- delay = rng->priv - (delay % rng->priv);
+ delay = cur - priv->expires;
+ delay = priv->period - (delay % priv->period);
- timeriomem_rng_timer.expires = cur + delay;
- timeriomem_rng_data->present = 0;
+ priv->expires = cur + delay;
+ priv->present = 0;
- init_completion(&timeriomem_rng_data->completion);
- add_timer(&timeriomem_rng_timer);
- }
+ reinit_completion(&priv->completion);
+ mod_timer(&priv->timer, priv->expires);
return 4;
}
-static void timeriomem_rng_trigger(unsigned long dummy)
+static void timeriomem_rng_trigger(unsigned long data)
{
- timeriomem_rng_data->present = 1;
- complete(&timeriomem_rng_data->completion);
-}
+ struct timeriomem_rng_private_data *priv
+ = (struct timeriomem_rng_private_data *)data;
-static struct hwrng timeriomem_rng_ops = {
- .name = "timeriomem",
- .data_present = timeriomem_rng_data_present,
- .data_read = timeriomem_rng_data_read,
- .priv = 0,
-};
+ priv->present = 1;
+ complete(&priv->completion);
+}
static int timeriomem_rng_probe(struct platform_device *pdev)
{
+ struct timeriomem_rng_data *pdata = pdev->dev.platform_data;
+ struct timeriomem_rng_private_data *priv;
struct resource *res;
- int ret;
+ int err = 0;
+ int period;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!pdev->dev.of_node && !pdata) {
+ dev_err(&pdev->dev, "timeriomem_rng_data is missing\n");
+ return -EINVAL;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
- return -ENOENT;
+ return -ENXIO;
+
+ if (res->start % 4 != 0 || resource_size(res) != 4) {
+ dev_err(&pdev->dev,
+ "address must be four bytes wide and aligned\n");
+ return -EINVAL;
+ }
+
+ /* Allocate memory for the device structure (and zero it) */
+ priv = devm_kzalloc(&pdev->dev,
+ sizeof(struct timeriomem_rng_private_data), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ if (pdev->dev.of_node) {
+ int i;
+
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "period", &i))
+ period = i;
+ else {
+ dev_err(&pdev->dev, "missing period\n");
+ return -EINVAL;
+ }
+ } else {
+ period = pdata->period;
+ }
+
+ priv->period = usecs_to_jiffies(period);
+ if (priv->period < 1) {
+ dev_err(&pdev->dev, "period is less than one jiffy\n");
+ return -EINVAL;
+ }
+
+ priv->expires = jiffies;
+ priv->present = 1;
- timeriomem_rng_data = pdev->dev.platform_data;
+ init_completion(&priv->completion);
+ complete(&priv->completion);
- timeriomem_rng_data->address = ioremap(res->start, resource_size(res));
- if (!timeriomem_rng_data->address)
- return -EIO;
+ setup_timer(&priv->timer, timeriomem_rng_trigger, (unsigned long)priv);
- if (timeriomem_rng_data->period != 0
- && usecs_to_jiffies(timeriomem_rng_data->period) > 0) {
- timeriomem_rng_timer.expires = jiffies;
+ priv->timeriomem_rng_ops.name = dev_name(&pdev->dev);
+ priv->timeriomem_rng_ops.data_present = timeriomem_rng_data_present;
+ priv->timeriomem_rng_ops.data_read = timeriomem_rng_data_read;
+ priv->timeriomem_rng_ops.priv = (unsigned long)priv;
- timeriomem_rng_ops.priv = usecs_to_jiffies(
- timeriomem_rng_data->period);
+ priv->io_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->io_base)) {
+ err = PTR_ERR(priv->io_base);
+ goto out_timer;
}
- timeriomem_rng_data->present = 1;
- ret = hwrng_register(&timeriomem_rng_ops);
- if (ret)
- goto failed;
+ err = hwrng_register(&priv->timeriomem_rng_ops);
+ if (err) {
+ dev_err(&pdev->dev, "problem registering\n");
+ goto out_timer;
+ }
dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n",
- timeriomem_rng_data->address,
- timeriomem_rng_data->period);
+ priv->io_base, period);
return 0;
-failed:
- dev_err(&pdev->dev, "problem registering\n");
- iounmap(timeriomem_rng_data->address);
-
- return ret;
+out_timer:
+ del_timer_sync(&priv->timer);
+ return err;
}
static int timeriomem_rng_remove(struct platform_device *pdev)
{
- del_timer_sync(&timeriomem_rng_timer);
- hwrng_unregister(&timeriomem_rng_ops);
+ struct timeriomem_rng_private_data *priv = platform_get_drvdata(pdev);
+
+ hwrng_unregister(&priv->timeriomem_rng_ops);
- iounmap(timeriomem_rng_data->address);
+ del_timer_sync(&priv->timer);
return 0;
}
+static const struct of_device_id timeriomem_rng_match[] = {
+ { .compatible = "timeriomem_rng" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, timeriomem_rng_match);
+
static struct platform_driver timeriomem_rng_driver = {
.driver = {
.name = "timeriomem_rng",
.owner = THIS_MODULE,
+ .of_match_table = timeriomem_rng_match,
},
.probe = timeriomem_rng_probe,
.remove = timeriomem_rng_remove,
diff --git a/drivers/char/hw_random/tx4939-rng.c b/drivers/char/hw_random/tx4939-rng.c
index de473ef3882..09c5fbea2b9 100644
--- a/drivers/char/hw_random/tx4939-rng.c
+++ b/drivers/char/hw_random/tx4939-rng.c
@@ -7,6 +7,7 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
+#include <linux/err.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -109,15 +110,13 @@ static int __init tx4939_rng_probe(struct platform_device *dev)
struct resource *r;
int i;
- r = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (!r)
- return -EBUSY;
rngdev = devm_kzalloc(&dev->dev, sizeof(*rngdev), GFP_KERNEL);
if (!rngdev)
return -ENOMEM;
- rngdev->base = devm_request_and_ioremap(&dev->dev, r);
- if (!rngdev->base)
- return -EBUSY;
+ r = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ rngdev->base = devm_ioremap_resource(&dev->dev, r);
+ if (IS_ERR(rngdev->base))
+ return PTR_ERR(rngdev->base);
rngdev->rng.name = dev_name(&dev->dev);
rngdev->rng.data_present = tx4939_rng_data_present;
@@ -153,7 +152,6 @@ static int __exit tx4939_rng_remove(struct platform_device *dev)
struct tx4939_rng *rngdev = platform_get_drvdata(dev);
hwrng_unregister(&rngdev->rng);
- platform_set_drvdata(dev, NULL);
return 0;
}
@@ -165,18 +163,7 @@ static struct platform_driver tx4939_rng_driver = {
.remove = tx4939_rng_remove,
};
-static int __init tx4939rng_init(void)
-{
- return platform_driver_probe(&tx4939_rng_driver, tx4939_rng_probe);
-}
-
-static void __exit tx4939rng_exit(void)
-{
- platform_driver_unregister(&tx4939_rng_driver);
-}
-
-module_init(tx4939rng_init);
-module_exit(tx4939rng_exit);
+module_platform_driver_probe(tx4939_rng_driver, tx4939_rng_probe);
MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver for TX4939");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c
index d0387a84eec..de5a6dcfb3e 100644
--- a/drivers/char/hw_random/via-rng.c
+++ b/drivers/char/hw_random/via-rng.c
@@ -29,6 +29,7 @@
#include <linux/kernel.h>
#include <linux/hw_random.h>
#include <linux/delay.h>
+#include <asm/cpu_device_id.h>
#include <asm/io.h>
#include <asm/msr.h>
#include <asm/cpufeature.h>
@@ -220,5 +221,11 @@ static void __exit mod_exit(void)
module_init(mod_init);
module_exit(mod_exit);
+static struct x86_cpu_id __maybe_unused via_rng_cpu_id[] = {
+ X86_FEATURE_MATCH(X86_FEATURE_XSTORE),
+ {}
+};
+
MODULE_DESCRIPTION("H/W RNG driver for VIA CPU with PadLock");
MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(x86cpu, via_rng_cpu_id);
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c
index b65c1039595..e9b15bc18b4 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -25,93 +25,140 @@
#include <linux/virtio_rng.h>
#include <linux/module.h>
-static struct virtqueue *vq;
-static unsigned int data_avail;
-static DECLARE_COMPLETION(have_data);
-static bool busy;
+static DEFINE_IDA(rng_index_ida);
+
+struct virtrng_info {
+ struct virtio_device *vdev;
+ struct hwrng hwrng;
+ struct virtqueue *vq;
+ unsigned int data_avail;
+ struct completion have_data;
+ bool busy;
+ char name[25];
+ int index;
+};
+
+static bool probe_done;
static void random_recv_done(struct virtqueue *vq)
{
+ struct virtrng_info *vi = vq->vdev->priv;
+
/* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
- if (!virtqueue_get_buf(vq, &data_avail))
+ if (!virtqueue_get_buf(vi->vq, &vi->data_avail))
return;
- complete(&have_data);
+ complete(&vi->have_data);
}
/* The host will fill any buffer we give it with sweet, sweet randomness. */
-static void register_buffer(u8 *buf, size_t size)
+static void register_buffer(struct virtrng_info *vi, u8 *buf, size_t size)
{
struct scatterlist sg;
sg_init_one(&sg, buf, size);
/* There should always be room for one buffer. */
- if (virtqueue_add_buf(vq, &sg, 0, 1, buf, GFP_KERNEL) < 0)
- BUG();
+ virtqueue_add_inbuf(vi->vq, &sg, 1, buf, GFP_KERNEL);
- virtqueue_kick(vq);
+ virtqueue_kick(vi->vq);
}
static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
{
int ret;
+ struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
- if (!busy) {
- busy = true;
- init_completion(&have_data);
- register_buffer(buf, size);
+ /*
+ * Don't ask host for data till we're setup. This call can
+ * happen during hwrng_register(), after commit d9e7972619.
+ */
+ if (unlikely(!probe_done))
+ return 0;
+
+ if (!vi->busy) {
+ vi->busy = true;
+ init_completion(&vi->have_data);
+ register_buffer(vi, buf, size);
}
if (!wait)
return 0;
- ret = wait_for_completion_killable(&have_data);
+ ret = wait_for_completion_killable(&vi->have_data);
if (ret < 0)
return ret;
- busy = false;
+ vi->busy = false;
- return data_avail;
+ return vi->data_avail;
}
static void virtio_cleanup(struct hwrng *rng)
{
- if (busy)
- wait_for_completion(&have_data);
-}
+ struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
-
-static struct hwrng virtio_hwrng = {
- .name = "virtio",
- .cleanup = virtio_cleanup,
- .read = virtio_read,
-};
+ if (vi->busy)
+ wait_for_completion(&vi->have_data);
+}
static int probe_common(struct virtio_device *vdev)
{
- int err;
+ int err, index;
+ struct virtrng_info *vi = NULL;
+
+ vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL);
+ if (!vi)
+ return -ENOMEM;
+
+ vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL);
+ if (index < 0) {
+ kfree(vi);
+ return index;
+ }
+ sprintf(vi->name, "virtio_rng.%d", index);
+ init_completion(&vi->have_data);
+
+ vi->hwrng = (struct hwrng) {
+ .read = virtio_read,
+ .cleanup = virtio_cleanup,
+ .priv = (unsigned long)vi,
+ .name = vi->name,
+ };
+ vdev->priv = vi;
/* We expect a single virtqueue. */
- vq = virtio_find_single_vq(vdev, random_recv_done, "input");
- if (IS_ERR(vq))
- return PTR_ERR(vq);
+ vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input");
+ if (IS_ERR(vi->vq)) {
+ err = PTR_ERR(vi->vq);
+ vi->vq = NULL;
+ kfree(vi);
+ ida_simple_remove(&rng_index_ida, index);
+ return err;
+ }
- err = hwrng_register(&virtio_hwrng);
+ err = hwrng_register(&vi->hwrng);
if (err) {
vdev->config->del_vqs(vdev);
+ vi->vq = NULL;
+ kfree(vi);
+ ida_simple_remove(&rng_index_ida, index);
return err;
}
+ probe_done = true;
return 0;
}
static void remove_common(struct virtio_device *vdev)
{
+ struct virtrng_info *vi = vdev->priv;
vdev->config->reset(vdev);
- busy = false;
- hwrng_unregister(&virtio_hwrng);
+ vi->busy = false;
+ hwrng_unregister(&vi->hwrng);
vdev->config->del_vqs(vdev);
+ ida_simple_remove(&rng_index_ida, vi->index);
+ kfree(vi);
}
static int virtrng_probe(struct virtio_device *vdev)
@@ -124,7 +171,7 @@ static void virtrng_remove(struct virtio_device *vdev)
remove_common(vdev);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int virtrng_freeze(struct virtio_device *vdev)
{
remove_common(vdev);
@@ -148,24 +195,13 @@ static struct virtio_driver virtio_rng_driver = {
.id_table = id_table,
.probe = virtrng_probe,
.remove = virtrng_remove,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
.freeze = virtrng_freeze,
.restore = virtrng_restore,
#endif
};
-static int __init init(void)
-{
- return register_virtio_driver(&virtio_rng_driver);
-}
-
-static void __exit fini(void)
-{
- unregister_virtio_driver(&virtio_rng_driver);
-}
-module_init(init);
-module_exit(fini);
-
+module_virtio_driver(virtio_rng_driver);
MODULE_DEVICE_TABLE(virtio, id_table);
MODULE_DESCRIPTION("Virtio random number driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c
index 40cc0cf2ded..93dcad0c1cb 100644
--- a/drivers/char/i8k.c
+++ b/drivers/char/i8k.c
@@ -1,12 +1,11 @@
/*
* i8k.c -- Linux driver for accessing the SMM BIOS on Dell laptops.
- * See http://www.debian.org/~dz/i8k/ for more information
- * and for latest version of this driver.
*
* Copyright (C) 2001 Massimo Dal Zotto <dz@debian.org>
*
* Hwmon integration:
- * Copyright (C) 2011 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2011 Jean Delvare <jdelvare@suse.de>
+ * Copyright (C) 2013 Guenter Roeck <linux@roeck-us.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -19,6 +18,8 @@
* General Public License for more details.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
@@ -29,13 +30,12 @@
#include <linux/mutex.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/sched.h>
#include <linux/i8k.h>
-#define I8K_VERSION "1.14 21/02/2005"
-
#define I8K_SMM_FN_STATUS 0x0025
#define I8K_SMM_POWER_STATUS 0x0069
#define I8K_SMM_SET_FAN 0x01a3
@@ -44,7 +44,6 @@
#define I8K_SMM_GET_TEMP 0x10a3
#define I8K_SMM_GET_DELL_SIG1 0xfea3
#define I8K_SMM_GET_DELL_SIG2 0xffa3
-#define I8K_SMM_BIOS_VERSION 0x00a6
#define I8K_FAN_MULT 30
#define I8K_MAX_TEMP 127
@@ -64,6 +63,15 @@
static DEFINE_MUTEX(i8k_mutex);
static char bios_version[4];
static struct device *i8k_hwmon_dev;
+static u32 i8k_hwmon_flags;
+static int i8k_fan_mult;
+
+#define I8K_HWMON_HAVE_TEMP1 (1 << 0)
+#define I8K_HWMON_HAVE_TEMP2 (1 << 1)
+#define I8K_HWMON_HAVE_TEMP3 (1 << 2)
+#define I8K_HWMON_HAVE_TEMP4 (1 << 3)
+#define I8K_HWMON_HAVE_FAN1 (1 << 4)
+#define I8K_HWMON_HAVE_FAN2 (1 << 5)
MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops");
@@ -103,11 +111,11 @@ static const struct file_operations i8k_fops = {
struct smm_regs {
unsigned int eax;
- unsigned int ebx __attribute__ ((packed));
- unsigned int ecx __attribute__ ((packed));
- unsigned int edx __attribute__ ((packed));
- unsigned int esi __attribute__ ((packed));
- unsigned int edi __attribute__ ((packed));
+ unsigned int ebx __packed;
+ unsigned int ecx __packed;
+ unsigned int edx __packed;
+ unsigned int esi __packed;
+ unsigned int edi __packed;
};
static inline const char *i8k_get_dmi_data(int field)
@@ -124,6 +132,19 @@ static int i8k_smm(struct smm_regs *regs)
{
int rc;
int eax = regs->eax;
+ cpumask_var_t old_mask;
+
+ /* SMM requires CPU 0 */
+ if (!alloc_cpumask_var(&old_mask, GFP_KERNEL))
+ return -ENOMEM;
+ cpumask_copy(old_mask, &current->cpus_allowed);
+ rc = set_cpus_allowed_ptr(current, cpumask_of(0));
+ if (rc)
+ goto out;
+ if (smp_processor_id() != 0) {
+ rc = -EBUSY;
+ goto out;
+ }
#if defined(CONFIG_X86_64)
asm volatile("pushq %%rax\n\t"
@@ -148,7 +169,7 @@ static int i8k_smm(struct smm_regs *regs)
"pushfq\n\t"
"popq %%rax\n\t"
"andl $1,%%eax\n"
- :"=a"(rc)
+ : "=a"(rc)
: "a"(regs)
: "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
#else
@@ -174,25 +195,17 @@ static int i8k_smm(struct smm_regs *regs)
"lahf\n\t"
"shrl $8,%%eax\n\t"
"andl $1,%%eax\n"
- :"=a"(rc)
+ : "=a"(rc)
: "a"(regs)
: "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
#endif
if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
- return -EINVAL;
-
- return 0;
-}
-
-/*
- * Read the bios version. Return the version as an integer corresponding
- * to the ascii value, for example "A17" is returned as 0x00413137.
- */
-static int i8k_get_bios_version(void)
-{
- struct smm_regs regs = { .eax = I8K_SMM_BIOS_VERSION, };
+ rc = -EINVAL;
- return i8k_smm(&regs) ? : regs.eax;
+out:
+ set_cpus_allowed_ptr(current, old_mask);
+ free_cpumask_var(old_mask);
+ return rc;
}
/*
@@ -203,7 +216,8 @@ static int i8k_get_fn_status(void)
struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, };
int rc;
- if ((rc = i8k_smm(&regs)) < 0)
+ rc = i8k_smm(&regs);
+ if (rc < 0)
return rc;
switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) {
@@ -226,7 +240,8 @@ static int i8k_get_power_status(void)
struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, };
int rc;
- if ((rc = i8k_smm(&regs)) < 0)
+ rc = i8k_smm(&regs);
+ if (rc < 0)
return rc;
return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY;
@@ -251,7 +266,7 @@ static int i8k_get_fan_speed(int fan)
struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, };
regs.ebx = fan & 0xff;
- return i8k_smm(&regs) ? : (regs.eax & 0xffff) * fan_mult;
+ return i8k_smm(&regs) ? : (regs.eax & 0xffff) * i8k_fan_mult;
}
/*
@@ -277,10 +292,11 @@ static int i8k_get_temp(int sensor)
int temp;
#ifdef I8K_TEMPERATURE_BUG
- static int prev;
+ static int prev[4];
#endif
regs.ebx = sensor & 0xff;
- if ((rc = i8k_smm(&regs)) < 0)
+ rc = i8k_smm(&regs);
+ if (rc < 0)
return rc;
temp = regs.eax & 0xff;
@@ -294,10 +310,10 @@ static int i8k_get_temp(int sensor)
# 1003655139 00000054 00005c52
*/
if (temp > I8K_MAX_TEMP) {
- temp = prev;
- prev = I8K_MAX_TEMP;
+ temp = prev[sensor];
+ prev[sensor] = I8K_MAX_TEMP;
} else {
- prev = temp;
+ prev[sensor] = temp;
}
#endif
@@ -309,7 +325,8 @@ static int i8k_get_dell_signature(int req_fn)
struct smm_regs regs = { .eax = req_fn, };
int rc;
- if ((rc = i8k_smm(&regs)) < 0)
+ rc = i8k_smm(&regs);
+ if (rc < 0)
return rc;
return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1;
@@ -328,12 +345,14 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
switch (cmd) {
case I8K_BIOS_VERSION:
- val = i8k_get_bios_version();
+ val = (bios_version[0] << 16) |
+ (bios_version[1] << 8) | bios_version[2];
break;
case I8K_MACHINE_ID:
memset(buff, 0, 16);
- strlcpy(buff, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), sizeof(buff));
+ strlcpy(buff, i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
+ sizeof(buff));
break;
case I8K_FN_STATUS:
@@ -470,12 +489,13 @@ static ssize_t i8k_hwmon_show_temp(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
- int cpu_temp;
+ int index = to_sensor_dev_attr(devattr)->index;
+ int temp;
- cpu_temp = i8k_get_temp(0);
- if (cpu_temp < 0)
- return cpu_temp;
- return sprintf(buf, "%d\n", cpu_temp * 1000);
+ temp = i8k_get_temp(index);
+ if (temp < 0)
+ return temp;
+ return sprintf(buf, "%d\n", temp * 1000);
}
static ssize_t i8k_hwmon_show_fan(struct device *dev,
@@ -491,12 +511,44 @@ static ssize_t i8k_hwmon_show_fan(struct device *dev,
return sprintf(buf, "%d\n", fan_speed);
}
+static ssize_t i8k_hwmon_show_pwm(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ int status;
+
+ status = i8k_get_fan_status(index);
+ if (status < 0)
+ return -EIO;
+ return sprintf(buf, "%d\n", clamp_val(status * 128, 0, 255));
+}
+
+static ssize_t i8k_hwmon_set_pwm(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 128), 0, 2);
+
+ mutex_lock(&i8k_mutex);
+ err = i8k_set_fan(index, val);
+ mutex_unlock(&i8k_mutex);
+
+ return err < 0 ? -EIO : count;
+}
+
static ssize_t i8k_hwmon_show_label(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
- static const char *labels[4] = {
- "i8k",
+ static const char *labels[3] = {
"CPU",
"Left Fan",
"Right Fan",
@@ -506,108 +558,108 @@ static ssize_t i8k_hwmon_show_label(struct device *dev,
return sprintf(buf, "%s\n", labels[index]);
}
-static DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 3);
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
I8K_FAN_LEFT);
+static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm,
+ i8k_hwmon_set_pwm, I8K_FAN_LEFT);
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
I8K_FAN_RIGHT);
-static SENSOR_DEVICE_ATTR(name, S_IRUGO, i8k_hwmon_show_label, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1);
-static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2);
-static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 3);
+static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm,
+ i8k_hwmon_set_pwm, I8K_FAN_RIGHT);
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2);
+
+static struct attribute *i8k_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */
+ &sensor_dev_attr_temp1_label.dev_attr.attr, /* 1 */
+ &sensor_dev_attr_temp2_input.dev_attr.attr, /* 2 */
+ &sensor_dev_attr_temp3_input.dev_attr.attr, /* 3 */
+ &sensor_dev_attr_temp4_input.dev_attr.attr, /* 4 */
+ &sensor_dev_attr_fan1_input.dev_attr.attr, /* 5 */
+ &sensor_dev_attr_pwm1.dev_attr.attr, /* 6 */
+ &sensor_dev_attr_fan1_label.dev_attr.attr, /* 7 */
+ &sensor_dev_attr_fan2_input.dev_attr.attr, /* 8 */
+ &sensor_dev_attr_pwm2.dev_attr.attr, /* 9 */
+ &sensor_dev_attr_fan2_label.dev_attr.attr, /* 10 */
+ NULL
+};
-static void i8k_hwmon_remove_files(struct device *dev)
+static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr,
+ int index)
{
- device_remove_file(dev, &dev_attr_temp1_input);
- device_remove_file(dev, &sensor_dev_attr_fan1_input.dev_attr);
- device_remove_file(dev, &sensor_dev_attr_fan2_input.dev_attr);
- device_remove_file(dev, &sensor_dev_attr_temp1_label.dev_attr);
- device_remove_file(dev, &sensor_dev_attr_fan1_label.dev_attr);
- device_remove_file(dev, &sensor_dev_attr_fan2_label.dev_attr);
- device_remove_file(dev, &sensor_dev_attr_name.dev_attr);
+ if ((index == 0 || index == 1) &&
+ !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1))
+ return 0;
+ if (index == 2 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2))
+ return 0;
+ if (index == 3 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3))
+ return 0;
+ if (index == 4 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4))
+ return 0;
+ if (index >= 5 && index <= 7 &&
+ !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1))
+ return 0;
+ if (index >= 8 && index <= 10 &&
+ !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2))
+ return 0;
+
+ return attr->mode;
}
+static const struct attribute_group i8k_group = {
+ .attrs = i8k_attrs,
+ .is_visible = i8k_is_visible,
+};
+__ATTRIBUTE_GROUPS(i8k);
+
static int __init i8k_init_hwmon(void)
{
int err;
- i8k_hwmon_dev = hwmon_device_register(NULL);
- if (IS_ERR(i8k_hwmon_dev)) {
- err = PTR_ERR(i8k_hwmon_dev);
- i8k_hwmon_dev = NULL;
- printk(KERN_ERR "i8k: hwmon registration failed (%d)\n", err);
- return err;
- }
-
- /* Required name attribute */
- err = device_create_file(i8k_hwmon_dev,
- &sensor_dev_attr_name.dev_attr);
- if (err)
- goto exit_unregister;
+ i8k_hwmon_flags = 0;
/* CPU temperature attributes, if temperature reading is OK */
err = i8k_get_temp(0);
- if (err < 0) {
- dev_dbg(i8k_hwmon_dev,
- "Not creating temperature attributes (%d)\n", err);
- } else {
- err = device_create_file(i8k_hwmon_dev, &dev_attr_temp1_input);
- if (err)
- goto exit_remove_files;
- err = device_create_file(i8k_hwmon_dev,
- &sensor_dev_attr_temp1_label.dev_attr);
- if (err)
- goto exit_remove_files;
- }
+ if (err >= 0)
+ i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP1;
+ /* check for additional temperature sensors */
+ err = i8k_get_temp(1);
+ if (err >= 0)
+ i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP2;
+ err = i8k_get_temp(2);
+ if (err >= 0)
+ i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP3;
+ err = i8k_get_temp(3);
+ if (err >= 0)
+ i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4;
/* Left fan attributes, if left fan is present */
err = i8k_get_fan_status(I8K_FAN_LEFT);
- if (err < 0) {
- dev_dbg(i8k_hwmon_dev,
- "Not creating %s fan attributes (%d)\n", "left", err);
- } else {
- err = device_create_file(i8k_hwmon_dev,
- &sensor_dev_attr_fan1_input.dev_attr);
- if (err)
- goto exit_remove_files;
- err = device_create_file(i8k_hwmon_dev,
- &sensor_dev_attr_fan1_label.dev_attr);
- if (err)
- goto exit_remove_files;
- }
+ if (err >= 0)
+ i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN1;
/* Right fan attributes, if right fan is present */
err = i8k_get_fan_status(I8K_FAN_RIGHT);
- if (err < 0) {
- dev_dbg(i8k_hwmon_dev,
- "Not creating %s fan attributes (%d)\n", "right", err);
- } else {
- err = device_create_file(i8k_hwmon_dev,
- &sensor_dev_attr_fan2_input.dev_attr);
- if (err)
- goto exit_remove_files;
- err = device_create_file(i8k_hwmon_dev,
- &sensor_dev_attr_fan2_label.dev_attr);
- if (err)
- goto exit_remove_files;
- }
+ if (err >= 0)
+ i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2;
+ i8k_hwmon_dev = hwmon_device_register_with_groups(NULL, "i8k", NULL,
+ i8k_groups);
+ if (IS_ERR(i8k_hwmon_dev)) {
+ err = PTR_ERR(i8k_hwmon_dev);
+ i8k_hwmon_dev = NULL;
+ pr_err("hwmon registration failed (%d)\n", err);
+ return err;
+ }
return 0;
-
- exit_remove_files:
- i8k_hwmon_remove_files(i8k_hwmon_dev);
- exit_unregister:
- hwmon_device_unregister(i8k_hwmon_dev);
- return err;
-}
-
-static void __exit i8k_exit_hwmon(void)
-{
- i8k_hwmon_remove_files(i8k_hwmon_dev);
- hwmon_device_unregister(i8k_hwmon_dev);
}
-static struct dmi_system_id __initdata i8k_dmi_table[] = {
+static struct dmi_system_id i8k_dmi_table[] __initdata = {
{
.ident = "Dell Inspiron",
.matches = {
@@ -664,7 +716,30 @@ static struct dmi_system_id __initdata i8k_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"),
},
},
- { }
+ {
+ .ident = "Dell XPS421",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "XPS L421X"),
+ },
+ },
+ {
+ .ident = "Dell Studio",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Studio"),
+ },
+ .driver_data = (void *)1, /* fan multiplier override */
+ },
+ {
+ .ident = "Dell XPS M140",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"),
+ },
+ .driver_data = (void *)1, /* fan multiplier override */
+ },
+ { }
};
/*
@@ -672,8 +747,7 @@ static struct dmi_system_id __initdata i8k_dmi_table[] = {
*/
static int __init i8k_probe(void)
{
- char buff[4];
- int version;
+ const struct dmi_system_id *id;
/*
* Get DMI information
@@ -682,49 +756,30 @@ static int __init i8k_probe(void)
if (!ignore_dmi && !force)
return -ENODEV;
- printk(KERN_INFO "i8k: not running on a supported Dell system.\n");
- printk(KERN_INFO "i8k: vendor=%s, model=%s, version=%s\n",
+ pr_info("not running on a supported Dell system.\n");
+ pr_info("vendor=%s, model=%s, version=%s\n",
i8k_get_dmi_data(DMI_SYS_VENDOR),
i8k_get_dmi_data(DMI_PRODUCT_NAME),
i8k_get_dmi_data(DMI_BIOS_VERSION));
}
- strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), sizeof(bios_version));
+ strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION),
+ sizeof(bios_version));
/*
* Get SMM Dell signature
*/
if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) &&
i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) {
- printk(KERN_ERR "i8k: unable to get SMM Dell signature\n");
+ pr_err("unable to get SMM Dell signature\n");
if (!force)
return -ENODEV;
}
- /*
- * Get SMM BIOS version.
- */
- version = i8k_get_bios_version();
- if (version <= 0) {
- printk(KERN_WARNING "i8k: unable to get SMM BIOS version\n");
- } else {
- buff[0] = (version >> 16) & 0xff;
- buff[1] = (version >> 8) & 0xff;
- buff[2] = (version) & 0xff;
- buff[3] = '\0';
- /*
- * If DMI BIOS version is unknown use SMM BIOS version.
- */
- if (!dmi_get_system_info(DMI_BIOS_VERSION))
- strlcpy(bios_version, buff, sizeof(bios_version));
-
- /*
- * Check if the two versions match.
- */
- if (strncmp(buff, bios_version, sizeof(bios_version)) != 0)
- printk(KERN_WARNING "i8k: BIOS version mismatch: %s != %s\n",
- buff, bios_version);
- }
+ i8k_fan_mult = fan_mult;
+ id = dmi_first_match(i8k_dmi_table);
+ if (id && fan_mult == I8K_FAN_MULT && id->driver_data)
+ i8k_fan_mult = (unsigned long)id->driver_data;
return 0;
}
@@ -747,10 +802,6 @@ static int __init i8k_init(void)
if (err)
goto exit_remove_proc;
- printk(KERN_INFO
- "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n",
- I8K_VERSION);
-
return 0;
exit_remove_proc:
@@ -760,7 +811,7 @@ static int __init i8k_init(void)
static void __exit i8k_exit(void)
{
- i8k_exit_hwmon();
+ hwmon_device_unregister(i8k_hwmon_dev);
remove_proc_entry("i8k", NULL);
}
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index 0baa8fab4ea..db1c9b7adaa 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -50,6 +50,18 @@ config IPMI_SI
Currently, only KCS and SMIC are supported. If
you are using IPMI, you should probably say "y" here.
+config IPMI_SI_PROBE_DEFAULTS
+ bool 'Probe for all possible IPMI system interfaces by default'
+ default n
+ depends on IPMI_SI
+ help
+ Modern systems will usually expose IPMI interfaces via a discoverable
+ firmware mechanism such as ACPI or DMI. Older systems do not, and so
+ the driver is forced to probe hardware manually. This may cause boot
+ delays. Say "n" here to disable this manual probing. IPMI will then
+ only be available on older systems if the "ipmi_si_intf.trydefaults=1"
+ boot argument is passed.
+
config IPMI_WATCHDOG
tristate 'IPMI Watchdog Timer'
help
diff --git a/drivers/char/ipmi/ipmi_bt_sm.c b/drivers/char/ipmi/ipmi_bt_sm.c
index cdd4c09fda9..61e71616689 100644
--- a/drivers/char/ipmi/ipmi_bt_sm.c
+++ b/drivers/char/ipmi/ipmi_bt_sm.c
@@ -95,9 +95,9 @@ struct si_sm_data {
enum bt_states state;
unsigned char seq; /* BT sequence number */
struct si_sm_io *io;
- unsigned char write_data[IPMI_MAX_MSG_LENGTH];
+ unsigned char write_data[IPMI_MAX_MSG_LENGTH + 2]; /* +2 for memcpy */
int write_count;
- unsigned char read_data[IPMI_MAX_MSG_LENGTH];
+ unsigned char read_data[IPMI_MAX_MSG_LENGTH + 2]; /* +2 for memcpy */
int read_count;
int truncated;
long timeout; /* microseconds countdown */
@@ -201,7 +201,7 @@ static unsigned int bt_init_data(struct si_sm_data *bt, struct si_sm_io *io)
}
bt->state = BT_STATE_IDLE; /* start here */
bt->complete = BT_STATE_IDLE; /* end here */
- bt->BT_CAP_req2rsp = BT_NORMAL_TIMEOUT * 1000000;
+ bt->BT_CAP_req2rsp = BT_NORMAL_TIMEOUT * USEC_PER_SEC;
bt->BT_CAP_retries = BT_NORMAL_RETRY_LIMIT;
/* BT_CAP_outreqs == zero is a flag to read BT Capabilities */
return 3; /* We claim 3 bytes of space; ought to check SPMI table */
@@ -352,7 +352,7 @@ static inline void write_all_bytes(struct si_sm_data *bt)
static inline int read_all_bytes(struct si_sm_data *bt)
{
- unsigned char i;
+ unsigned int i;
/*
* length is "framing info", minimum = 4: NetFn, Seq, Cmd, cCode.
@@ -613,7 +613,7 @@ static enum si_sm_result bt_event(struct si_sm_data *bt, long time)
HOST2BMC(42); /* Sequence number */
HOST2BMC(3); /* Cmd == Soft reset */
BT_CONTROL(BT_H2B_ATN);
- bt->timeout = BT_RESET_DELAY * 1000000;
+ bt->timeout = BT_RESET_DELAY * USEC_PER_SEC;
BT_STATE_CHANGE(BT_STATE_RESET3,
SI_SM_CALL_WITH_DELAY);
@@ -651,14 +651,14 @@ static enum si_sm_result bt_event(struct si_sm_data *bt, long time)
bt_init_data(bt, bt->io);
if ((i == 8) && !BT_CAP[2]) {
bt->BT_CAP_outreqs = BT_CAP[3];
- bt->BT_CAP_req2rsp = BT_CAP[6] * 1000000;
+ bt->BT_CAP_req2rsp = BT_CAP[6] * USEC_PER_SEC;
bt->BT_CAP_retries = BT_CAP[7];
} else
printk(KERN_WARNING "IPMI BT: using default values\n");
if (!bt->BT_CAP_outreqs)
bt->BT_CAP_outreqs = 1;
printk(KERN_WARNING "IPMI BT: req2rsp=%ld secs retries=%d\n",
- bt->BT_CAP_req2rsp / 1000000L, bt->BT_CAP_retries);
+ bt->BT_CAP_req2rsp / USEC_PER_SEC, bt->BT_CAP_retries);
bt->timeout = bt->BT_CAP_req2rsp;
return SI_SM_CALL_WITHOUT_DELAY;
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c
index 9eb360ff8ca..ec318bf434a 100644
--- a/drivers/char/ipmi/ipmi_devintf.c
+++ b/drivers/char/ipmi/ipmi_devintf.c
@@ -810,6 +810,7 @@ static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd,
struct ipmi_recv __user *precv64;
struct ipmi_recv recv64;
+ memset(&recv64, 0, sizeof(recv64));
if (get_compat_ipmi_recv(&recv64, compat_ptr(arg)))
return -EFAULT;
@@ -837,13 +838,25 @@ static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd,
return ipmi_ioctl(filep, cmd, arg);
}
}
+
+static long unlocked_compat_ipmi_ioctl(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret;
+
+ mutex_lock(&ipmi_mutex);
+ ret = compat_ipmi_ioctl(filep, cmd, arg);
+ mutex_unlock(&ipmi_mutex);
+
+ return ret;
+}
#endif
static const struct file_operations ipmi_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = ipmi_unlocked_ioctl,
#ifdef CONFIG_COMPAT
- .compat_ioctl = compat_ipmi_ioctl,
+ .compat_ioctl = unlocked_compat_ipmi_ioctl,
#endif
.open = ipmi_open,
.release = ipmi_release,
diff --git a/drivers/char/ipmi/ipmi_kcs_sm.c b/drivers/char/ipmi/ipmi_kcs_sm.c
index e53fc24c6af..8c25f596808 100644
--- a/drivers/char/ipmi/ipmi_kcs_sm.c
+++ b/drivers/char/ipmi/ipmi_kcs_sm.c
@@ -118,8 +118,8 @@ enum kcs_states {
#define MAX_KCS_WRITE_SIZE IPMI_MAX_MSG_LENGTH
/* Timeouts in microseconds. */
-#define IBF_RETRY_TIMEOUT 5000000
-#define OBF_RETRY_TIMEOUT 5000000
+#define IBF_RETRY_TIMEOUT (5*USEC_PER_SEC)
+#define OBF_RETRY_TIMEOUT (5*USEC_PER_SEC)
#define MAX_ERROR_RETRIES 10
#define ERROR0_OBF_WAIT_JIFFIES (2*HZ)
@@ -251,8 +251,9 @@ static inline int check_obf(struct si_sm_data *kcs, unsigned char status,
if (!GET_STATUS_OBF(status)) {
kcs->obf_timeout -= time;
if (kcs->obf_timeout < 0) {
- start_error_recovery(kcs, "OBF not ready in time");
- return 1;
+ kcs->obf_timeout = OBF_RETRY_TIMEOUT;
+ start_error_recovery(kcs, "OBF not ready in time");
+ return 1;
}
return 0;
}
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 053201b062a..e6db9381b2c 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -55,6 +55,7 @@ static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);
static int ipmi_init_msghandler(void);
static void smi_recv_tasklet(unsigned long);
static void handle_new_recv_msgs(ipmi_smi_t intf);
+static void need_waiter(ipmi_smi_t intf);
static int initialized;
@@ -73,14 +74,28 @@ static struct proc_dir_entry *proc_ipmi_root;
*/
#define MAX_MSG_TIMEOUT 60000
+/* Call every ~1000 ms. */
+#define IPMI_TIMEOUT_TIME 1000
+
+/* How many jiffies does it take to get to the timeout time. */
+#define IPMI_TIMEOUT_JIFFIES ((IPMI_TIMEOUT_TIME * HZ) / 1000)
+
+/*
+ * Request events from the queue every second (this is the number of
+ * IPMI_TIMEOUT_TIMES between event requests). Hopefully, in the
+ * future, IPMI will add a way to know immediately if an event is in
+ * the queue and this silliness can go away.
+ */
+#define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME))
+
/*
* The main "user" data structure.
*/
struct ipmi_user {
struct list_head link;
- /* Set to "0" when the user is destroyed. */
- int valid;
+ /* Set to false when the user is destroyed. */
+ bool valid;
struct kref refcount;
@@ -92,7 +107,7 @@ struct ipmi_user {
ipmi_smi_t intf;
/* Does this interface receive IPMI events? */
- int gets_events;
+ bool gets_events;
};
struct cmd_rcvr {
@@ -383,6 +398,9 @@ struct ipmi_smi {
unsigned int waiting_events_count; /* How many events in queue? */
char delivering_events;
char event_msg_printed;
+ atomic_t event_waiters;
+ unsigned int ticks_to_req_ev;
+ int last_needs_timer;
/*
* The event receiver for my BMC, only really used at panic
@@ -395,7 +413,7 @@ struct ipmi_smi {
/* For handling of maintenance mode. */
int maintenance_mode;
- int maintenance_mode_enable;
+ bool maintenance_mode_enable;
int auto_maintenance_timeout;
spinlock_t maintenance_mode_lock; /* Used in a timer... */
@@ -451,7 +469,6 @@ static DEFINE_MUTEX(ipmi_interfaces_mutex);
static LIST_HEAD(smi_watchers);
static DEFINE_MUTEX(smi_watchers_mutex);
-
#define ipmi_inc_stat(intf, stat) \
atomic_inc(&(intf)->stats[IPMI_STAT_ ## stat])
#define ipmi_get_stat(intf, stat) \
@@ -772,6 +789,7 @@ static int intf_next_seq(ipmi_smi_t intf,
*seq = i;
*seqid = intf->seq_table[i].seqid;
intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ;
+ need_waiter(intf);
} else {
rv = -EAGAIN;
}
@@ -941,7 +959,7 @@ int ipmi_create_user(unsigned int if_num,
new_user->handler = handler;
new_user->handler_data = handler_data;
new_user->intf = intf;
- new_user->gets_events = 0;
+ new_user->gets_events = false;
if (!try_module_get(intf->handlers->owner)) {
rv = -ENODEV;
@@ -962,10 +980,15 @@ int ipmi_create_user(unsigned int if_num,
*/
mutex_unlock(&ipmi_interfaces_mutex);
- new_user->valid = 1;
+ new_user->valid = true;
spin_lock_irqsave(&intf->seq_lock, flags);
list_add_rcu(&new_user->link, &intf->users);
spin_unlock_irqrestore(&intf->seq_lock, flags);
+ if (handler->ipmi_watchdog_pretimeout) {
+ /* User wants pretimeouts, so make sure to watch for them. */
+ if (atomic_inc_return(&intf->event_waiters) == 1)
+ need_waiter(intf);
+ }
*user = new_user;
return 0;
@@ -1019,7 +1042,13 @@ int ipmi_destroy_user(ipmi_user_t user)
struct cmd_rcvr *rcvr;
struct cmd_rcvr *rcvrs = NULL;
- user->valid = 0;
+ user->valid = false;
+
+ if (user->handler->ipmi_watchdog_pretimeout)
+ atomic_dec(&intf->event_waiters);
+
+ if (user->gets_events)
+ atomic_dec(&intf->event_waiters);
/* Remove the user from the interface's sequence table. */
spin_lock_irqsave(&intf->seq_lock, flags);
@@ -1155,25 +1184,23 @@ int ipmi_set_maintenance_mode(ipmi_user_t user, int mode)
if (intf->maintenance_mode != mode) {
switch (mode) {
case IPMI_MAINTENANCE_MODE_AUTO:
- intf->maintenance_mode = mode;
intf->maintenance_mode_enable
= (intf->auto_maintenance_timeout > 0);
break;
case IPMI_MAINTENANCE_MODE_OFF:
- intf->maintenance_mode = mode;
- intf->maintenance_mode_enable = 0;
+ intf->maintenance_mode_enable = false;
break;
case IPMI_MAINTENANCE_MODE_ON:
- intf->maintenance_mode = mode;
- intf->maintenance_mode_enable = 1;
+ intf->maintenance_mode_enable = true;
break;
default:
rv = -EINVAL;
goto out_unlock;
}
+ intf->maintenance_mode = mode;
maintenance_mode_update(intf);
}
@@ -1184,7 +1211,7 @@ int ipmi_set_maintenance_mode(ipmi_user_t user, int mode)
}
EXPORT_SYMBOL(ipmi_set_maintenance_mode);
-int ipmi_set_gets_events(ipmi_user_t user, int val)
+int ipmi_set_gets_events(ipmi_user_t user, bool val)
{
unsigned long flags;
ipmi_smi_t intf = user->intf;
@@ -1194,8 +1221,18 @@ int ipmi_set_gets_events(ipmi_user_t user, int val)
INIT_LIST_HEAD(&msgs);
spin_lock_irqsave(&intf->events_lock, flags);
+ if (user->gets_events == val)
+ goto out;
+
user->gets_events = val;
+ if (val) {
+ if (atomic_inc_return(&intf->event_waiters) == 1)
+ need_waiter(intf);
+ } else {
+ atomic_dec(&intf->event_waiters);
+ }
+
if (intf->delivering_events)
/*
* Another thread is delivering events for this, so
@@ -1289,6 +1326,9 @@ int ipmi_register_for_cmd(ipmi_user_t user,
goto out_unlock;
}
+ if (atomic_inc_return(&intf->event_waiters) == 1)
+ need_waiter(intf);
+
list_add_rcu(&rcvr->link, &intf->cmd_rcvrs);
out_unlock:
@@ -1330,6 +1370,7 @@ int ipmi_unregister_for_cmd(ipmi_user_t user,
mutex_unlock(&intf->cmd_rcvrs_mutex);
synchronize_rcu();
while (rcvrs) {
+ atomic_dec(&intf->event_waiters);
rcvr = rcvrs;
rcvrs = rcvr->next;
kfree(rcvr);
@@ -1535,7 +1576,7 @@ static int i_ipmi_request(ipmi_user_t user,
= IPMI_MAINTENANCE_MODE_TIMEOUT;
if (!intf->maintenance_mode
&& !intf->maintenance_mode_enable) {
- intf->maintenance_mode_enable = 1;
+ intf->maintenance_mode_enable = true;
maintenance_mode_update(intf);
}
spin_unlock_irqrestore(&intf->maintenance_mode_lock,
@@ -1848,7 +1889,7 @@ int ipmi_request_settime(ipmi_user_t user,
int retries,
unsigned int retry_time_ms)
{
- unsigned char saddr, lun;
+ unsigned char saddr = 0, lun = 0;
int rv;
if (!user)
@@ -1917,7 +1958,7 @@ static int smi_ipmb_proc_show(struct seq_file *m, void *v)
static int smi_ipmb_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, smi_ipmb_proc_show, PDE(inode)->data);
+ return single_open(file, smi_ipmb_proc_show, PDE_DATA(inode));
}
static const struct file_operations smi_ipmb_proc_ops = {
@@ -1938,7 +1979,7 @@ static int smi_version_proc_show(struct seq_file *m, void *v)
static int smi_version_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, smi_version_proc_show, PDE(inode)->data);
+ return single_open(file, smi_version_proc_show, PDE_DATA(inode));
}
static const struct file_operations smi_version_proc_ops = {
@@ -2013,7 +2054,7 @@ static int smi_stats_proc_show(struct seq_file *m, void *v)
static int smi_stats_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, smi_stats_proc_show, PDE(inode)->data);
+ return single_open(file, smi_stats_proc_show, PDE_DATA(inode));
}
static const struct file_operations smi_stats_proc_ops = {
@@ -2037,12 +2078,11 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name,
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
- entry->name = kmalloc(strlen(name)+1, GFP_KERNEL);
+ entry->name = kstrdup(name, GFP_KERNEL);
if (!entry->name) {
kfree(entry);
return -ENOMEM;
}
- strcpy(entry->name, name);
file = proc_create_data(name, 0, smi->proc_dir, proc_ops, data);
if (!file) {
@@ -2877,6 +2917,8 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
(unsigned long) intf);
atomic_set(&intf->watchdog_pretimeouts_to_deliver, 0);
spin_lock_init(&intf->events_lock);
+ atomic_set(&intf->event_waiters, 0);
+ intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
INIT_LIST_HEAD(&intf->waiting_events);
intf->waiting_events_count = 0;
mutex_init(&intf->cmd_rcvrs_mutex);
@@ -3966,7 +4008,8 @@ smi_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg,
static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent,
struct list_head *timeouts, long timeout_period,
- int slot, unsigned long *flags)
+ int slot, unsigned long *flags,
+ unsigned int *waiting_msgs)
{
struct ipmi_recv_msg *msg;
struct ipmi_smi_handlers *handlers;
@@ -3978,8 +4021,10 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent,
return;
ent->timeout -= timeout_period;
- if (ent->timeout > 0)
+ if (ent->timeout > 0) {
+ (*waiting_msgs)++;
return;
+ }
if (ent->retries_left == 0) {
/* The message has used all its retries. */
@@ -3996,6 +4041,8 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent,
struct ipmi_smi_msg *smi_msg;
/* More retries, send again. */
+ (*waiting_msgs)++;
+
/*
* Start with the max timer, set to normal timer after
* the message is sent.
@@ -4041,117 +4088,118 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent,
}
}
-static void ipmi_timeout_handler(long timeout_period)
+static unsigned int ipmi_timeout_handler(ipmi_smi_t intf, long timeout_period)
{
- ipmi_smi_t intf;
struct list_head timeouts;
struct ipmi_recv_msg *msg, *msg2;
unsigned long flags;
int i;
+ unsigned int waiting_msgs = 0;
- rcu_read_lock();
- list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
- tasklet_schedule(&intf->recv_tasklet);
-
- /*
- * Go through the seq table and find any messages that
- * have timed out, putting them in the timeouts
- * list.
- */
- INIT_LIST_HEAD(&timeouts);
- spin_lock_irqsave(&intf->seq_lock, flags);
- for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++)
- check_msg_timeout(intf, &(intf->seq_table[i]),
- &timeouts, timeout_period, i,
- &flags);
- spin_unlock_irqrestore(&intf->seq_lock, flags);
+ /*
+ * Go through the seq table and find any messages that
+ * have timed out, putting them in the timeouts
+ * list.
+ */
+ INIT_LIST_HEAD(&timeouts);
+ spin_lock_irqsave(&intf->seq_lock, flags);
+ for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++)
+ check_msg_timeout(intf, &(intf->seq_table[i]),
+ &timeouts, timeout_period, i,
+ &flags, &waiting_msgs);
+ spin_unlock_irqrestore(&intf->seq_lock, flags);
- list_for_each_entry_safe(msg, msg2, &timeouts, link)
- deliver_err_response(msg, IPMI_TIMEOUT_COMPLETION_CODE);
+ list_for_each_entry_safe(msg, msg2, &timeouts, link)
+ deliver_err_response(msg, IPMI_TIMEOUT_COMPLETION_CODE);
- /*
- * Maintenance mode handling. Check the timeout
- * optimistically before we claim the lock. It may
- * mean a timeout gets missed occasionally, but that
- * only means the timeout gets extended by one period
- * in that case. No big deal, and it avoids the lock
- * most of the time.
- */
+ /*
+ * Maintenance mode handling. Check the timeout
+ * optimistically before we claim the lock. It may
+ * mean a timeout gets missed occasionally, but that
+ * only means the timeout gets extended by one period
+ * in that case. No big deal, and it avoids the lock
+ * most of the time.
+ */
+ if (intf->auto_maintenance_timeout > 0) {
+ spin_lock_irqsave(&intf->maintenance_mode_lock, flags);
if (intf->auto_maintenance_timeout > 0) {
- spin_lock_irqsave(&intf->maintenance_mode_lock, flags);
- if (intf->auto_maintenance_timeout > 0) {
- intf->auto_maintenance_timeout
- -= timeout_period;
- if (!intf->maintenance_mode
- && (intf->auto_maintenance_timeout <= 0)) {
- intf->maintenance_mode_enable = 0;
- maintenance_mode_update(intf);
- }
+ intf->auto_maintenance_timeout
+ -= timeout_period;
+ if (!intf->maintenance_mode
+ && (intf->auto_maintenance_timeout <= 0)) {
+ intf->maintenance_mode_enable = false;
+ maintenance_mode_update(intf);
}
- spin_unlock_irqrestore(&intf->maintenance_mode_lock,
- flags);
}
+ spin_unlock_irqrestore(&intf->maintenance_mode_lock,
+ flags);
}
- rcu_read_unlock();
+
+ tasklet_schedule(&intf->recv_tasklet);
+
+ return waiting_msgs;
}
-static void ipmi_request_event(void)
+static void ipmi_request_event(ipmi_smi_t intf)
{
- ipmi_smi_t intf;
struct ipmi_smi_handlers *handlers;
- rcu_read_lock();
- /*
- * Called from the timer, no need to check if handlers is
- * valid.
- */
- list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
- /* No event requests when in maintenance mode. */
- if (intf->maintenance_mode_enable)
- continue;
+ /* No event requests when in maintenance mode. */
+ if (intf->maintenance_mode_enable)
+ return;
- handlers = intf->handlers;
- if (handlers)
- handlers->request_events(intf->send_info);
- }
- rcu_read_unlock();
+ handlers = intf->handlers;
+ if (handlers)
+ handlers->request_events(intf->send_info);
}
static struct timer_list ipmi_timer;
-/* Call every ~1000 ms. */
-#define IPMI_TIMEOUT_TIME 1000
-
-/* How many jiffies does it take to get to the timeout time. */
-#define IPMI_TIMEOUT_JIFFIES ((IPMI_TIMEOUT_TIME * HZ) / 1000)
-
-/*
- * Request events from the queue every second (this is the number of
- * IPMI_TIMEOUT_TIMES between event requests). Hopefully, in the
- * future, IPMI will add a way to know immediately if an event is in
- * the queue and this silliness can go away.
- */
-#define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME))
-
static atomic_t stop_operation;
-static unsigned int ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
static void ipmi_timeout(unsigned long data)
{
+ ipmi_smi_t intf;
+ int nt = 0;
+
if (atomic_read(&stop_operation))
return;
- ticks_to_req_ev--;
- if (ticks_to_req_ev == 0) {
- ipmi_request_event();
- ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
- }
+ rcu_read_lock();
+ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
+ int lnt = 0;
+
+ if (atomic_read(&intf->event_waiters)) {
+ intf->ticks_to_req_ev--;
+ if (intf->ticks_to_req_ev == 0) {
+ ipmi_request_event(intf);
+ intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
+ }
+ lnt++;
+ }
- ipmi_timeout_handler(IPMI_TIMEOUT_TIME);
+ lnt += ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME);
- mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
+ lnt = !!lnt;
+ if (lnt != intf->last_needs_timer &&
+ intf->handlers->set_need_watch)
+ intf->handlers->set_need_watch(intf->send_info, lnt);
+ intf->last_needs_timer = lnt;
+
+ nt += lnt;
+ }
+ rcu_read_unlock();
+
+ if (nt)
+ mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
}
+static void need_waiter(ipmi_smi_t intf)
+{
+ /* Racy, but worst case we start the timer twice. */
+ if (!timer_pending(&ipmi_timer))
+ mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
+}
static atomic_t smi_msg_inuse_count = ATOMIC_INIT(0);
static atomic_t recv_msg_inuse_count = ATOMIC_INIT(0);
@@ -4541,7 +4589,7 @@ static void __exit cleanup_ipmi(void)
del_timer_sync(&ipmi_timer);
#ifdef CONFIG_PROC_FS
- remove_proc_entry(proc_ipmi_root->name, NULL);
+ proc_remove(proc_ipmi_root);
#endif /* CONFIG_PROC_FS */
driver_unregister(&ipmidriver.driver);
diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c
index 2efa176beab..9f2e3be2c5b 100644
--- a/drivers/char/ipmi/ipmi_poweroff.c
+++ b/drivers/char/ipmi/ipmi_poweroff.c
@@ -659,7 +659,7 @@ static struct ipmi_smi_watcher smi_watcher = {
#ifdef CONFIG_PROC_FS
#include <linux/sysctl.h>
-static ctl_table ipmi_table[] = {
+static struct ctl_table ipmi_table[] = {
{ .procname = "poweroff_powercycle",
.data = &poweroff_powercycle,
.maxlen = sizeof(poweroff_powercycle),
@@ -668,14 +668,14 @@ static ctl_table ipmi_table[] = {
{ }
};
-static ctl_table ipmi_dir_table[] = {
+static struct ctl_table ipmi_dir_table[] = {
{ .procname = "ipmi",
.mode = 0555,
.child = ipmi_table },
{ }
};
-static ctl_table ipmi_root_table[] = {
+static struct ctl_table ipmi_root_table[] = {
{ .procname = "dev",
.mode = 0555,
.child = ipmi_dir_table },
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 1c7fdcd22a9..5d665680ae3 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -61,7 +61,6 @@
#include <linux/ipmi_smi.h>
#include <asm/io.h>
#include "ipmi_si_sm.h"
-#include <linux/init.h>
#include <linux/dmi.h>
#include <linux/string.h>
#include <linux/ctype.h>
@@ -71,6 +70,11 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#ifdef CONFIG_PARISC
+#include <asm/hardware.h> /* for register_parisc_driver() stuff */
+#include <asm/parisc-device.h>
+#endif
+
#define PFX "ipmi_si: "
/* Measure times between events in the driver. */
@@ -213,7 +217,7 @@ struct smi_info {
unsigned char msg_flags;
/* Does the BMC have an event buffer? */
- char has_event_buffer;
+ bool has_event_buffer;
/*
* If set to true, this will request events the next time the
@@ -226,7 +230,7 @@ struct smi_info {
* call. Generally used after a panic to make sure stuff goes
* out.
*/
- int run_to_completion;
+ bool run_to_completion;
/* The I/O port of an SI interface. */
int port;
@@ -244,19 +248,25 @@ struct smi_info {
/* The timer for this si. */
struct timer_list si_timer;
+ /* This flag is set, if the timer is running (timer_pending() isn't enough) */
+ bool timer_running;
+
/* The time (in jiffies) the last timeout occurred at. */
unsigned long last_timeout_jiffies;
/* Used to gracefully stop the timer without race conditions. */
atomic_t stop_operation;
+ /* Are we waiting for the events, pretimeouts, received msgs? */
+ atomic_t need_watch;
+
/*
* The driver will disable interrupts when it gets into a
* situation where it cannot handle messages due to lack of
* memory. Once that situation clears up, it will re-enable
* interrupts.
*/
- int interrupt_disabled;
+ bool interrupt_disabled;
/* From the get device id response... */
struct ipmi_device_id device_id;
@@ -269,7 +279,7 @@ struct smi_info {
* True if we allocated the device, false if it came from
* someplace else (like PCI).
*/
- int dev_registered;
+ bool dev_registered;
/* Slave address, could be reported from DMI. */
unsigned char slave_addr;
@@ -293,16 +303,19 @@ struct smi_info {
static int force_kipmid[SI_MAX_PARMS];
static int num_force_kipmid;
#ifdef CONFIG_PCI
-static int pci_registered;
+static bool pci_registered;
#endif
#ifdef CONFIG_ACPI
-static int pnp_registered;
+static bool pnp_registered;
+#endif
+#ifdef CONFIG_PARISC
+static bool parisc_registered;
#endif
static unsigned int kipmid_max_busy_us[SI_MAX_PARMS];
static int num_max_busy_us;
-static int unload_when_empty = 1;
+static bool unload_when_empty = true;
static int add_smi(struct smi_info *smi);
static int try_smi_init(struct smi_info *smi);
@@ -427,6 +440,13 @@ static void start_clear_flags(struct smi_info *smi_info)
smi_info->si_state = SI_CLEARING_FLAGS;
}
+static void smi_mod_timer(struct smi_info *smi_info, unsigned long new_val)
+{
+ smi_info->last_timeout_jiffies = jiffies;
+ mod_timer(&smi_info->si_timer, new_val);
+ smi_info->timer_running = true;
+}
+
/*
* When we have a situtaion where we run out of memory and cannot
* allocate messages, we just leave them in the BMC and run the system
@@ -437,10 +457,9 @@ static inline void disable_si_irq(struct smi_info *smi_info)
{
if ((smi_info->irq) && (!smi_info->interrupt_disabled)) {
start_disable_irq(smi_info);
- smi_info->interrupt_disabled = 1;
+ smi_info->interrupt_disabled = true;
if (!atomic_read(&smi_info->stop_operation))
- mod_timer(&smi_info->si_timer,
- jiffies + SI_TIMEOUT_JIFFIES);
+ smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES);
}
}
@@ -448,7 +467,7 @@ static inline void enable_si_irq(struct smi_info *smi_info)
{
if ((smi_info->irq) && (smi_info->interrupt_disabled)) {
start_enable_irq(smi_info);
- smi_info->interrupt_disabled = 0;
+ smi_info->interrupt_disabled = false;
}
}
@@ -663,8 +682,10 @@ static void handle_transaction_done(struct smi_info *smi_info)
/* We got the flags from the SMI, now handle them. */
smi_info->handlers->get_result(smi_info->si_sm, msg, 4);
if (msg[2] != 0) {
- dev_warn(smi_info->dev, "Could not enable interrupts"
- ", failed get, using polled mode.\n");
+ dev_warn(smi_info->dev,
+ "Couldn't get irq info: %x.\n", msg[2]);
+ dev_warn(smi_info->dev,
+ "Maybe ok, but ipmi might run very slowly.\n");
smi_info->si_state = SI_NORMAL;
} else {
msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
@@ -685,11 +706,13 @@ static void handle_transaction_done(struct smi_info *smi_info)
/* We got the flags from the SMI, now handle them. */
smi_info->handlers->get_result(smi_info->si_sm, msg, 4);
- if (msg[2] != 0)
- dev_warn(smi_info->dev, "Could not enable interrupts"
- ", failed set, using polled mode.\n");
- else
- smi_info->interrupt_disabled = 0;
+ if (msg[2] != 0) {
+ dev_warn(smi_info->dev,
+ "Couldn't set irq info: %x.\n", msg[2]);
+ dev_warn(smi_info->dev,
+ "Maybe ok, but ipmi might run very slowly.\n");
+ } else
+ smi_info->interrupt_disabled = false;
smi_info->si_state = SI_NORMAL;
break;
}
@@ -842,6 +865,19 @@ static enum si_sm_result smi_event_handler(struct smi_info *smi_info,
return si_sm_result;
}
+static void check_start_timer_thread(struct smi_info *smi_info)
+{
+ if (smi_info->si_state == SI_NORMAL && smi_info->curr_msg == NULL) {
+ smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES);
+
+ if (smi_info->thread)
+ wake_up_process(smi_info->thread);
+
+ start_next_msg(smi_info);
+ smi_event_handler(smi_info, 0);
+ }
+}
+
static void sender(void *send_info,
struct ipmi_smi_msg *msg,
int priority)
@@ -895,27 +931,11 @@ static void sender(void *send_info,
else
list_add_tail(&msg->link, &smi_info->xmit_msgs);
- if (smi_info->si_state == SI_NORMAL && smi_info->curr_msg == NULL) {
- /*
- * last_timeout_jiffies is updated here to avoid
- * smi_timeout() handler passing very large time_diff
- * value to smi_event_handler() that causes
- * the send command to abort.
- */
- smi_info->last_timeout_jiffies = jiffies;
-
- mod_timer(&smi_info->si_timer, jiffies + SI_TIMEOUT_JIFFIES);
-
- if (smi_info->thread)
- wake_up_process(smi_info->thread);
-
- start_next_msg(smi_info);
- smi_event_handler(smi_info, 0);
- }
+ check_start_timer_thread(smi_info);
spin_unlock_irqrestore(&smi_info->si_lock, flags);
}
-static void set_run_to_completion(void *send_info, int i_run_to_completion)
+static void set_run_to_completion(void *send_info, bool i_run_to_completion)
{
struct smi_info *smi_info = send_info;
enum si_sm_result result;
@@ -987,12 +1007,23 @@ static int ipmi_thread(void *data)
struct timespec busy_until;
ipmi_si_set_not_busy(&busy_until);
- set_user_nice(current, 19);
+ set_user_nice(current, MAX_NICE);
while (!kthread_should_stop()) {
int busy_wait;
spin_lock_irqsave(&(smi_info->si_lock), flags);
smi_result = smi_event_handler(smi_info, 0);
+
+ /*
+ * If the driver is doing something, there is a possible
+ * race with the timer. If the timer handler see idle,
+ * and the thread here sees something else, the timer
+ * handler won't restart the timer even though it is
+ * required. So start it here if necessary.
+ */
+ if (smi_result != SI_SM_IDLE && !smi_info->timer_running)
+ smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES);
+
spin_unlock_irqrestore(&(smi_info->si_lock), flags);
busy_wait = ipmi_thread_busy_wait(smi_result, smi_info,
&busy_until);
@@ -1000,9 +1031,15 @@ static int ipmi_thread(void *data)
; /* do nothing */
else if (smi_result == SI_SM_CALL_WITH_DELAY && busy_wait)
schedule();
- else if (smi_result == SI_SM_IDLE)
- schedule_timeout_interruptible(100);
- else
+ else if (smi_result == SI_SM_IDLE) {
+ if (atomic_read(&smi_info->need_watch)) {
+ schedule_timeout_interruptible(100);
+ } else {
+ /* Wait to be woken up when we are needed. */
+ __set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ }
+ } else
schedule_timeout_interruptible(1);
}
return 0;
@@ -1013,7 +1050,7 @@ static void poll(void *send_info)
{
struct smi_info *smi_info = send_info;
unsigned long flags = 0;
- int run_to_completion = smi_info->run_to_completion;
+ bool run_to_completion = smi_info->run_to_completion;
/*
* Make sure there is some delay in the poll loop so we can
@@ -1038,6 +1075,17 @@ static void request_events(void *send_info)
atomic_set(&smi_info->req_events, 1);
}
+static void set_need_watch(void *send_info, bool enable)
+{
+ struct smi_info *smi_info = send_info;
+ unsigned long flags;
+
+ atomic_set(&smi_info->need_watch, enable);
+ spin_lock_irqsave(&smi_info->si_lock, flags);
+ check_start_timer_thread(smi_info);
+ spin_unlock_irqrestore(&smi_info->si_lock, flags);
+}
+
static int initialized;
static void smi_timeout(unsigned long data)
@@ -1062,10 +1110,6 @@ static void smi_timeout(unsigned long data)
* SI_USEC_PER_JIFFY);
smi_result = smi_event_handler(smi_info, time_diff);
- spin_unlock_irqrestore(&(smi_info->si_lock), flags);
-
- smi_info->last_timeout_jiffies = jiffies_now;
-
if ((smi_info->irq) && (!smi_info->interrupt_disabled)) {
/* Running with interrupts, only do long timeouts. */
timeout = jiffies + SI_TIMEOUT_JIFFIES;
@@ -1087,7 +1131,10 @@ static void smi_timeout(unsigned long data)
do_mod_timer:
if (smi_result != SI_SM_IDLE)
- mod_timer(&(smi_info->si_timer), timeout);
+ smi_mod_timer(smi_info, timeout);
+ else
+ smi_info->timer_running = false;
+ spin_unlock_irqrestore(&(smi_info->si_lock), flags);
}
static irqreturn_t si_irq_handler(int irq, void *data)
@@ -1135,8 +1182,7 @@ static int smi_start_processing(void *send_info,
/* Set up the timer that drives the interface. */
setup_timer(&new_smi->si_timer, smi_timeout, (long)new_smi);
- new_smi->last_timeout_jiffies = jiffies;
- mod_timer(&new_smi->si_timer, jiffies + SI_TIMEOUT_JIFFIES);
+ smi_mod_timer(new_smi, jiffies + SI_TIMEOUT_JIFFIES);
/*
* Check if the user forcefully enabled the daemon.
@@ -1177,7 +1223,7 @@ static int get_smi_info(void *send_info, struct ipmi_smi_info *data)
return 0;
}
-static void set_maintenance_mode(void *send_info, int enable)
+static void set_maintenance_mode(void *send_info, bool enable)
{
struct smi_info *smi_info = send_info;
@@ -1191,6 +1237,7 @@ static struct ipmi_smi_handlers handlers = {
.get_smi_info = get_smi_info,
.sender = sender,
.request_events = request_events,
+ .set_need_watch = set_need_watch,
.set_maintenance_mode = set_maintenance_mode,
.set_run_to_completion = set_run_to_completion,
.poll = poll,
@@ -1208,7 +1255,17 @@ static int smi_num; /* Used to sequence the SMIs */
#define DEFAULT_REGSPACING 1
#define DEFAULT_REGSIZE 1
-static bool si_trydefaults = 1;
+#ifdef CONFIG_ACPI
+static bool si_tryacpi = 1;
+#endif
+#ifdef CONFIG_DMI
+static bool si_trydmi = 1;
+#endif
+static bool si_tryplatform = 1;
+#ifdef CONFIG_PCI
+static bool si_trypci = 1;
+#endif
+static bool si_trydefaults = IS_ENABLED(CONFIG_IPMI_SI_PROBE_DEFAULTS);
static char *si_type[SI_MAX_PARMS];
#define MAX_SI_TYPE_STR 30
static char si_type_str[MAX_SI_TYPE_STR];
@@ -1238,6 +1295,25 @@ MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See"
" Documentation/IPMI.txt in the kernel sources for the"
" gory details.");
+#ifdef CONFIG_ACPI
+module_param_named(tryacpi, si_tryacpi, bool, 0);
+MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the"
+ " default scan of the interfaces identified via ACPI");
+#endif
+#ifdef CONFIG_DMI
+module_param_named(trydmi, si_trydmi, bool, 0);
+MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the"
+ " default scan of the interfaces identified via DMI");
+#endif
+module_param_named(tryplatform, si_tryplatform, bool, 0);
+MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the"
+ " default scan of the interfaces identified via platform"
+ " interfaces like openfirmware");
+#ifdef CONFIG_PCI
+module_param_named(trypci, si_trypci, bool, 0);
+MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the"
+ " default scan of the interfaces identified via pci");
+#endif
module_param_named(trydefaults, si_trydefaults, bool, 0);
MODULE_PARM_DESC(trydefaults, "Setting this to 'false' will disable the"
" default scan of the KCS and SMIC interface at the standard"
@@ -1288,7 +1364,7 @@ module_param_array(force_kipmid, int, &num_force_kipmid, 0);
MODULE_PARM_DESC(force_kipmid, "Force the kipmi daemon to be enabled (1) or"
" disabled(0). Normally the IPMI driver auto-detects"
" this, but the value may be overridden by this parm.");
-module_param(unload_when_empty, int, 0);
+module_param(unload_when_empty, bool, 0);
MODULE_PARM_DESC(unload_when_empty, "Unload the module if no interfaces are"
" specified or found, default is 1. Setting to 0"
" is useful for hot add of devices using hotmod.");
@@ -1317,7 +1393,7 @@ static int std_irq_setup(struct smi_info *info)
if (info->si_type == SI_BT) {
rv = request_irq(info->irq,
si_bt_irq_handler,
- IRQF_SHARED | IRQF_DISABLED,
+ IRQF_SHARED,
DEVICE_NAME,
info);
if (!rv)
@@ -1327,7 +1403,7 @@ static int std_irq_setup(struct smi_info *info)
} else
rv = request_irq(info->irq,
si_irq_handler,
- IRQF_SHARED | IRQF_DISABLED,
+ IRQF_SHARED,
DEVICE_NAME,
info);
if (rv) {
@@ -1808,11 +1884,15 @@ static int hotmod_handler(const char *val, struct kernel_param *kp)
info->irq_setup = std_irq_setup;
info->slave_addr = ipmb;
- if (!add_smi(info)) {
- if (try_smi_init(info))
- cleanup_one_si(info);
- } else {
+ rv = add_smi(info);
+ if (rv) {
kfree(info);
+ goto out;
+ }
+ rv = try_smi_init(info);
+ if (rv) {
+ cleanup_one_si(info);
+ goto out;
}
} else {
/* remove */
@@ -2026,6 +2106,7 @@ struct SPMITable {
static int try_init_spmi(struct SPMITable *spmi)
{
struct smi_info *info;
+ int rv;
if (spmi->IPMIlegacy != 1) {
printk(KERN_INFO PFX "Bad SPMI legacy %d\n", spmi->IPMIlegacy);
@@ -2100,10 +2181,11 @@ static int try_init_spmi(struct SPMITable *spmi)
info->io.addr_data, info->io.regsize, info->io.regspacing,
info->irq);
- if (add_smi(info))
+ rv = add_smi(info);
+ if (rv)
kfree(info);
- return 0;
+ return rv;
}
static void spmi_find_bmc(void)
@@ -2137,6 +2219,7 @@ static int ipmi_pnp_probe(struct pnp_dev *dev,
acpi_handle handle;
acpi_status status;
unsigned long long tmp;
+ int rv;
acpi_dev = pnp_acpi_device(dev);
if (!acpi_dev)
@@ -2218,10 +2301,11 @@ static int ipmi_pnp_probe(struct pnp_dev *dev,
res, info->io.regsize, info->io.regspacing,
info->irq);
- if (add_smi(info))
- goto err_free;
+ rv = add_smi(info);
+ if (rv)
+ kfree(info);
- return 0;
+ return rv;
err_free:
kfree(info);
@@ -2246,6 +2330,8 @@ static struct pnp_driver ipmi_pnp_driver = {
.remove = ipmi_pnp_remove,
.id_table = pnp_dev_table,
};
+
+MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
#endif
#ifdef CONFIG_DMI
@@ -2523,16 +2609,20 @@ static int ipmi_pci_probe(struct pci_dev *pdev,
&pdev->resource[0], info->io.regsize, info->io.regspacing,
info->irq);
- if (add_smi(info))
+ rv = add_smi(info);
+ if (rv) {
kfree(info);
+ pci_disable_device(pdev);
+ }
- return 0;
+ return rv;
}
static void ipmi_pci_remove(struct pci_dev *pdev)
{
struct smi_info *info = pci_get_drvdata(pdev);
cleanup_one_si(info);
+ pci_disable_device(pdev);
}
static struct pci_device_id ipmi_pci_devices[] = {
@@ -2627,9 +2717,10 @@ static int ipmi_probe(struct platform_device *dev)
dev_set_drvdata(&dev->dev, info);
- if (add_smi(info)) {
+ ret = add_smi(info);
+ if (ret) {
kfree(info);
- return -EBUSY;
+ return ret;
}
#endif
return 0;
@@ -2664,6 +2755,64 @@ static struct platform_driver ipmi_driver = {
.remove = ipmi_remove,
};
+#ifdef CONFIG_PARISC
+static int ipmi_parisc_probe(struct parisc_device *dev)
+{
+ struct smi_info *info;
+ int rv;
+
+ info = smi_info_alloc();
+
+ if (!info) {
+ dev_err(&dev->dev,
+ "could not allocate memory for PARISC probe\n");
+ return -ENOMEM;
+ }
+
+ info->si_type = SI_KCS;
+ info->addr_source = SI_DEVICETREE;
+ info->io_setup = mem_setup;
+ info->io.addr_type = IPMI_MEM_ADDR_SPACE;
+ info->io.addr_data = dev->hpa.start;
+ info->io.regsize = 1;
+ info->io.regspacing = 1;
+ info->io.regshift = 0;
+ info->irq = 0; /* no interrupt */
+ info->irq_setup = NULL;
+ info->dev = &dev->dev;
+
+ dev_dbg(&dev->dev, "addr 0x%lx\n", info->io.addr_data);
+
+ dev_set_drvdata(&dev->dev, info);
+
+ rv = add_smi(info);
+ if (rv) {
+ kfree(info);
+ return rv;
+ }
+
+ return 0;
+}
+
+static int ipmi_parisc_remove(struct parisc_device *dev)
+{
+ cleanup_one_si(dev_get_drvdata(&dev->dev));
+ return 0;
+}
+
+static struct parisc_device_id ipmi_parisc_tbl[] = {
+ { HPHW_MC, HVERSION_REV_ANY_ID, 0x004, 0xC0 },
+ { 0, }
+};
+
+static struct parisc_driver ipmi_parisc_driver = {
+ .name = "ipmi",
+ .id_table = ipmi_parisc_tbl,
+ .probe = ipmi_parisc_probe,
+ .remove = ipmi_parisc_remove,
+};
+#endif /* CONFIG_PARISC */
+
static int wait_for_msg_done(struct smi_info *smi_info)
{
enum si_sm_result smi_result;
@@ -2674,7 +2823,7 @@ static int wait_for_msg_done(struct smi_info *smi_info)
smi_result == SI_SM_CALL_WITH_TICK_DELAY) {
schedule_timeout_uninterruptible(1);
smi_result = smi_info->handlers->event(
- smi_info->si_sm, 100);
+ smi_info->si_sm, jiffies_to_usecs(1));
} else if (smi_result == SI_SM_CALL_WITHOUT_DELAY) {
smi_result = smi_info->handlers->event(
smi_info->si_sm, 0);
@@ -2810,7 +2959,7 @@ static int smi_type_proc_show(struct seq_file *m, void *v)
static int smi_type_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, smi_type_proc_show, PDE(inode)->data);
+ return single_open(file, smi_type_proc_show, PDE_DATA(inode));
}
static const struct file_operations smi_type_proc_ops = {
@@ -2853,7 +3002,7 @@ static int smi_si_stats_proc_show(struct seq_file *m, void *v)
static int smi_si_stats_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, smi_si_stats_proc_show, PDE(inode)->data);
+ return single_open(file, smi_si_stats_proc_show, PDE_DATA(inode));
}
static const struct file_operations smi_si_stats_proc_ops = {
@@ -2881,7 +3030,7 @@ static int smi_params_proc_show(struct seq_file *m, void *v)
static int smi_params_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, smi_params_proc_show, PDE(inode)->data);
+ return single_open(file, smi_params_proc_show, PDE_DATA(inode));
}
static const struct file_operations smi_params_proc_ops = {
@@ -3223,18 +3372,19 @@ static int try_smi_init(struct smi_info *new_smi)
INIT_LIST_HEAD(&(new_smi->hp_xmit_msgs));
new_smi->curr_msg = NULL;
atomic_set(&new_smi->req_events, 0);
- new_smi->run_to_completion = 0;
+ new_smi->run_to_completion = false;
for (i = 0; i < SI_NUM_STATS; i++)
atomic_set(&new_smi->stats[i], 0);
- new_smi->interrupt_disabled = 1;
+ new_smi->interrupt_disabled = true;
atomic_set(&new_smi->stop_operation, 0);
+ atomic_set(&new_smi->need_watch, 0);
new_smi->intf_num = smi_num;
smi_num++;
rv = try_enable_event_buffer(new_smi);
if (rv == 0)
- new_smi->has_event_buffer = 1;
+ new_smi->has_event_buffer = true;
/*
* Start clearing the flags before we enable interrupts or the
@@ -3268,7 +3418,7 @@ static int try_smi_init(struct smi_info *new_smi)
rv);
goto out_err;
}
- new_smi->dev_registered = 1;
+ new_smi->dev_registered = true;
}
rv = ipmi_register_smi(&handlers,
@@ -3317,7 +3467,7 @@ static int try_smi_init(struct smi_info *new_smi)
wait_for_timer_and_thread(new_smi);
out_err:
- new_smi->interrupt_disabled = 1;
+ new_smi->interrupt_disabled = true;
if (new_smi->intf) {
ipmi_unregister_smi(new_smi->intf);
@@ -3353,7 +3503,7 @@ static int try_smi_init(struct smi_info *new_smi)
if (new_smi->dev_registered) {
platform_device_unregister(new_smi->pdev);
- new_smi->dev_registered = 0;
+ new_smi->dev_registered = false;
}
return rv;
@@ -3371,13 +3521,15 @@ static int init_ipmi_si(void)
return 0;
initialized = 1;
- rv = platform_driver_register(&ipmi_driver);
- if (rv) {
- printk(KERN_ERR PFX "Unable to register driver: %d\n", rv);
- return rv;
+ if (si_tryplatform) {
+ rv = platform_driver_register(&ipmi_driver);
+ if (rv) {
+ printk(KERN_ERR PFX "Unable to register "
+ "driver: %d\n", rv);
+ return rv;
+ }
}
-
/* Parse out the si_type string into its components. */
str = si_type_str;
if (*str != '\0') {
@@ -3400,24 +3552,38 @@ static int init_ipmi_si(void)
return 0;
#ifdef CONFIG_PCI
- rv = pci_register_driver(&ipmi_pci_driver);
- if (rv)
- printk(KERN_ERR PFX "Unable to register PCI driver: %d\n", rv);
- else
- pci_registered = 1;
+ if (si_trypci) {
+ rv = pci_register_driver(&ipmi_pci_driver);
+ if (rv)
+ printk(KERN_ERR PFX "Unable to register "
+ "PCI driver: %d\n", rv);
+ else
+ pci_registered = true;
+ }
#endif
#ifdef CONFIG_ACPI
- pnp_register_driver(&ipmi_pnp_driver);
- pnp_registered = 1;
+ if (si_tryacpi) {
+ pnp_register_driver(&ipmi_pnp_driver);
+ pnp_registered = true;
+ }
#endif
#ifdef CONFIG_DMI
- dmi_find_bmc();
+ if (si_trydmi)
+ dmi_find_bmc();
#endif
#ifdef CONFIG_ACPI
- spmi_find_bmc();
+ if (si_tryacpi)
+ spmi_find_bmc();
+#endif
+
+#ifdef CONFIG_PARISC
+ register_parisc_driver(&ipmi_parisc_driver);
+ parisc_registered = true;
+ /* poking PC IO addresses will crash machine, don't do it */
+ si_trydefaults = 0;
#endif
/* We prefer devices with interrupts, but in the case of a machine
@@ -3566,6 +3732,10 @@ static void cleanup_ipmi_si(void)
if (pnp_registered)
pnp_unregister_driver(&ipmi_pnp_driver);
#endif
+#ifdef CONFIG_PARISC
+ if (parisc_registered)
+ unregister_parisc_driver(&ipmi_parisc_driver);
+#endif
platform_driver_unregister(&ipmi_driver);
diff --git a/drivers/char/ipmi/ipmi_smic_sm.c b/drivers/char/ipmi/ipmi_smic_sm.c
index faed9297190..c8e77afa8b9 100644
--- a/drivers/char/ipmi/ipmi_smic_sm.c
+++ b/drivers/char/ipmi/ipmi_smic_sm.c
@@ -80,7 +80,7 @@ enum smic_states {
#define SMIC_MAX_ERROR_RETRIES 3
/* Timeouts in microseconds. */
-#define SMIC_RETRY_TIMEOUT 2000000
+#define SMIC_RETRY_TIMEOUT (2*USEC_PER_SEC)
/* SMIC Flags Register Bits */
#define SMIC_RX_DATA_READY 0x80
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index a741e418b45..c4094c4e22c 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -294,7 +294,7 @@ static int lp_wait_ready(int minor, int nonblock)
static ssize_t lp_write(struct file * file, const char __user * buf,
size_t count, loff_t *ppos)
{
- unsigned int minor = iminor(file->f_path.dentry->d_inode);
+ unsigned int minor = iminor(file_inode(file));
struct parport *port = lp_table[minor].dev->port;
char *kbuf = lp_table[minor].lp_buffer;
ssize_t retv = 0;
@@ -413,7 +413,7 @@ static ssize_t lp_read(struct file * file, char __user * buf,
size_t count, loff_t *ppos)
{
DEFINE_WAIT(wait);
- unsigned int minor=iminor(file->f_path.dentry->d_inode);
+ unsigned int minor=iminor(file_inode(file));
struct parport *port = lp_table[minor].dev->port;
ssize_t retval = 0;
char *kbuf = lp_table[minor].lp_buffer;
@@ -587,6 +587,8 @@ static int lp_do_ioctl(unsigned int minor, unsigned int cmd,
return -ENODEV;
switch ( cmd ) {
case LPTIME:
+ if (arg > UINT_MAX / HZ)
+ return -EINVAL;
LP_TIME(minor) = arg * HZ/100;
break;
case LPCHAR:
@@ -622,9 +624,12 @@ static int lp_do_ioctl(unsigned int minor, unsigned int cmd,
return -EFAULT;
break;
case LPGETSTATUS:
+ if (mutex_lock_interruptible(&lp_table[minor].port_mutex))
+ return -EINTR;
lp_claim_parport_or_block (&lp_table[minor]);
status = r_str(minor);
lp_release_parport (&lp_table[minor]);
+ mutex_unlock(&lp_table[minor].port_mutex);
if (copy_to_user(argp, &status, sizeof(int)))
return -EFAULT;
@@ -679,7 +684,7 @@ static long lp_ioctl(struct file *file, unsigned int cmd,
struct timeval par_timeout;
int ret;
- minor = iminor(file->f_path.dentry->d_inode);
+ minor = iminor(file_inode(file));
mutex_lock(&lp_mutex);
switch (cmd) {
case LPSETTIMEOUT:
@@ -707,7 +712,7 @@ static long lp_compat_ioctl(struct file *file, unsigned int cmd,
struct timeval par_timeout;
int ret;
- minor = iminor(file->f_path.dentry->d_inode);
+ minor = iminor(file_inode(file));
mutex_lock(&lp_mutex);
switch (cmd) {
case LPSETTIMEOUT:
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index c6fa3bc2baa..917403fe10d 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -21,13 +21,12 @@
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
-#include <linux/crash_dump.h>
#include <linux/backing-dev.h>
-#include <linux/bootmem.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
+#include <linux/aio.h>
#include <asm/uaccess.h>
@@ -100,6 +99,9 @@ static ssize_t read_mem(struct file *file, char __user *buf,
ssize_t read, sz;
char *ptr;
+ if (p != *ppos)
+ return 0;
+
if (!valid_phys_addr_range(p, count))
return -EFAULT;
read = 0;
@@ -158,6 +160,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf,
unsigned long copied;
void *ptr;
+ if (p != *ppos)
+ return -EFBIG;
+
if (!valid_phys_addr_range(p, count))
return -EFAULT;
@@ -356,40 +361,6 @@ static int mmap_kmem(struct file *file, struct vm_area_struct *vma)
}
#endif
-#ifdef CONFIG_CRASH_DUMP
-/*
- * Read memory corresponding to the old kernel.
- */
-static ssize_t read_oldmem(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- unsigned long pfn, offset;
- size_t read = 0, csize;
- int rc = 0;
-
- while (count) {
- pfn = *ppos / PAGE_SIZE;
- if (pfn > saved_max_pfn)
- return read;
-
- offset = (unsigned long)(*ppos % PAGE_SIZE);
- if (count > PAGE_SIZE - offset)
- csize = PAGE_SIZE - offset;
- else
- csize = count;
-
- rc = copy_oldmem_page(pfn, buf, csize, offset, 1);
- if (rc < 0)
- return rc;
- buf += csize;
- *ppos += csize;
- read += csize;
- count -= csize;
- }
- return read;
-}
-#endif
-
#ifdef CONFIG_DEVKMEM
/*
* This function reads the *virtual* memory as seen by the kernel.
@@ -399,7 +370,7 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
{
unsigned long p = *ppos;
ssize_t low_count, read, sz;
- char * kbuf; /* k-addr because vread() takes vmlist_lock rwlock */
+ char *kbuf; /* k-addr because vread() takes vmlist_lock rwlock */
int err = 0;
read = 0;
@@ -527,7 +498,7 @@ static ssize_t write_kmem(struct file *file, const char __user *buf,
unsigned long p = *ppos;
ssize_t wrote = 0;
ssize_t virtr = 0;
- char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
+ char *kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
int err = 0;
if (p < (unsigned long) high_memory) {
@@ -595,7 +566,7 @@ static ssize_t write_port(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long i = *ppos;
- const char __user * tmp = buf;
+ const char __user *tmp = buf;
if (!access_ok(VERIFY_READ, buf, count))
return -EFAULT;
@@ -627,6 +598,18 @@ static ssize_t write_null(struct file *file, const char __user *buf,
return count;
}
+static ssize_t aio_read_null(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos)
+{
+ return 0;
+}
+
+static ssize_t aio_write_null(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos)
+{
+ return iov_length(iov, nr_segs);
+}
+
static int pipe_to_null(struct pipe_inode_info *info, struct pipe_buffer *buf,
struct splice_desc *sd)
{
@@ -670,6 +653,24 @@ static ssize_t read_zero(struct file *file, char __user *buf,
return written ? written : -EFAULT;
}
+static ssize_t aio_read_zero(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos)
+{
+ size_t written = 0;
+ unsigned long i;
+ ssize_t ret;
+
+ for (i = 0; i < nr_segs; i++) {
+ ret = read_zero(iocb->ki_filp, iov[i].iov_base, iov[i].iov_len,
+ &pos);
+ if (ret < 0)
+ break;
+ written += ret;
+ }
+
+ return written ? written : -EFAULT;
+}
+
static int mmap_zero(struct file *file, struct vm_area_struct *vma)
{
#ifndef CONFIG_MMU
@@ -708,13 +709,13 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig)
{
loff_t ret;
- mutex_lock(&file->f_path.dentry->d_inode->i_mutex);
+ mutex_lock(&file_inode(file)->i_mutex);
switch (orig) {
case SEEK_CUR:
offset += file->f_pos;
case SEEK_SET:
/* to avoid userland mistaking f_pos=-9 as -EBADF=-9 */
- if ((unsigned long long)offset >= ~0xFFFULL) {
+ if (IS_ERR_VALUE((unsigned long long)offset)) {
ret = -EOVERFLOW;
break;
}
@@ -725,11 +726,11 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig)
default:
ret = -EINVAL;
}
- mutex_unlock(&file->f_path.dentry->d_inode->i_mutex);
+ mutex_unlock(&file_inode(file)->i_mutex);
return ret;
}
-static int open_port(struct inode * inode, struct file * filp)
+static int open_port(struct inode *inode, struct file *filp)
{
return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
}
@@ -738,9 +739,9 @@ static int open_port(struct inode * inode, struct file * filp)
#define full_lseek null_lseek
#define write_zero write_null
#define read_full read_zero
+#define aio_write_zero aio_write_null
#define open_mem open_port
#define open_kmem open_mem
-#define open_oldmem open_mem
static const struct file_operations mem_fops = {
.llseek = memory_lseek,
@@ -766,6 +767,8 @@ static const struct file_operations null_fops = {
.llseek = null_lseek,
.read = read_null,
.write = write_null,
+ .aio_read = aio_read_null,
+ .aio_write = aio_write_null,
.splice_write = splice_write_null,
};
@@ -782,6 +785,8 @@ static const struct file_operations zero_fops = {
.llseek = zero_lseek,
.read = read_zero,
.write = write_zero,
+ .aio_read = aio_read_zero,
+ .aio_write = aio_write_zero,
.mmap = mmap_zero,
};
@@ -801,14 +806,6 @@ static const struct file_operations full_fops = {
.write = write_full,
};
-#ifdef CONFIG_CRASH_DUMP
-static const struct file_operations oldmem_fops = {
- .read = read_oldmem,
- .open = open_oldmem,
- .llseek = default_llseek,
-};
-#endif
-
static const struct memdev {
const char *name;
umode_t mode;
@@ -830,9 +827,6 @@ static const struct memdev {
#ifdef CONFIG_PRINTK
[11] = { "kmsg", 0644, &kmsg_fops, NULL },
#endif
-#ifdef CONFIG_CRASH_DUMP
- [12] = { "oldmem", 0, &oldmem_fops, NULL },
-#endif
};
static int memory_open(struct inode *inode, struct file *filp)
@@ -898,7 +892,7 @@ static int __init chr_dev_init(void)
continue;
/*
- * Create /dev/port?
+ * Create /dev/port?
*/
if ((minor == DEVPORT_MINOR) && !arch_has_dev_port())
continue;
diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index 522136d4084..ffa97d261cf 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -114,7 +114,7 @@ static int misc_open(struct inode * inode, struct file * file)
int minor = iminor(inode);
struct miscdevice *c;
int err = -ENODEV;
- const struct file_operations *old_fops, *new_fops = NULL;
+ const struct file_operations *new_fops = NULL;
mutex_lock(&misc_mtx);
@@ -141,17 +141,11 @@ static int misc_open(struct inode * inode, struct file * file)
}
err = 0;
- old_fops = file->f_op;
- file->f_op = new_fops;
+ replace_fops(file, new_fops);
if (file->f_op->open) {
file->private_data = c;
- err=file->f_op->open(inode,file);
- if (err) {
- fops_put(file->f_op);
- file->f_op = fops_get(old_fops);
- }
+ err = file->f_op->open(inode,file);
}
- fops_put(old_fops);
fail:
mutex_unlock(&misc_mtx);
return err;
@@ -183,28 +177,30 @@ static const struct file_operations misc_fops = {
int misc_register(struct miscdevice * misc)
{
- struct miscdevice *c;
dev_t dev;
int err = 0;
INIT_LIST_HEAD(&misc->list);
mutex_lock(&misc_mtx);
- list_for_each_entry(c, &misc_list, list) {
- if (c->minor == misc->minor) {
- mutex_unlock(&misc_mtx);
- return -EBUSY;
- }
- }
if (misc->minor == MISC_DYNAMIC_MINOR) {
int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
if (i >= DYNAMIC_MINORS) {
- mutex_unlock(&misc_mtx);
- return -EBUSY;
+ err = -EBUSY;
+ goto out;
}
misc->minor = DYNAMIC_MINORS - i - 1;
set_bit(i, misc_minors);
+ } else {
+ struct miscdevice *c;
+
+ list_for_each_entry(c, &misc_list, list) {
+ if (c->minor == misc->minor) {
+ err = -EBUSY;
+ goto out;
+ }
+ }
}
dev = MKDEV(MISC_MAJOR, misc->minor);
diff --git a/drivers/char/msm_smd_pkt.c b/drivers/char/msm_smd_pkt.c
index 8eca55deb3a..ba82a06d968 100644
--- a/drivers/char/msm_smd_pkt.c
+++ b/drivers/char/msm_smd_pkt.c
@@ -182,7 +182,7 @@ static int smd_pkt_write(struct file *file, const char __user *buf,
if (count > MAX_BUF_SIZE)
return -EINVAL;
- DBG("writting %d bytes\n", count);
+ DBG("writing %d bytes\n", count);
smd_pkt_devp = file->private_data;
if (!smd_pkt_devp || !smd_pkt_devp->ch)
diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c
index e1f60f968fd..f1d7fa45c27 100644
--- a/drivers/char/mspec.c
+++ b/drivers/char/mspec.c
@@ -267,7 +267,7 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma,
if ((vma->vm_flags & VM_WRITE) == 0)
return -EPERM;
- pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ pages = vma_pages(vma);
vdata_size = sizeof(struct vma_data) + pages * sizeof(long);
if (vdata_size <= PAGE_SIZE)
vdata = kzalloc(vdata_size, GFP_KERNEL);
diff --git a/drivers/char/mwave/3780i.c b/drivers/char/mwave/3780i.c
index 881c9e59593..28740046bc8 100644
--- a/drivers/char/mwave/3780i.c
+++ b/drivers/char/mwave/3780i.c
@@ -50,7 +50,6 @@
#include <linux/unistd.h>
#include <linux/delay.h>
#include <linux/ioport.h>
-#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/sched.h> /* cond_resched() */
diff --git a/drivers/char/mwave/tp3780i.c b/drivers/char/mwave/tp3780i.c
index c6896970806..04e6d6a2799 100644
--- a/drivers/char/mwave/tp3780i.c
+++ b/drivers/char/mwave/tp3780i.c
@@ -479,6 +479,7 @@ int tp3780I_QueryAbilities(THINKPAD_BD_DATA * pBDData, MW_ABILITIES * pAbilities
PRINTK_2(TRACE_TP3780I,
"tp3780i::tp3780I_QueryAbilities entry pBDData %p\n", pBDData);
+ memset(pAbilities, 0, sizeof(*pAbilities));
/* fill out standard constant fields */
pAbilities->instr_per_sec = pBDData->rDspSettings.uIps;
pAbilities->data_size = pBDData->rDspSettings.uDStoreSize;
diff --git a/drivers/char/nsc_gpio.c b/drivers/char/nsc_gpio.c
index 808d44e9a32..b07b119ae57 100644
--- a/drivers/char/nsc_gpio.c
+++ b/drivers/char/nsc_gpio.c
@@ -41,7 +41,7 @@ void nsc_gpio_dump(struct nsc_gpio_ops *amp, unsigned index)
ssize_t nsc_gpio_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
- unsigned m = iminor(file->f_path.dentry->d_inode);
+ unsigned m = iminor(file_inode(file));
struct nsc_gpio_ops *amp = file->private_data;
struct device *dev = amp->dev;
size_t i;
@@ -104,7 +104,7 @@ ssize_t nsc_gpio_write(struct file *file, const char __user *data,
ssize_t nsc_gpio_read(struct file *file, char __user * buf,
size_t len, loff_t * ppos)
{
- unsigned m = iminor(file->f_path.dentry->d_inode);
+ unsigned m = iminor(file_inode(file));
int value;
struct nsc_gpio_ops *amp = file->private_data;
diff --git a/drivers/char/nwbutton.c b/drivers/char/nwbutton.c
index cfdfe493c6a..76c490fa051 100644
--- a/drivers/char/nwbutton.c
+++ b/drivers/char/nwbutton.c
@@ -168,7 +168,10 @@ static irqreturn_t button_handler (int irq, void *dev_id)
static int button_read (struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
- interruptible_sleep_on (&button_wait_queue);
+ DEFINE_WAIT(wait);
+ prepare_to_wait(&button_wait_queue, &wait, TASK_INTERRUPTIBLE);
+ schedule();
+ finish_wait(&button_wait_queue, &wait);
return (copy_to_user (buffer, &button_output_buffer, bcount))
? -EFAULT : bcount;
}
@@ -220,7 +223,7 @@ static int __init nwbutton_init(void)
return -EBUSY;
}
- if (request_irq (IRQ_NETWINDER_BUTTON, button_handler, IRQF_DISABLED,
+ if (request_irq (IRQ_NETWINDER_BUTTON, button_handler, 0,
"nwbutton", NULL)) {
printk (KERN_WARNING "nwbutton: IRQ %d is not free.\n",
IRQ_NETWINDER_BUTTON);
diff --git a/drivers/char/pcmcia/Kconfig b/drivers/char/pcmcia/Kconfig
index 6614416a862..8d3dfb0c8a2 100644
--- a/drivers/char/pcmcia/Kconfig
+++ b/drivers/char/pcmcia/Kconfig
@@ -3,11 +3,11 @@
#
menu "PCMCIA character devices"
- depends on HOTPLUG && PCMCIA!=n
+ depends on PCMCIA!=n
config SYNCLINK_CS
tristate "SyncLink PC Card support"
- depends on PCMCIA
+ depends on PCMCIA && TTY
help
Enable support for the SyncLink PC Card serial adapter, running
asynchronous and HDLC communications up to 512Kbps. The port is
@@ -15,7 +15,7 @@ config SYNCLINK_CS
This driver may be built as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
- The module will be called synclinkmp. If you want to do that, say M
+ The module will be called synclink_cs. If you want to do that, say M
here.
config CARDMAN_4000
@@ -45,7 +45,7 @@ config CARDMAN_4040
config IPWIRELESS
tristate "IPWireless 3G UMTS PCMCIA card support"
- depends on PCMCIA && NETDEVICES
+ depends on PCMCIA && NETDEVICES && TTY
select PPP
help
This is a driver for 3G UMTS PCMCIA card from IPWireless company. In
diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c
index a7584860e9a..c115217c79a 100644
--- a/drivers/char/pcmcia/cm4000_cs.c
+++ b/drivers/char/pcmcia/cm4000_cs.c
@@ -1400,7 +1400,7 @@ static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct cm4000_dev *dev = filp->private_data;
unsigned int iobase = dev->p_dev->resource[0]->start;
- struct inode *inode = filp->f_path.dentry->d_inode;
+ struct inode *inode = file_inode(filp);
struct pcmcia_device *link;
int size;
int rc;
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
index b66eaa04f8c..8320abd1ef1 100644
--- a/drivers/char/pcmcia/synclink_cs.c
+++ b/drivers/char/pcmcia/synclink_cs.c
@@ -102,8 +102,7 @@ static MGSL_PARAMS default_params = {
ASYNC_PARITY_NONE /* unsigned char parity; */
};
-typedef struct
-{
+typedef struct {
int count;
unsigned char status;
char data[1];
@@ -210,7 +209,7 @@ typedef struct _mgslpc_info {
char testing_irq;
unsigned int init_error; /* startup error (DIAGS) */
- char flag_buf[MAX_ASYNC_BUFFER_SIZE];
+ char *flag_buf;
bool drop_rts_on_tx_done;
struct _input_signal_events input_signal_events;
@@ -326,10 +325,10 @@ typedef struct _mgslpc_info {
#define write_reg16(info, reg, val) outw((val), (info)->io_base + (reg))
#define set_reg_bits(info, reg, mask) \
- write_reg(info, (reg), \
+ write_reg(info, (reg), \
(unsigned char) (read_reg(info, (reg)) | (mask)))
#define clear_reg_bits(info, reg, mask) \
- write_reg(info, (reg), \
+ write_reg(info, (reg), \
(unsigned char) (read_reg(info, (reg)) & ~(mask)))
/*
* interrupt enable/disable routines
@@ -356,10 +355,10 @@ static void irq_enable(MGSLPC_INFO *info, unsigned char channel, unsigned short
}
#define port_irq_disable(info, mask) \
- { info->pim_value |= (mask); write_reg(info, PIM, info->pim_value); }
+ { info->pim_value |= (mask); write_reg(info, PIM, info->pim_value); }
#define port_irq_enable(info, mask) \
- { info->pim_value &= ~(mask); write_reg(info, PIM, info->pim_value); }
+ { info->pim_value &= ~(mask); write_reg(info, PIM, info->pim_value); }
static void rx_start(MGSLPC_INFO *info);
static void rx_stop(MGSLPC_INFO *info);
@@ -397,7 +396,7 @@ static int adapter_test(MGSLPC_INFO *info);
static int claim_resources(MGSLPC_INFO *info);
static void release_resources(MGSLPC_INFO *info);
-static void mgslpc_add_device(MGSLPC_INFO *info);
+static int mgslpc_add_device(MGSLPC_INFO *info);
static void mgslpc_remove_device(MGSLPC_INFO *info);
static bool rx_get_frame(MGSLPC_INFO *info, struct tty_struct *tty);
@@ -514,49 +513,56 @@ static const struct tty_port_operations mgslpc_port_ops = {
static int mgslpc_probe(struct pcmcia_device *link)
{
- MGSLPC_INFO *info;
- int ret;
-
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("mgslpc_attach\n");
-
- info = kzalloc(sizeof(MGSLPC_INFO), GFP_KERNEL);
- if (!info) {
- printk("Error can't allocate device instance data\n");
- return -ENOMEM;
- }
-
- info->magic = MGSLPC_MAGIC;
- tty_port_init(&info->port);
- info->port.ops = &mgslpc_port_ops;
- INIT_WORK(&info->task, bh_handler);
- info->max_frame_size = 4096;
- info->port.close_delay = 5*HZ/10;
- info->port.closing_wait = 30*HZ;
- init_waitqueue_head(&info->status_event_wait_q);
- init_waitqueue_head(&info->event_wait_q);
- spin_lock_init(&info->lock);
- spin_lock_init(&info->netlock);
- memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
- info->idle_mode = HDLC_TXIDLE_FLAGS;
- info->imra_value = 0xffff;
- info->imrb_value = 0xffff;
- info->pim_value = 0xff;
-
- info->p_dev = link;
- link->priv = info;
-
- /* Initialize the struct pcmcia_device structure */
-
- ret = mgslpc_config(link);
- if (ret) {
- tty_port_destroy(&info->port);
- return ret;
- }
-
- mgslpc_add_device(info);
-
- return 0;
+ MGSLPC_INFO *info;
+ int ret;
+
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("mgslpc_attach\n");
+
+ info = kzalloc(sizeof(MGSLPC_INFO), GFP_KERNEL);
+ if (!info) {
+ printk("Error can't allocate device instance data\n");
+ return -ENOMEM;
+ }
+
+ info->magic = MGSLPC_MAGIC;
+ tty_port_init(&info->port);
+ info->port.ops = &mgslpc_port_ops;
+ INIT_WORK(&info->task, bh_handler);
+ info->max_frame_size = 4096;
+ info->port.close_delay = 5*HZ/10;
+ info->port.closing_wait = 30*HZ;
+ init_waitqueue_head(&info->status_event_wait_q);
+ init_waitqueue_head(&info->event_wait_q);
+ spin_lock_init(&info->lock);
+ spin_lock_init(&info->netlock);
+ memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
+ info->idle_mode = HDLC_TXIDLE_FLAGS;
+ info->imra_value = 0xffff;
+ info->imrb_value = 0xffff;
+ info->pim_value = 0xff;
+
+ info->p_dev = link;
+ link->priv = info;
+
+ /* Initialize the struct pcmcia_device structure */
+
+ ret = mgslpc_config(link);
+ if (ret != 0)
+ goto failed;
+
+ ret = mgslpc_add_device(info);
+ if (ret != 0)
+ goto failed_release;
+
+ return 0;
+
+failed_release:
+ mgslpc_release((u_long)link);
+failed:
+ tty_port_destroy(&info->port);
+ kfree(info);
+ return ret;
}
/* Card has been inserted.
@@ -569,35 +575,35 @@ static int mgslpc_ioprobe(struct pcmcia_device *p_dev, void *priv_data)
static int mgslpc_config(struct pcmcia_device *link)
{
- MGSLPC_INFO *info = link->priv;
- int ret;
+ MGSLPC_INFO *info = link->priv;
+ int ret;
- if (debug_level >= DEBUG_LEVEL_INFO)
- printk("mgslpc_config(0x%p)\n", link);
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("mgslpc_config(0x%p)\n", link);
- link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+ link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
- ret = pcmcia_loop_config(link, mgslpc_ioprobe, NULL);
- if (ret != 0)
- goto failed;
+ ret = pcmcia_loop_config(link, mgslpc_ioprobe, NULL);
+ if (ret != 0)
+ goto failed;
- link->config_index = 8;
- link->config_regs = PRESENT_OPTION;
+ link->config_index = 8;
+ link->config_regs = PRESENT_OPTION;
- ret = pcmcia_request_irq(link, mgslpc_isr);
- if (ret)
- goto failed;
- ret = pcmcia_enable_device(link);
- if (ret)
- goto failed;
+ ret = pcmcia_request_irq(link, mgslpc_isr);
+ if (ret)
+ goto failed;
+ ret = pcmcia_enable_device(link);
+ if (ret)
+ goto failed;
- info->io_base = link->resource[0]->start;
- info->irq_level = link->irq;
- return 0;
+ info->io_base = link->resource[0]->start;
+ info->irq_level = link->irq;
+ return 0;
failed:
- mgslpc_release((u_long)link);
- return -ENODEV;
+ mgslpc_release((u_long)link);
+ return -ENODEV;
}
/* Card has been removed.
@@ -703,12 +709,12 @@ static void tx_pause(struct tty_struct *tty)
if (mgslpc_paranoia_check(info, tty->name, "tx_pause"))
return;
if (debug_level >= DEBUG_LEVEL_INFO)
- printk("tx_pause(%s)\n",info->device_name);
+ printk("tx_pause(%s)\n", info->device_name);
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
if (info->tx_enabled)
- tx_stop(info);
- spin_unlock_irqrestore(&info->lock,flags);
+ tx_stop(info);
+ spin_unlock_irqrestore(&info->lock, flags);
}
static void tx_release(struct tty_struct *tty)
@@ -719,12 +725,12 @@ static void tx_release(struct tty_struct *tty)
if (mgslpc_paranoia_check(info, tty->name, "tx_release"))
return;
if (debug_level >= DEBUG_LEVEL_INFO)
- printk("tx_release(%s)\n",info->device_name);
+ printk("tx_release(%s)\n", info->device_name);
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
if (!info->tx_enabled)
- tx_start(info, tty);
- spin_unlock_irqrestore(&info->lock,flags);
+ tx_start(info, tty);
+ spin_unlock_irqrestore(&info->lock, flags);
}
/* Return next bottom half action to perform.
@@ -735,7 +741,7 @@ static int bh_action(MGSLPC_INFO *info)
unsigned long flags;
int rc = 0;
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
if (info->pending_bh & BH_RECEIVE) {
info->pending_bh &= ~BH_RECEIVE;
@@ -754,7 +760,7 @@ static int bh_action(MGSLPC_INFO *info)
info->bh_requested = false;
}
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
return rc;
}
@@ -765,11 +771,8 @@ static void bh_handler(struct work_struct *work)
struct tty_struct *tty;
int action;
- if (!info)
- return;
-
if (debug_level >= DEBUG_LEVEL_BH)
- printk( "%s(%d):bh_handler(%s) entry\n",
+ printk("%s(%d):bh_handler(%s) entry\n",
__FILE__,__LINE__,info->device_name);
info->bh_running = true;
@@ -778,8 +781,8 @@ static void bh_handler(struct work_struct *work)
while((action = bh_action(info)) != 0) {
/* Process work item */
- if ( debug_level >= DEBUG_LEVEL_BH )
- printk( "%s(%d):bh_handler() work item action=%d\n",
+ if (debug_level >= DEBUG_LEVEL_BH)
+ printk("%s(%d):bh_handler() work item action=%d\n",
__FILE__,__LINE__,action);
switch (action) {
@@ -802,7 +805,7 @@ static void bh_handler(struct work_struct *work)
tty_kref_put(tty);
if (debug_level >= DEBUG_LEVEL_BH)
- printk( "%s(%d):bh_handler(%s) exit\n",
+ printk("%s(%d):bh_handler(%s) exit\n",
__FILE__,__LINE__,info->device_name);
}
@@ -831,7 +834,7 @@ static void rx_ready_hdlc(MGSLPC_INFO *info, int eom)
RXBUF *buf = (RXBUF*)(info->rx_buf + (info->rx_put * info->rx_buf_size));
if (debug_level >= DEBUG_LEVEL_ISR)
- printk("%s(%d):rx_ready_hdlc(eom=%d)\n",__FILE__,__LINE__,eom);
+ printk("%s(%d):rx_ready_hdlc(eom=%d)\n", __FILE__, __LINE__, eom);
if (!info->rx_enabled)
return;
@@ -847,7 +850,8 @@ static void rx_ready_hdlc(MGSLPC_INFO *info, int eom)
if (eom) {
/* end of frame, get FIFO count from RBCL register */
- if (!(fifo_count = (unsigned char)(read_reg(info, CHA+RBCL) & 0x1f)))
+ fifo_count = (unsigned char)(read_reg(info, CHA+RBCL) & 0x1f);
+ if (fifo_count == 0)
fifo_count = 32;
} else
fifo_count = 32;
@@ -886,20 +890,13 @@ static void rx_ready_hdlc(MGSLPC_INFO *info, int eom)
issue_command(info, CHA, CMD_RXFIFO);
}
-static void rx_ready_async(MGSLPC_INFO *info, int tcd, struct tty_struct *tty)
+static void rx_ready_async(MGSLPC_INFO *info, int tcd)
{
+ struct tty_port *port = &info->port;
unsigned char data, status, flag;
int fifo_count;
int work = 0;
- struct mgsl_icount *icount = &info->icount;
-
- if (!tty) {
- /* tty is not available anymore */
- issue_command(info, CHA, CMD_RXRESET);
- if (debug_level >= DEBUG_LEVEL_ISR)
- printk("%s(%d):rx_ready_async(tty=NULL)\n",__FILE__,__LINE__);
- return;
- }
+ struct mgsl_icount *icount = &info->icount;
if (tcd) {
/* early termination, get FIFO count from RBCL register */
@@ -913,7 +910,7 @@ static void rx_ready_async(MGSLPC_INFO *info, int tcd, struct tty_struct *tty)
} else
fifo_count = 32;
- tty_buffer_request_room(tty, fifo_count);
+ tty_buffer_request_room(port, fifo_count);
/* Flush received async data to receive data buffer. */
while (fifo_count) {
data = read_reg(info, CHA + RXFIFO);
@@ -944,7 +941,7 @@ static void rx_ready_async(MGSLPC_INFO *info, int tcd, struct tty_struct *tty)
else if (status & BIT6)
flag = TTY_FRAME;
}
- work += tty_insert_flip_char(tty, data, flag);
+ work += tty_insert_flip_char(port, data, flag);
}
issue_command(info, CHA, CMD_RXFIFO);
@@ -957,7 +954,7 @@ static void rx_ready_async(MGSLPC_INFO *info, int tcd, struct tty_struct *tty)
}
if (work)
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(port);
}
@@ -1004,7 +1001,7 @@ static void tx_ready(MGSLPC_INFO *info, struct tty_struct *tty)
int c;
if (debug_level >= DEBUG_LEVEL_ISR)
- printk("%s(%d):tx_ready(%s)\n", __FILE__,__LINE__,info->device_name);
+ printk("%s(%d):tx_ready(%s)\n", __FILE__, __LINE__, info->device_name);
if (info->params.mode == MGSL_MODE_HDLC) {
if (!info->tx_active)
@@ -1185,14 +1182,14 @@ static irqreturn_t mgslpc_isr(int dummy, void *dev_id)
}
count++;
- if (gis & (BIT1 + BIT0)) {
+ if (gis & (BIT1 | BIT0)) {
isr = read_reg16(info, CHB + ISR);
if (isr & IRQ_DCD)
dcd_change(info, tty);
if (isr & IRQ_CTS)
cts_change(info, tty);
}
- if (gis & (BIT3 + BIT2))
+ if (gis & (BIT3 | BIT2))
{
isr = read_reg16(info, CHA + ISR);
if (isr & IRQ_TIMER) {
@@ -1213,11 +1210,11 @@ static irqreturn_t mgslpc_isr(int dummy, void *dev_id)
if (isr & IRQ_RXTIME) {
issue_command(info, CHA, CMD_RXFIFO_READ);
}
- if (isr & (IRQ_RXEOM + IRQ_RXFIFO)) {
+ if (isr & (IRQ_RXEOM | IRQ_RXFIFO)) {
if (info->params.mode == MGSL_MODE_HDLC)
rx_ready_hdlc(info, isr & IRQ_RXEOM);
else
- rx_ready_async(info, isr & IRQ_RXEOM, tty);
+ rx_ready_async(info, isr & IRQ_RXEOM);
}
/* transmit IRQs */
@@ -1249,7 +1246,7 @@ static irqreturn_t mgslpc_isr(int dummy, void *dev_id)
*/
if (info->pending_bh && !info->bh_running && !info->bh_requested) {
- if ( debug_level >= DEBUG_LEVEL_ISR )
+ if (debug_level >= DEBUG_LEVEL_ISR)
printk("%s(%d):%s queueing bh task.\n",
__FILE__,__LINE__,info->device_name);
schedule_work(&info->task);
@@ -1273,7 +1270,7 @@ static int startup(MGSLPC_INFO * info, struct tty_struct *tty)
int retval = 0;
if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):startup(%s)\n",__FILE__,__LINE__,info->device_name);
+ printk("%s(%d):startup(%s)\n", __FILE__, __LINE__, info->device_name);
if (info->port.flags & ASYNC_INITIALIZED)
return 0;
@@ -1283,7 +1280,7 @@ static int startup(MGSLPC_INFO * info, struct tty_struct *tty)
info->tx_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL);
if (!info->tx_buf) {
printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n",
- __FILE__,__LINE__,info->device_name);
+ __FILE__, __LINE__, info->device_name);
return -ENOMEM;
}
}
@@ -1298,15 +1295,15 @@ static int startup(MGSLPC_INFO * info, struct tty_struct *tty)
retval = claim_resources(info);
/* perform existence check and diagnostics */
- if ( !retval )
+ if (!retval)
retval = adapter_test(info);
- if ( retval ) {
- if (capable(CAP_SYS_ADMIN) && tty)
+ if (retval) {
+ if (capable(CAP_SYS_ADMIN) && tty)
set_bit(TTY_IO_ERROR, &tty->flags);
release_resources(info);
- return retval;
- }
+ return retval;
+ }
/* program hardware for current parameters */
mgslpc_change_params(info, tty);
@@ -1330,7 +1327,7 @@ static void shutdown(MGSLPC_INFO * info, struct tty_struct *tty)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_shutdown(%s)\n",
- __FILE__,__LINE__, info->device_name );
+ __FILE__, __LINE__, info->device_name);
/* clear status wait queue because status changes */
/* can't happen after shutting down the hardware */
@@ -1344,7 +1341,7 @@ static void shutdown(MGSLPC_INFO * info, struct tty_struct *tty)
info->tx_buf = NULL;
}
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
rx_stop(info);
tx_stop(info);
@@ -1352,12 +1349,12 @@ static void shutdown(MGSLPC_INFO * info, struct tty_struct *tty)
/* TODO:disable interrupts instead of reset to preserve signal states */
reset_device(info);
- if (!tty || tty->termios.c_cflag & HUPCL) {
- info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
+ if (!tty || tty->termios.c_cflag & HUPCL) {
+ info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
set_signals(info);
}
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
release_resources(info);
@@ -1371,7 +1368,7 @@ static void mgslpc_program_hw(MGSLPC_INFO *info, struct tty_struct *tty)
{
unsigned long flags;
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
rx_stop(info);
tx_stop(info);
@@ -1396,7 +1393,7 @@ static void mgslpc_program_hw(MGSLPC_INFO *info, struct tty_struct *tty)
if (info->netcount || (tty && (tty->termios.c_cflag & CREAD)))
rx_start(info);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
}
/* Reconfigure adapter based on new parameters
@@ -1411,16 +1408,16 @@ static void mgslpc_change_params(MGSLPC_INFO *info, struct tty_struct *tty)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_change_params(%s)\n",
- __FILE__,__LINE__, info->device_name );
+ __FILE__, __LINE__, info->device_name);
cflag = tty->termios.c_cflag;
- /* if B0 rate (hangup) specified then negate DTR and RTS */
- /* otherwise assert DTR and RTS */
- if (cflag & CBAUD)
- info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+ /* if B0 rate (hangup) specified then negate RTS and DTR */
+ /* otherwise assert RTS and DTR */
+ if (cflag & CBAUD)
+ info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR;
else
- info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+ info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
/* byte size and parity */
@@ -1463,7 +1460,7 @@ static void mgslpc_change_params(MGSLPC_INFO *info, struct tty_struct *tty)
info->params.data_rate = tty_get_baud_rate(tty);
}
- if ( info->params.data_rate ) {
+ if (info->params.data_rate) {
info->timeout = (32*HZ*bits_per_char) /
info->params.data_rate;
}
@@ -1498,8 +1495,8 @@ static int mgslpc_put_char(struct tty_struct *tty, unsigned char ch)
unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO) {
- printk( "%s(%d):mgslpc_put_char(%d) on %s\n",
- __FILE__,__LINE__,ch,info->device_name);
+ printk("%s(%d):mgslpc_put_char(%d) on %s\n",
+ __FILE__, __LINE__, ch, info->device_name);
}
if (mgslpc_paranoia_check(info, tty->name, "mgslpc_put_char"))
@@ -1508,7 +1505,7 @@ static int mgslpc_put_char(struct tty_struct *tty, unsigned char ch)
if (!info->tx_buf)
return 0;
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
if (info->params.mode == MGSL_MODE_ASYNC || !info->tx_active) {
if (info->tx_count < TXBUFSIZE - 1) {
@@ -1518,7 +1515,7 @@ static int mgslpc_put_char(struct tty_struct *tty, unsigned char ch)
}
}
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
return 1;
}
@@ -1531,8 +1528,8 @@ static void mgslpc_flush_chars(struct tty_struct *tty)
unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO)
- printk( "%s(%d):mgslpc_flush_chars() entry on %s tx_count=%d\n",
- __FILE__,__LINE__,info->device_name,info->tx_count);
+ printk("%s(%d):mgslpc_flush_chars() entry on %s tx_count=%d\n",
+ __FILE__, __LINE__, info->device_name, info->tx_count);
if (mgslpc_paranoia_check(info, tty->name, "mgslpc_flush_chars"))
return;
@@ -1542,13 +1539,13 @@ static void mgslpc_flush_chars(struct tty_struct *tty)
return;
if (debug_level >= DEBUG_LEVEL_INFO)
- printk( "%s(%d):mgslpc_flush_chars() entry on %s starting transmitter\n",
- __FILE__,__LINE__,info->device_name);
+ printk("%s(%d):mgslpc_flush_chars() entry on %s starting transmitter\n",
+ __FILE__, __LINE__, info->device_name);
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
if (!info->tx_active)
- tx_start(info, tty);
- spin_unlock_irqrestore(&info->lock,flags);
+ tx_start(info, tty);
+ spin_unlock_irqrestore(&info->lock, flags);
}
/* Send a block of data
@@ -1569,8 +1566,8 @@ static int mgslpc_write(struct tty_struct * tty,
unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO)
- printk( "%s(%d):mgslpc_write(%s) count=%d\n",
- __FILE__,__LINE__,info->device_name,count);
+ printk("%s(%d):mgslpc_write(%s) count=%d\n",
+ __FILE__, __LINE__, info->device_name, count);
if (mgslpc_paranoia_check(info, tty->name, "mgslpc_write") ||
!info->tx_buf)
@@ -1596,26 +1593,26 @@ static int mgslpc_write(struct tty_struct * tty,
memcpy(info->tx_buf + info->tx_put, buf, c);
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
info->tx_put = (info->tx_put + c) & (TXBUFSIZE-1);
info->tx_count += c;
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
buf += c;
count -= c;
ret += c;
}
start:
- if (info->tx_count && !tty->stopped && !tty->hw_stopped) {
- spin_lock_irqsave(&info->lock,flags);
+ if (info->tx_count && !tty->stopped && !tty->hw_stopped) {
+ spin_lock_irqsave(&info->lock, flags);
if (!info->tx_active)
- tx_start(info, tty);
- spin_unlock_irqrestore(&info->lock,flags);
- }
+ tx_start(info, tty);
+ spin_unlock_irqrestore(&info->lock, flags);
+ }
cleanup:
if (debug_level >= DEBUG_LEVEL_INFO)
- printk( "%s(%d):mgslpc_write(%s) returning=%d\n",
- __FILE__,__LINE__,info->device_name,ret);
+ printk("%s(%d):mgslpc_write(%s) returning=%d\n",
+ __FILE__, __LINE__, info->device_name, ret);
return ret;
}
@@ -1643,7 +1640,7 @@ static int mgslpc_write_room(struct tty_struct *tty)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_write_room(%s)=%d\n",
- __FILE__,__LINE__, info->device_name, ret);
+ __FILE__, __LINE__, info->device_name, ret);
return ret;
}
@@ -1656,7 +1653,7 @@ static int mgslpc_chars_in_buffer(struct tty_struct *tty)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_chars_in_buffer(%s)\n",
- __FILE__,__LINE__, info->device_name );
+ __FILE__, __LINE__, info->device_name);
if (mgslpc_paranoia_check(info, tty->name, "mgslpc_chars_in_buffer"))
return 0;
@@ -1668,7 +1665,7 @@ static int mgslpc_chars_in_buffer(struct tty_struct *tty)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_chars_in_buffer(%s)=%d\n",
- __FILE__,__LINE__, info->device_name, rc);
+ __FILE__, __LINE__, info->device_name, rc);
return rc;
}
@@ -1682,15 +1679,15 @@ static void mgslpc_flush_buffer(struct tty_struct *tty)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_flush_buffer(%s) entry\n",
- __FILE__,__LINE__, info->device_name );
+ __FILE__, __LINE__, info->device_name);
if (mgslpc_paranoia_check(info, tty->name, "mgslpc_flush_buffer"))
return;
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
info->tx_count = info->tx_put = info->tx_get = 0;
del_timer(&info->tx_timer);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
wake_up_interruptible(&tty->write_wait);
tty_wakeup(tty);
@@ -1705,17 +1702,17 @@ static void mgslpc_send_xchar(struct tty_struct *tty, char ch)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_send_xchar(%s,%d)\n",
- __FILE__,__LINE__, info->device_name, ch );
+ __FILE__, __LINE__, info->device_name, ch);
if (mgslpc_paranoia_check(info, tty->name, "mgslpc_send_xchar"))
return;
info->x_char = ch;
if (ch) {
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
if (!info->tx_enabled)
- tx_start(info, tty);
- spin_unlock_irqrestore(&info->lock,flags);
+ tx_start(info, tty);
+ spin_unlock_irqrestore(&info->lock, flags);
}
}
@@ -1728,7 +1725,7 @@ static void mgslpc_throttle(struct tty_struct * tty)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_throttle(%s) entry\n",
- __FILE__,__LINE__, info->device_name );
+ __FILE__, __LINE__, info->device_name);
if (mgslpc_paranoia_check(info, tty->name, "mgslpc_throttle"))
return;
@@ -1736,11 +1733,11 @@ static void mgslpc_throttle(struct tty_struct * tty)
if (I_IXOFF(tty))
mgslpc_send_xchar(tty, STOP_CHAR(tty));
- if (tty->termios.c_cflag & CRTSCTS) {
- spin_lock_irqsave(&info->lock,flags);
+ if (tty->termios.c_cflag & CRTSCTS) {
+ spin_lock_irqsave(&info->lock, flags);
info->serial_signals &= ~SerialSignal_RTS;
- set_signals(info);
- spin_unlock_irqrestore(&info->lock,flags);
+ set_signals(info);
+ spin_unlock_irqrestore(&info->lock, flags);
}
}
@@ -1753,7 +1750,7 @@ static void mgslpc_unthrottle(struct tty_struct * tty)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_unthrottle(%s) entry\n",
- __FILE__,__LINE__, info->device_name );
+ __FILE__, __LINE__, info->device_name);
if (mgslpc_paranoia_check(info, tty->name, "mgslpc_unthrottle"))
return;
@@ -1765,11 +1762,11 @@ static void mgslpc_unthrottle(struct tty_struct * tty)
mgslpc_send_xchar(tty, START_CHAR(tty));
}
- if (tty->termios.c_cflag & CRTSCTS) {
- spin_lock_irqsave(&info->lock,flags);
+ if (tty->termios.c_cflag & CRTSCTS) {
+ spin_lock_irqsave(&info->lock, flags);
info->serial_signals |= SerialSignal_RTS;
- set_signals(info);
- spin_unlock_irqrestore(&info->lock,flags);
+ set_signals(info);
+ spin_unlock_irqrestore(&info->lock, flags);
}
}
@@ -1807,33 +1804,33 @@ static int get_params(MGSLPC_INFO * info, MGSL_PARAMS __user *user_params)
*
* Arguments:
*
- * info pointer to device instance data
- * new_params user buffer containing new serial params
+ * info pointer to device instance data
+ * new_params user buffer containing new serial params
*
* Returns: 0 if success, otherwise error code
*/
static int set_params(MGSLPC_INFO * info, MGSL_PARAMS __user *new_params, struct tty_struct *tty)
{
- unsigned long flags;
+ unsigned long flags;
MGSL_PARAMS tmp_params;
int err;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):set_params %s\n", __FILE__,__LINE__,
- info->device_name );
+ info->device_name);
COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS));
if (err) {
- if ( debug_level >= DEBUG_LEVEL_INFO )
- printk( "%s(%d):set_params(%s) user buffer copy failed\n",
- __FILE__,__LINE__,info->device_name);
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("%s(%d):set_params(%s) user buffer copy failed\n",
+ __FILE__, __LINE__, info->device_name);
return -EFAULT;
}
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
- mgslpc_change_params(info, tty);
+ mgslpc_change_params(info, tty);
return 0;
}
@@ -1851,13 +1848,13 @@ static int get_txidle(MGSLPC_INFO * info, int __user *idle_mode)
static int set_txidle(MGSLPC_INFO * info, int idle_mode)
{
- unsigned long flags;
+ unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("set_txidle(%s,%d)\n", info->device_name, idle_mode);
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
info->idle_mode = idle_mode;
tx_set_idle(info);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
return 0;
}
@@ -1874,11 +1871,11 @@ static int get_interface(MGSLPC_INFO * info, int __user *if_mode)
static int set_interface(MGSLPC_INFO * info, int if_mode)
{
- unsigned long flags;
+ unsigned long flags;
unsigned char val;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("set_interface(%s,%d)\n", info->device_name, if_mode);
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
info->if_mode = if_mode;
val = read_reg(info, PVR) & 0x0f;
@@ -1890,18 +1887,18 @@ static int set_interface(MGSLPC_INFO * info, int if_mode)
}
write_reg(info, PVR, val);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
return 0;
}
static int set_txenable(MGSLPC_INFO * info, int enable, struct tty_struct *tty)
{
- unsigned long flags;
+ unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("set_txenable(%s,%d)\n", info->device_name, enable);
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
if (enable) {
if (!info->tx_enabled)
tx_start(info, tty);
@@ -1909,18 +1906,18 @@ static int set_txenable(MGSLPC_INFO * info, int enable, struct tty_struct *tty)
if (info->tx_enabled)
tx_stop(info);
}
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
return 0;
}
static int tx_abort(MGSLPC_INFO * info)
{
- unsigned long flags;
+ unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("tx_abort(%s)\n", info->device_name);
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
if (info->tx_active && info->tx_count &&
info->params.mode == MGSL_MODE_HDLC) {
/* clear data count so FIFO is not filled on next IRQ.
@@ -1929,18 +1926,18 @@ static int tx_abort(MGSLPC_INFO * info)
info->tx_count = info->tx_put = info->tx_get = 0;
info->tx_aborting = true;
}
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
return 0;
}
static int set_rxenable(MGSLPC_INFO * info, int enable)
{
- unsigned long flags;
+ unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("set_rxenable(%s,%d)\n", info->device_name, enable);
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
if (enable) {
if (!info->rx_enabled)
rx_start(info);
@@ -1948,21 +1945,21 @@ static int set_rxenable(MGSLPC_INFO * info, int enable)
if (info->rx_enabled)
rx_stop(info);
}
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
return 0;
}
/* wait for specified event to occur
*
- * Arguments: info pointer to device instance data
- * mask pointer to bitmask of events to wait for
- * Return Value: 0 if successful and bit mask updated with
+ * Arguments: info pointer to device instance data
+ * mask pointer to bitmask of events to wait for
+ * Return Value: 0 if successful and bit mask updated with
* of events triggerred,
- * otherwise error code
+ * otherwise error code
*/
static int wait_events(MGSLPC_INFO * info, int __user *mask_ptr)
{
- unsigned long flags;
+ unsigned long flags;
int s;
int rc=0;
struct mgsl_icount cprev, cnow;
@@ -1978,18 +1975,18 @@ static int wait_events(MGSLPC_INFO * info, int __user *mask_ptr)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("wait_events(%s,%d)\n", info->device_name, mask);
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
/* return immediately if state matches requested events */
get_signals(info);
s = info->serial_signals;
events = mask &
( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) +
- ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) +
+ ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) +
((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) +
((s & SerialSignal_RI) ? MgslEvent_RiActive :MgslEvent_RiInactive) );
if (events) {
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
goto exit;
}
@@ -2004,7 +2001,7 @@ static int wait_events(MGSLPC_INFO * info, int __user *mask_ptr)
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&info->event_wait_q, &wait);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
for(;;) {
@@ -2015,11 +2012,11 @@ static int wait_events(MGSLPC_INFO * info, int __user *mask_ptr)
}
/* get current irq counts */
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
cnow = info->icount;
newsigs = info->input_signal_events;
set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
/* if no change, wait aborted for some reason */
if (newsigs.dsr_up == oldsigs.dsr_up &&
@@ -2058,10 +2055,10 @@ static int wait_events(MGSLPC_INFO * info, int __user *mask_ptr)
set_current_state(TASK_RUNNING);
if (mask & MgslEvent_ExitHuntMode) {
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
if (!waitqueue_active(&info->event_wait_q))
irq_disable(info, CHA, IRQ_EXITHUNT);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
}
exit:
if (rc == 0)
@@ -2071,17 +2068,17 @@ exit:
static int modem_input_wait(MGSLPC_INFO *info,int arg)
{
- unsigned long flags;
+ unsigned long flags;
int rc;
struct mgsl_icount cprev, cnow;
DECLARE_WAITQUEUE(wait, current);
/* save current irq counts */
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
cprev = info->icount;
add_wait_queue(&info->status_event_wait_q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
for(;;) {
schedule();
@@ -2091,10 +2088,10 @@ static int modem_input_wait(MGSLPC_INFO *info,int arg)
}
/* get new irq counts */
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
cnow = info->icount;
set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
/* if no change, wait aborted for some reason */
if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
@@ -2125,11 +2122,11 @@ static int tiocmget(struct tty_struct *tty)
{
MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
unsigned int result;
- unsigned long flags;
+ unsigned long flags;
- spin_lock_irqsave(&info->lock,flags);
- get_signals(info);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
+ get_signals(info);
+ spin_unlock_irqrestore(&info->lock, flags);
result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS:0) +
((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR:0) +
@@ -2140,7 +2137,7 @@ static int tiocmget(struct tty_struct *tty)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):%s tiocmget() value=%08X\n",
- __FILE__,__LINE__, info->device_name, result );
+ __FILE__, __LINE__, info->device_name, result);
return result;
}
@@ -2150,11 +2147,11 @@ static int tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
- unsigned long flags;
+ unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):%s tiocmset(%x,%x)\n",
- __FILE__,__LINE__,info->device_name, set, clear);
+ __FILE__, __LINE__, info->device_name, set, clear);
if (set & TIOCM_RTS)
info->serial_signals |= SerialSignal_RTS;
@@ -2165,9 +2162,9 @@ static int tiocmset(struct tty_struct *tty,
if (clear & TIOCM_DTR)
info->serial_signals &= ~SerialSignal_DTR;
- spin_lock_irqsave(&info->lock,flags);
- set_signals(info);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
+ set_signals(info);
+ spin_unlock_irqrestore(&info->lock, flags);
return 0;
}
@@ -2184,17 +2181,17 @@ static int mgslpc_break(struct tty_struct *tty, int break_state)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_break(%s,%d)\n",
- __FILE__,__LINE__, info->device_name, break_state);
+ __FILE__, __LINE__, info->device_name, break_state);
if (mgslpc_paranoia_check(info, tty->name, "mgslpc_break"))
return -EINVAL;
- spin_lock_irqsave(&info->lock,flags);
- if (break_state == -1)
+ spin_lock_irqsave(&info->lock, flags);
+ if (break_state == -1)
set_reg_bits(info, CHA+DAFO, BIT6);
else
clear_reg_bits(info, CHA+DAFO, BIT6);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
return 0;
}
@@ -2205,9 +2202,9 @@ static int mgslpc_get_icount(struct tty_struct *tty,
struct mgsl_icount cnow; /* kernel counter temps */
unsigned long flags;
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
cnow = info->icount;
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
icount->cts = cnow.cts;
icount->dsr = cnow.dsr;
@@ -2228,9 +2225,9 @@ static int mgslpc_get_icount(struct tty_struct *tty,
*
* Arguments:
*
- * tty pointer to tty instance data
- * cmd IOCTL command code
- * arg command argument/context
+ * tty pointer to tty instance data
+ * cmd IOCTL command code
+ * arg command argument/context
*
* Return Value: 0 if success, otherwise error code
*/
@@ -2241,8 +2238,8 @@ static int mgslpc_ioctl(struct tty_struct *tty,
void __user *argp = (void __user *)arg;
if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_ioctl %s cmd=%08X\n", __FILE__,__LINE__,
- info->device_name, cmd );
+ printk("%s(%d):mgslpc_ioctl %s cmd=%08X\n", __FILE__, __LINE__,
+ info->device_name, cmd);
if (mgslpc_paranoia_check(info, tty->name, "mgslpc_ioctl"))
return -ENODEV;
@@ -2288,8 +2285,8 @@ static int mgslpc_ioctl(struct tty_struct *tty,
*
* Arguments:
*
- * tty pointer to tty structure
- * termios pointer to buffer to hold returned old termios
+ * tty pointer to tty structure
+ * termios pointer to buffer to hold returned old termios
*/
static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
{
@@ -2297,8 +2294,8 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term
unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_set_termios %s\n", __FILE__,__LINE__,
- tty->driver->name );
+ printk("%s(%d):mgslpc_set_termios %s\n", __FILE__, __LINE__,
+ tty->driver->name);
/* just return if nothing has changed */
if ((tty->termios.c_cflag == old_termios->c_cflag)
@@ -2311,23 +2308,23 @@ static void mgslpc_set_termios(struct tty_struct *tty, struct ktermios *old_term
/* Handle transition to B0 status */
if (old_termios->c_cflag & CBAUD &&
!(tty->termios.c_cflag & CBAUD)) {
- info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
- spin_lock_irqsave(&info->lock,flags);
- set_signals(info);
- spin_unlock_irqrestore(&info->lock,flags);
+ info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
+ spin_lock_irqsave(&info->lock, flags);
+ set_signals(info);
+ spin_unlock_irqrestore(&info->lock, flags);
}
/* Handle transition away from B0 status */
if (!(old_termios->c_cflag & CBAUD) &&
tty->termios.c_cflag & CBAUD) {
info->serial_signals |= SerialSignal_DTR;
- if (!(tty->termios.c_cflag & CRTSCTS) ||
- !test_bit(TTY_THROTTLED, &tty->flags)) {
+ if (!(tty->termios.c_cflag & CRTSCTS) ||
+ !test_bit(TTY_THROTTLED, &tty->flags)) {
info->serial_signals |= SerialSignal_RTS;
- }
- spin_lock_irqsave(&info->lock,flags);
- set_signals(info);
- spin_unlock_irqrestore(&info->lock,flags);
+ }
+ spin_lock_irqsave(&info->lock, flags);
+ set_signals(info);
+ spin_unlock_irqrestore(&info->lock, flags);
}
/* Handle turning off CRTSCTS */
@@ -2348,15 +2345,15 @@ static void mgslpc_close(struct tty_struct *tty, struct file * filp)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_close(%s) entry, count=%d\n",
- __FILE__,__LINE__, info->device_name, port->count);
+ __FILE__, __LINE__, info->device_name, port->count);
WARN_ON(!port->count);
if (tty_port_close_start(port, tty, filp) == 0)
goto cleanup;
- if (port->flags & ASYNC_INITIALIZED)
- mgslpc_wait_until_sent(tty, info->timeout);
+ if (port->flags & ASYNC_INITIALIZED)
+ mgslpc_wait_until_sent(tty, info->timeout);
mgslpc_flush_buffer(tty);
@@ -2367,7 +2364,7 @@ static void mgslpc_close(struct tty_struct *tty, struct file * filp)
tty_port_tty_set(port, NULL);
cleanup:
if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s(%d):mgslpc_close(%s) exit, count=%d\n", __FILE__,__LINE__,
+ printk("%s(%d):mgslpc_close(%s) exit, count=%d\n", __FILE__, __LINE__,
tty->driver->name, port->count);
}
@@ -2378,12 +2375,12 @@ static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout)
MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data;
unsigned long orig_jiffies, char_time;
- if (!info )
+ if (!info)
return;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_wait_until_sent(%s) entry\n",
- __FILE__,__LINE__, info->device_name );
+ __FILE__, __LINE__, info->device_name);
if (mgslpc_paranoia_check(info, tty->name, "mgslpc_wait_until_sent"))
return;
@@ -2399,8 +2396,8 @@ static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout)
* Note: use tight timings here to satisfy the NIST-PCTS.
*/
- if ( info->params.data_rate ) {
- char_time = info->timeout/(32 * 5);
+ if (info->params.data_rate) {
+ char_time = info->timeout/(32 * 5);
if (!char_time)
char_time++;
} else
@@ -2431,7 +2428,7 @@ static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout)
exit:
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_wait_until_sent(%s) exit\n",
- __FILE__,__LINE__, info->device_name );
+ __FILE__, __LINE__, info->device_name);
}
/* Called by tty_hangup() when a hangup is signaled.
@@ -2443,7 +2440,7 @@ static void mgslpc_hangup(struct tty_struct *tty)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_hangup(%s)\n",
- __FILE__,__LINE__, info->device_name );
+ __FILE__, __LINE__, info->device_name);
if (mgslpc_paranoia_check(info, tty->name, "mgslpc_hangup"))
return;
@@ -2458,9 +2455,9 @@ static int carrier_raised(struct tty_port *port)
MGSLPC_INFO *info = container_of(port, MGSLPC_INFO, port);
unsigned long flags;
- spin_lock_irqsave(&info->lock,flags);
- get_signals(info);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
+ get_signals(info);
+ spin_unlock_irqrestore(&info->lock, flags);
if (info->serial_signals & SerialSignal_DCD)
return 1;
@@ -2472,13 +2469,13 @@ static void dtr_rts(struct tty_port *port, int onoff)
MGSLPC_INFO *info = container_of(port, MGSLPC_INFO, port);
unsigned long flags;
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
if (onoff)
- info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+ info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR;
else
- info->serial_signals &= ~SerialSignal_RTS + SerialSignal_DTR;
+ info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
set_signals(info);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
}
@@ -2486,14 +2483,14 @@ static int mgslpc_open(struct tty_struct *tty, struct file * filp)
{
MGSLPC_INFO *info;
struct tty_port *port;
- int retval, line;
- unsigned long flags;
+ int retval, line;
+ unsigned long flags;
/* verify range of specified line number */
line = tty->index;
if (line >= mgslpc_device_count) {
printk("%s(%d):mgslpc_open with invalid line #%d.\n",
- __FILE__,__LINE__,line);
+ __FILE__, __LINE__, line);
return -ENODEV;
}
@@ -2510,18 +2507,18 @@ static int mgslpc_open(struct tty_struct *tty, struct file * filp)
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_open(%s), old ref count = %d\n",
- __FILE__,__LINE__,tty->driver->name, port->count);
+ __FILE__, __LINE__, tty->driver->name, port->count);
/* If port is closing, signal caller to try again */
if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING){
- if (port->flags & ASYNC_CLOSING)
- interruptible_sleep_on(&port->close_wait);
+ wait_event_interruptible_tty(tty, port->close_wait,
+ !(port->flags & ASYNC_CLOSING));
retval = ((port->flags & ASYNC_HUP_NOTIFY) ?
-EAGAIN : -ERESTARTSYS);
goto cleanup;
}
- tty->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+ port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
spin_lock_irqsave(&info->netlock, flags);
if (info->netcount) {
@@ -2545,13 +2542,13 @@ static int mgslpc_open(struct tty_struct *tty, struct file * filp)
if (retval) {
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):block_til_ready(%s) returned %d\n",
- __FILE__,__LINE__, info->device_name, retval);
+ __FILE__, __LINE__, info->device_name, retval);
goto cleanup;
}
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgslpc_open(%s) success\n",
- __FILE__,__LINE__, info->device_name);
+ __FILE__, __LINE__, info->device_name);
retval = 0;
cleanup:
@@ -2571,9 +2568,9 @@ static inline void line_info(struct seq_file *m, MGSLPC_INFO *info)
info->device_name, info->io_base, info->irq_level);
/* output current serial signal states */
- spin_lock_irqsave(&info->lock,flags);
- get_signals(info);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
+ get_signals(info);
+ spin_unlock_irqrestore(&info->lock, flags);
stat_buf[0] = 0;
stat_buf[1] = 0;
@@ -2635,7 +2632,7 @@ static int mgslpc_proc_show(struct seq_file *m, void *v)
seq_printf(m, "synclink driver:%s\n", driver_version);
info = mgslpc_device_list;
- while( info ) {
+ while (info) {
line_info(m, info);
info = info->next_device;
}
@@ -2674,6 +2671,14 @@ static int rx_alloc_buffers(MGSLPC_INFO *info)
if (info->rx_buf == NULL)
return -ENOMEM;
+ /* unused flag buffer to satisfy receive_buf calling interface */
+ info->flag_buf = kzalloc(info->max_frame_size, GFP_KERNEL);
+ if (!info->flag_buf) {
+ kfree(info->rx_buf);
+ info->rx_buf = NULL;
+ return -ENOMEM;
+ }
+
rx_reset_buffers(info);
return 0;
}
@@ -2682,12 +2687,14 @@ static void rx_free_buffers(MGSLPC_INFO *info)
{
kfree(info->rx_buf);
info->rx_buf = NULL;
+ kfree(info->flag_buf);
+ info->flag_buf = NULL;
}
static int claim_resources(MGSLPC_INFO *info)
{
- if (rx_alloc_buffers(info) < 0 ) {
- printk( "Can't allocate rx buffer %s\n", info->device_name);
+ if (rx_alloc_buffers(info) < 0) {
+ printk("Can't allocate rx buffer %s\n", info->device_name);
release_resources(info);
return -ENODEV;
}
@@ -2706,8 +2713,12 @@ static void release_resources(MGSLPC_INFO *info)
*
* Arguments: info pointer to device instance data
*/
-static void mgslpc_add_device(MGSLPC_INFO *info)
+static int mgslpc_add_device(MGSLPC_INFO *info)
{
+ MGSLPC_INFO *current_dev = NULL;
+ struct device *tty_dev;
+ int ret;
+
info->next_device = NULL;
info->line = mgslpc_device_count;
sprintf(info->device_name,"ttySLP%d",info->line);
@@ -2722,8 +2733,8 @@ static void mgslpc_add_device(MGSLPC_INFO *info)
if (!mgslpc_device_list)
mgslpc_device_list = info;
else {
- MGSLPC_INFO *current_dev = mgslpc_device_list;
- while( current_dev->next_device )
+ current_dev = mgslpc_device_list;
+ while (current_dev->next_device)
current_dev = current_dev->next_device;
current_dev->next_device = info;
}
@@ -2733,14 +2744,34 @@ static void mgslpc_add_device(MGSLPC_INFO *info)
else if (info->max_frame_size > 65535)
info->max_frame_size = 65535;
- printk( "SyncLink PC Card %s:IO=%04X IRQ=%d\n",
+ printk("SyncLink PC Card %s:IO=%04X IRQ=%d\n",
info->device_name, info->io_base, info->irq_level);
#if SYNCLINK_GENERIC_HDLC
- hdlcdev_init(info);
+ ret = hdlcdev_init(info);
+ if (ret != 0)
+ goto failed;
#endif
- tty_port_register_device(&info->port, serial_driver, info->line,
+
+ tty_dev = tty_port_register_device(&info->port, serial_driver, info->line,
&info->p_dev->dev);
+ if (IS_ERR(tty_dev)) {
+ ret = PTR_ERR(tty_dev);
+#if SYNCLINK_GENERIC_HDLC
+ hdlcdev_exit(info);
+#endif
+ goto failed;
+ }
+
+ return 0;
+
+failed:
+ if (current_dev)
+ current_dev->next_device = NULL;
+ else
+ mgslpc_device_list = NULL;
+ mgslpc_device_count--;
+ return ret;
}
static void mgslpc_remove_device(MGSLPC_INFO *remove_info)
@@ -3000,11 +3031,11 @@ static void loopback_enable(MGSLPC_INFO *info)
unsigned char val;
/* CCR1:02..00 CM[2..0] Clock Mode = 111 (clock mode 7) */
- val = read_reg(info, CHA + CCR1) | (BIT2 + BIT1 + BIT0);
+ val = read_reg(info, CHA + CCR1) | (BIT2 | BIT1 | BIT0);
write_reg(info, CHA + CCR1, val);
/* CCR2:04 SSEL Clock source select, 1=submode b */
- val = read_reg(info, CHA + CCR2) | (BIT4 + BIT5);
+ val = read_reg(info, CHA + CCR2) | (BIT4 | BIT5);
write_reg(info, CHA + CCR2, val);
/* set LinkSpeed if available, otherwise default to 2Mbps */
@@ -3094,10 +3125,10 @@ static void hdlc_mode(MGSLPC_INFO *info)
val |= BIT4;
break; // FM0
case HDLC_ENCODING_BIPHASE_MARK:
- val |= BIT4 + BIT2;
+ val |= BIT4 | BIT2;
break; // FM1
case HDLC_ENCODING_BIPHASE_LEVEL:
- val |= BIT4 + BIT3;
+ val |= BIT4 | BIT3;
break; // Manchester
}
write_reg(info, CHA + CCR0, val);
@@ -3154,7 +3185,7 @@ static void hdlc_mode(MGSLPC_INFO *info)
*/
val = 0x00;
if (info->params.crc_type == HDLC_CRC_NONE)
- val |= BIT2 + BIT1;
+ val |= BIT2 | BIT1;
if (info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE)
val |= BIT5;
switch (info->params.preamble_length)
@@ -3166,7 +3197,7 @@ static void hdlc_mode(MGSLPC_INFO *info)
val |= BIT6;
break;
case HDLC_PREAMBLE_LENGTH_64BITS:
- val |= BIT7 + BIT6;
+ val |= BIT7 | BIT6;
break;
}
write_reg(info, CHA + CCR3, val);
@@ -3233,8 +3264,8 @@ static void hdlc_mode(MGSLPC_INFO *info)
clear_reg_bits(info, CHA + PVR, BIT3);
irq_enable(info, CHA,
- IRQ_RXEOM + IRQ_RXFIFO + IRQ_ALLSENT +
- IRQ_UNDERRUN + IRQ_TXFIFO);
+ IRQ_RXEOM | IRQ_RXFIFO | IRQ_ALLSENT |
+ IRQ_UNDERRUN | IRQ_TXFIFO);
issue_command(info, CHA, CMD_TXRESET + CMD_RXRESET);
wait_command_complete(info, CHA);
read_reg16(info, CHA + ISR); /* clear pending IRQs */
@@ -3262,7 +3293,7 @@ static void rx_stop(MGSLPC_INFO *info)
{
if (debug_level >= DEBUG_LEVEL_ISR)
printk("%s(%d):rx_stop(%s)\n",
- __FILE__,__LINE__, info->device_name );
+ __FILE__, __LINE__, info->device_name);
/* MODE:03 RAC Receiver Active, 0=inactive */
clear_reg_bits(info, CHA + MODE, BIT3);
@@ -3275,7 +3306,7 @@ static void rx_start(MGSLPC_INFO *info)
{
if (debug_level >= DEBUG_LEVEL_ISR)
printk("%s(%d):rx_start(%s)\n",
- __FILE__,__LINE__, info->device_name );
+ __FILE__, __LINE__, info->device_name);
rx_reset_buffers(info);
info->rx_enabled = false;
@@ -3291,7 +3322,7 @@ static void tx_start(MGSLPC_INFO *info, struct tty_struct *tty)
{
if (debug_level >= DEBUG_LEVEL_ISR)
printk("%s(%d):tx_start(%s)\n",
- __FILE__,__LINE__, info->device_name );
+ __FILE__, __LINE__, info->device_name);
if (info->tx_count) {
/* If auto RTS enabled and RTS is inactive, then assert */
@@ -3329,7 +3360,7 @@ static void tx_stop(MGSLPC_INFO *info)
{
if (debug_level >= DEBUG_LEVEL_ISR)
printk("%s(%d):tx_stop(%s)\n",
- __FILE__,__LINE__, info->device_name );
+ __FILE__, __LINE__, info->device_name);
del_timer(&info->tx_timer);
@@ -3551,8 +3582,8 @@ static void async_mode(MGSLPC_INFO *info)
} else
clear_reg_bits(info, CHA + PVR, BIT3);
irq_enable(info, CHA,
- IRQ_RXEOM + IRQ_RXFIFO + IRQ_BREAK_ON + IRQ_RXTIME +
- IRQ_ALLSENT + IRQ_TXFIFO);
+ IRQ_RXEOM | IRQ_RXFIFO | IRQ_BREAK_ON | IRQ_RXTIME |
+ IRQ_ALLSENT | IRQ_TXFIFO);
issue_command(info, CHA, CMD_TXRESET + CMD_RXRESET);
wait_command_complete(info, CHA);
read_reg16(info, CHA + ISR); /* clear pending IRQs */
@@ -3575,8 +3606,8 @@ static void get_signals(MGSLPC_INFO *info)
{
unsigned char status = 0;
- /* preserve DTR and RTS */
- info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS;
+ /* preserve RTS and DTR */
+ info->serial_signals &= SerialSignal_RTS | SerialSignal_DTR;
if (read_reg(info, CHB + VSTR) & BIT7)
info->serial_signals |= SerialSignal_DCD;
@@ -3590,7 +3621,7 @@ static void get_signals(MGSLPC_INFO *info)
info->serial_signals |= SerialSignal_DSR;
}
-/* Set the state of DTR and RTS based on contents of
+/* Set the state of RTS and DTR based on contents of
* serial_signals member of device extension.
*/
static void set_signals(MGSLPC_INFO *info)
@@ -3681,7 +3712,7 @@ static bool rx_get_frame(MGSLPC_INFO *info, struct tty_struct *tty)
if (debug_level >= DEBUG_LEVEL_BH)
printk("%s(%d):rx_get_frame(%s) status=%04X size=%d\n",
- __FILE__,__LINE__,info->device_name,status,framesize);
+ __FILE__, __LINE__, info->device_name, status, framesize);
if (debug_level >= DEBUG_LEVEL_DATA)
trace_block(info, buf->data, framesize, 0);
@@ -3709,13 +3740,13 @@ static bool rx_get_frame(MGSLPC_INFO *info, struct tty_struct *tty)
}
}
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
buf->status = buf->count = 0;
info->rx_frame_count--;
info->rx_get++;
if (info->rx_get >= info->rx_buf_count)
info->rx_get = 0;
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
return true;
}
@@ -3729,7 +3760,7 @@ static bool register_test(MGSLPC_INFO *info)
bool rc = true;
unsigned long flags;
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
reset_device(info);
for (i = 0; i < count; i++) {
@@ -3742,7 +3773,7 @@ static bool register_test(MGSLPC_INFO *info)
}
}
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
return rc;
}
@@ -3751,7 +3782,7 @@ static bool irq_test(MGSLPC_INFO *info)
unsigned long end_time;
unsigned long flags;
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
reset_device(info);
info->testing_irq = true;
@@ -3765,7 +3796,7 @@ static bool irq_test(MGSLPC_INFO *info)
write_reg(info, CHA + TIMR, 0); /* 512 cycles */
issue_command(info, CHA, CMD_START_TIMER);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
end_time=100;
while(end_time-- && !info->irq_occurred) {
@@ -3774,9 +3805,9 @@ static bool irq_test(MGSLPC_INFO *info)
info->testing_irq = false;
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
reset_device(info);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
return info->irq_occurred;
}
@@ -3785,21 +3816,21 @@ static int adapter_test(MGSLPC_INFO *info)
{
if (!register_test(info)) {
info->init_error = DiagStatus_AddressFailure;
- printk( "%s(%d):Register test failure for device %s Addr=%04X\n",
- __FILE__,__LINE__,info->device_name, (unsigned short)(info->io_base) );
+ printk("%s(%d):Register test failure for device %s Addr=%04X\n",
+ __FILE__, __LINE__, info->device_name, (unsigned short)(info->io_base));
return -ENODEV;
}
if (!irq_test(info)) {
info->init_error = DiagStatus_IrqFailure;
- printk( "%s(%d):Interrupt test failure for device %s IRQ=%d\n",
- __FILE__,__LINE__,info->device_name, (unsigned short)(info->irq_level) );
+ printk("%s(%d):Interrupt test failure for device %s IRQ=%d\n",
+ __FILE__, __LINE__, info->device_name, (unsigned short)(info->irq_level));
return -ENODEV;
}
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):device %s passed diagnostics\n",
- __FILE__,__LINE__,info->device_name);
+ __FILE__, __LINE__, info->device_name);
return 0;
}
@@ -3808,9 +3839,9 @@ static void trace_block(MGSLPC_INFO *info,const char* data, int count, int xmit)
int i;
int linecount;
if (xmit)
- printk("%s tx data:\n",info->device_name);
+ printk("%s tx data:\n", info->device_name);
else
- printk("%s rx data:\n",info->device_name);
+ printk("%s rx data:\n", info->device_name);
while(count) {
if (count > 16)
@@ -3819,12 +3850,12 @@ static void trace_block(MGSLPC_INFO *info,const char* data, int count, int xmit)
linecount = count;
for(i=0;i<linecount;i++)
- printk("%02X ",(unsigned char)data[i]);
+ printk("%02X ", (unsigned char)data[i]);
for(;i<17;i++)
printk(" ");
for(i=0;i<linecount;i++) {
if (data[i]>=040 && data[i]<=0176)
- printk("%c",data[i]);
+ printk("%c", data[i]);
else
printk(".");
}
@@ -3843,18 +3874,18 @@ static void tx_timeout(unsigned long context)
MGSLPC_INFO *info = (MGSLPC_INFO*)context;
unsigned long flags;
- if ( debug_level >= DEBUG_LEVEL_INFO )
- printk( "%s(%d):tx_timeout(%s)\n",
- __FILE__,__LINE__,info->device_name);
- if(info->tx_active &&
- info->params.mode == MGSL_MODE_HDLC) {
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("%s(%d):tx_timeout(%s)\n",
+ __FILE__, __LINE__, info->device_name);
+ if (info->tx_active &&
+ info->params.mode == MGSL_MODE_HDLC) {
info->icount.txtimeout++;
}
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
info->tx_active = false;
info->tx_count = info->tx_put = info->tx_get = 0;
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
#if SYNCLINK_GENERIC_HDLC
if (info->netcount)
@@ -3936,7 +3967,7 @@ static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO)
- printk(KERN_INFO "%s:hdlc_xmit(%s)\n",__FILE__,dev->name);
+ printk(KERN_INFO "%s:hdlc_xmit(%s)\n", __FILE__, dev->name);
/* stop sending until this frame completes */
netif_stop_queue(dev);
@@ -3957,13 +3988,13 @@ static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
dev->trans_start = jiffies;
/* start hardware transmitter if necessary */
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
if (!info->tx_active) {
struct tty_struct *tty = tty_port_tty_get(&info->port);
- tx_start(info, tty);
- tty_kref_put(tty);
+ tx_start(info, tty);
+ tty_kref_put(tty);
}
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
return NETDEV_TX_OK;
}
@@ -3984,10 +4015,11 @@ static int hdlcdev_open(struct net_device *dev)
unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s:hdlcdev_open(%s)\n",__FILE__,dev->name);
+ printk("%s:hdlcdev_open(%s)\n", __FILE__, dev->name);
/* generic HDLC layer open processing */
- if ((rc = hdlc_open(dev)))
+ rc = hdlc_open(dev);
+ if (rc != 0)
return rc;
/* arbitrate between network and tty opens */
@@ -4002,15 +4034,16 @@ static int hdlcdev_open(struct net_device *dev)
tty = tty_port_tty_get(&info->port);
/* claim resources and init adapter */
- if ((rc = startup(info, tty)) != 0) {
+ rc = startup(info, tty);
+ if (rc != 0) {
tty_kref_put(tty);
spin_lock_irqsave(&info->netlock, flags);
info->netcount=0;
spin_unlock_irqrestore(&info->netlock, flags);
return rc;
}
- /* assert DTR and RTS, apply hardware settings */
- info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+ /* assert RTS and DTR, apply hardware settings */
+ info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR;
mgslpc_program_hw(info, tty);
tty_kref_put(tty);
@@ -4044,7 +4077,7 @@ static int hdlcdev_close(struct net_device *dev)
unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s:hdlcdev_close(%s)\n",__FILE__,dev->name);
+ printk("%s:hdlcdev_close(%s)\n", __FILE__, dev->name);
netif_stop_queue(dev);
@@ -4078,7 +4111,7 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
unsigned int flags;
if (debug_level >= DEBUG_LEVEL_INFO)
- printk("%s:hdlcdev_ioctl(%s)\n",__FILE__,dev->name);
+ printk("%s:hdlcdev_ioctl(%s)\n", __FILE__, dev->name);
/* return error if TTY interface open */
if (info->port.count)
@@ -4179,14 +4212,14 @@ static void hdlcdev_tx_timeout(struct net_device *dev)
unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO)
- printk("hdlcdev_tx_timeout(%s)\n",dev->name);
+ printk("hdlcdev_tx_timeout(%s)\n", dev->name);
dev->stats.tx_errors++;
dev->stats.tx_aborted_errors++;
- spin_lock_irqsave(&info->lock,flags);
+ spin_lock_irqsave(&info->lock, flags);
tx_stop(info);
- spin_unlock_irqrestore(&info->lock,flags);
+ spin_unlock_irqrestore(&info->lock, flags);
netif_wake_queue(dev);
}
@@ -4217,7 +4250,7 @@ static void hdlcdev_rx(MGSLPC_INFO *info, char *buf, int size)
struct net_device *dev = info->netdev;
if (debug_level >= DEBUG_LEVEL_INFO)
- printk("hdlcdev_rx(%s)\n",dev->name);
+ printk("hdlcdev_rx(%s)\n", dev->name);
if (skb == NULL) {
printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", dev->name);
@@ -4260,8 +4293,9 @@ static int hdlcdev_init(MGSLPC_INFO *info)
/* allocate and initialize network and HDLC layer objects */
- if (!(dev = alloc_hdlcdev(info))) {
- printk(KERN_ERR "%s:hdlc device allocation failure\n",__FILE__);
+ dev = alloc_hdlcdev(info);
+ if (dev == NULL) {
+ printk(KERN_ERR "%s:hdlc device allocation failure\n", __FILE__);
return -ENOMEM;
}
@@ -4280,8 +4314,9 @@ static int hdlcdev_init(MGSLPC_INFO *info)
hdlc->xmit = hdlcdev_xmit;
/* register objects with HDLC layer */
- if ((rc = register_hdlc_device(dev))) {
- printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__);
+ rc = register_hdlc_device(dev);
+ if (rc) {
+ printk(KERN_WARNING "%s:unable to register hdlc device\n", __FILE__);
free_netdev(dev);
return rc;
}
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index 1cd49241e60..ae0b42b66e5 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -107,7 +107,7 @@ static inline void pp_enable_irq (struct pp_struct *pp)
static ssize_t pp_read (struct file * file, char __user * buf, size_t count,
loff_t * ppos)
{
- unsigned int minor = iminor(file->f_path.dentry->d_inode);
+ unsigned int minor = iminor(file_inode(file));
struct pp_struct *pp = file->private_data;
char * kbuffer;
ssize_t bytes_read = 0;
@@ -189,7 +189,7 @@ static ssize_t pp_read (struct file * file, char __user * buf, size_t count,
static ssize_t pp_write (struct file * file, const char __user * buf,
size_t count, loff_t * ppos)
{
- unsigned int minor = iminor(file->f_path.dentry->d_inode);
+ unsigned int minor = iminor(file_inode(file));
struct pp_struct *pp = file->private_data;
char * kbuffer;
ssize_t bytes_written = 0;
@@ -324,7 +324,7 @@ static enum ieee1284_phase init_phase (int mode)
static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- unsigned int minor = iminor(file->f_path.dentry->d_inode);
+ unsigned int minor = iminor(file_inode(file));
struct pp_struct *pp = file->private_data;
struct parport * port;
void __user *argp = (void __user *)arg;
diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c
index 588063ac951..0b311fa277e 100644
--- a/drivers/char/ps3flash.c
+++ b/drivers/char/ps3flash.c
@@ -98,32 +98,8 @@ static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector)
static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)
{
struct ps3_storage_device *dev = ps3flash_dev;
- loff_t res;
-
- mutex_lock(&file->f_mapping->host->i_mutex);
- switch (origin) {
- case 0:
- break;
- case 1:
- offset += file->f_pos;
- break;
- case 2:
- offset += dev->regions[dev->region_idx].size*dev->blk_size;
- break;
- default:
- offset = -1;
- }
- if (offset < 0) {
- res = -EINVAL;
- goto out;
- }
-
- file->f_pos = offset;
- res = file->f_pos;
-
-out:
- mutex_unlock(&file->f_mapping->host->i_mutex);
- return res;
+ return generic_file_llseek_size(file, offset, origin, MAX_LFS_FILESIZE,
+ dev->regions[dev->region_idx].size*dev->blk_size);
}
static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf,
@@ -312,7 +288,7 @@ static int ps3flash_flush(struct file *file, fl_owner_t id)
static int ps3flash_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
- struct inode *inode = file->f_path.dentry->d_inode;
+ struct inode *inode = file_inode(file);
int err;
mutex_lock(&inode->i_mutex);
err = ps3flash_writeback(ps3flash_dev);
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 85e81ec1451..71529e196b8 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -255,10 +255,8 @@
#include <linux/fips.h>
#include <linux/ptrace.h>
#include <linux/kmemcheck.h>
-
-#ifdef CONFIG_GENERIC_HARDIRQS
-# include <linux/irq.h>
-#endif
+#include <linux/workqueue.h>
+#include <linux/irq.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
@@ -272,126 +270,132 @@
/*
* Configuration information
*/
-#define INPUT_POOL_WORDS 128
-#define OUTPUT_POOL_WORDS 32
-#define SEC_XFER_SIZE 512
-#define EXTRACT_SIZE 10
+#define INPUT_POOL_SHIFT 12
+#define INPUT_POOL_WORDS (1 << (INPUT_POOL_SHIFT-5))
+#define OUTPUT_POOL_SHIFT 10
+#define OUTPUT_POOL_WORDS (1 << (OUTPUT_POOL_SHIFT-5))
+#define SEC_XFER_SIZE 512
+#define EXTRACT_SIZE 10
+
+#define DEBUG_RANDOM_BOOT 0
#define LONGS(x) (((x) + sizeof(unsigned long) - 1)/sizeof(unsigned long))
/*
+ * To allow fractional bits to be tracked, the entropy_count field is
+ * denominated in units of 1/8th bits.
+ *
+ * 2*(ENTROPY_SHIFT + log2(poolbits)) must <= 31, or the multiply in
+ * credit_entropy_bits() needs to be 64 bits wide.
+ */
+#define ENTROPY_SHIFT 3
+#define ENTROPY_BITS(r) ((r)->entropy_count >> ENTROPY_SHIFT)
+
+/*
* The minimum number of bits of entropy before we wake up a read on
* /dev/random. Should be enough to do a significant reseed.
*/
-static int random_read_wakeup_thresh = 64;
+static int random_read_wakeup_bits = 64;
/*
* If the entropy count falls under this number of bits, then we
* should wake up processes which are selecting or polling on write
* access to /dev/random.
*/
-static int random_write_wakeup_thresh = 128;
+static int random_write_wakeup_bits = 28 * OUTPUT_POOL_WORDS;
/*
- * When the input pool goes over trickle_thresh, start dropping most
- * samples to avoid wasting CPU time and reduce lock contention.
+ * The minimum number of seconds between urandom pool reseeding. We
+ * do this to limit the amount of entropy that can be drained from the
+ * input pool even if there are heavy demands on /dev/urandom.
*/
-
-static int trickle_thresh __read_mostly = INPUT_POOL_WORDS * 28;
-
-static DEFINE_PER_CPU(int, trickle_count);
+static int random_min_urandom_seed = 60;
/*
- * A pool of size .poolwords is stirred with a primitive polynomial
- * of degree .poolwords over GF(2). The taps for various sizes are
- * defined below. They are chosen to be evenly spaced (minimum RMS
- * distance from evenly spaced; the numbers in the comments are a
- * scaled squared error sum) except for the last tap, which is 1 to
- * get the twisting happening as fast as possible.
+ * Originally, we used a primitive polynomial of degree .poolwords
+ * over GF(2). The taps for various sizes are defined below. They
+ * were chosen to be evenly spaced except for the last tap, which is 1
+ * to get the twisting happening as fast as possible.
+ *
+ * For the purposes of better mixing, we use the CRC-32 polynomial as
+ * well to make a (modified) twisted Generalized Feedback Shift
+ * Register. (See M. Matsumoto & Y. Kurita, 1992. Twisted GFSR
+ * generators. ACM Transactions on Modeling and Computer Simulation
+ * 2(3):179-194. Also see M. Matsumoto & Y. Kurita, 1994. Twisted
+ * GFSR generators II. ACM Transactions on Modeling and Computer
+ * Simulation 4:254-266)
+ *
+ * Thanks to Colin Plumb for suggesting this.
+ *
+ * The mixing operation is much less sensitive than the output hash,
+ * where we use SHA-1. All that we want of mixing operation is that
+ * it be a good non-cryptographic hash; i.e. it not produce collisions
+ * when fed "random" data of the sort we expect to see. As long as
+ * the pool state differs for different inputs, we have preserved the
+ * input entropy and done a good job. The fact that an intelligent
+ * attacker can construct inputs that will produce controlled
+ * alterations to the pool's state is not important because we don't
+ * consider such inputs to contribute any randomness. The only
+ * property we need with respect to them is that the attacker can't
+ * increase his/her knowledge of the pool's state. Since all
+ * additions are reversible (knowing the final state and the input,
+ * you can reconstruct the initial state), if an attacker has any
+ * uncertainty about the initial state, he/she can only shuffle that
+ * uncertainty about, but never cause any collisions (which would
+ * decrease the uncertainty).
+ *
+ * Our mixing functions were analyzed by Lacharme, Roeck, Strubel, and
+ * Videau in their paper, "The Linux Pseudorandom Number Generator
+ * Revisited" (see: http://eprint.iacr.org/2012/251.pdf). In their
+ * paper, they point out that we are not using a true Twisted GFSR,
+ * since Matsumoto & Kurita used a trinomial feedback polynomial (that
+ * is, with only three taps, instead of the six that we are using).
+ * As a result, the resulting polynomial is neither primitive nor
+ * irreducible, and hence does not have a maximal period over
+ * GF(2**32). They suggest a slight change to the generator
+ * polynomial which improves the resulting TGFSR polynomial to be
+ * irreducible, which we have made here.
*/
static struct poolinfo {
- int poolwords;
+ int poolbitshift, poolwords, poolbytes, poolbits, poolfracbits;
+#define S(x) ilog2(x)+5, (x), (x)*4, (x)*32, (x) << (ENTROPY_SHIFT+5)
int tap1, tap2, tap3, tap4, tap5;
} poolinfo_table[] = {
- /* x^128 + x^103 + x^76 + x^51 +x^25 + x + 1 -- 105 */
- { 128, 103, 76, 51, 25, 1 },
- /* x^32 + x^26 + x^20 + x^14 + x^7 + x + 1 -- 15 */
- { 32, 26, 20, 14, 7, 1 },
+ /* was: x^128 + x^103 + x^76 + x^51 +x^25 + x + 1 */
+ /* x^128 + x^104 + x^76 + x^51 +x^25 + x + 1 */
+ { S(128), 104, 76, 51, 25, 1 },
+ /* was: x^32 + x^26 + x^20 + x^14 + x^7 + x + 1 */
+ /* x^32 + x^26 + x^19 + x^14 + x^7 + x + 1 */
+ { S(32), 26, 19, 14, 7, 1 },
#if 0
/* x^2048 + x^1638 + x^1231 + x^819 + x^411 + x + 1 -- 115 */
- { 2048, 1638, 1231, 819, 411, 1 },
+ { S(2048), 1638, 1231, 819, 411, 1 },
/* x^1024 + x^817 + x^615 + x^412 + x^204 + x + 1 -- 290 */
- { 1024, 817, 615, 412, 204, 1 },
+ { S(1024), 817, 615, 412, 204, 1 },
/* x^1024 + x^819 + x^616 + x^410 + x^207 + x^2 + 1 -- 115 */
- { 1024, 819, 616, 410, 207, 2 },
+ { S(1024), 819, 616, 410, 207, 2 },
/* x^512 + x^411 + x^308 + x^208 + x^104 + x + 1 -- 225 */
- { 512, 411, 308, 208, 104, 1 },
+ { S(512), 411, 308, 208, 104, 1 },
/* x^512 + x^409 + x^307 + x^206 + x^102 + x^2 + 1 -- 95 */
- { 512, 409, 307, 206, 102, 2 },
+ { S(512), 409, 307, 206, 102, 2 },
/* x^512 + x^409 + x^309 + x^205 + x^103 + x^2 + 1 -- 95 */
- { 512, 409, 309, 205, 103, 2 },
+ { S(512), 409, 309, 205, 103, 2 },
/* x^256 + x^205 + x^155 + x^101 + x^52 + x + 1 -- 125 */
- { 256, 205, 155, 101, 52, 1 },
+ { S(256), 205, 155, 101, 52, 1 },
/* x^128 + x^103 + x^78 + x^51 + x^27 + x^2 + 1 -- 70 */
- { 128, 103, 78, 51, 27, 2 },
+ { S(128), 103, 78, 51, 27, 2 },
/* x^64 + x^52 + x^39 + x^26 + x^14 + x + 1 -- 15 */
- { 64, 52, 39, 26, 14, 1 },
+ { S(64), 52, 39, 26, 14, 1 },
#endif
};
-#define POOLBITS poolwords*32
-#define POOLBYTES poolwords*4
-
-/*
- * For the purposes of better mixing, we use the CRC-32 polynomial as
- * well to make a twisted Generalized Feedback Shift Reigster
- *
- * (See M. Matsumoto & Y. Kurita, 1992. Twisted GFSR generators. ACM
- * Transactions on Modeling and Computer Simulation 2(3):179-194.
- * Also see M. Matsumoto & Y. Kurita, 1994. Twisted GFSR generators
- * II. ACM Transactions on Mdeling and Computer Simulation 4:254-266)
- *
- * Thanks to Colin Plumb for suggesting this.
- *
- * We have not analyzed the resultant polynomial to prove it primitive;
- * in fact it almost certainly isn't. Nonetheless, the irreducible factors
- * of a random large-degree polynomial over GF(2) are more than large enough
- * that periodicity is not a concern.
- *
- * The input hash is much less sensitive than the output hash. All
- * that we want of it is that it be a good non-cryptographic hash;
- * i.e. it not produce collisions when fed "random" data of the sort
- * we expect to see. As long as the pool state differs for different
- * inputs, we have preserved the input entropy and done a good job.
- * The fact that an intelligent attacker can construct inputs that
- * will produce controlled alterations to the pool's state is not
- * important because we don't consider such inputs to contribute any
- * randomness. The only property we need with respect to them is that
- * the attacker can't increase his/her knowledge of the pool's state.
- * Since all additions are reversible (knowing the final state and the
- * input, you can reconstruct the initial state), if an attacker has
- * any uncertainty about the initial state, he/she can only shuffle
- * that uncertainty about, but never cause any collisions (which would
- * decrease the uncertainty).
- *
- * The chosen system lets the state of the pool be (essentially) the input
- * modulo the generator polymnomial. Now, for random primitive polynomials,
- * this is a universal class of hash functions, meaning that the chance
- * of a collision is limited by the attacker's knowledge of the generator
- * polynomail, so if it is chosen at random, an attacker can never force
- * a collision. Here, we use a fixed polynomial, but we *can* assume that
- * ###--> it is unknown to the processes generating the input entropy. <-###
- * Because of this important property, this is a good, collision-resistant
- * hash; hash collisions will occur no more often than chance.
- */
-
/*
* Static global variables
*/
@@ -399,17 +403,6 @@ static DECLARE_WAIT_QUEUE_HEAD(random_read_wait);
static DECLARE_WAIT_QUEUE_HEAD(random_write_wait);
static struct fasync_struct *fasync;
-static bool debug;
-module_param(debug, bool, 0644);
-#define DEBUG_ENT(fmt, arg...) do { \
- if (debug) \
- printk(KERN_DEBUG "random %04d %04d %04d: " \
- fmt,\
- input_pool.entropy_count,\
- blocking_pool.entropy_count,\
- nonblocking_pool.entropy_count,\
- ## arg); } while (0)
-
/**********************************************************************
*
* OS independent entropy store. Here are the functions which handle
@@ -420,23 +413,26 @@ module_param(debug, bool, 0644);
struct entropy_store;
struct entropy_store {
/* read-only data: */
- struct poolinfo *poolinfo;
+ const struct poolinfo *poolinfo;
__u32 *pool;
const char *name;
struct entropy_store *pull;
- int limit;
+ struct work_struct push_work;
/* read-write data: */
+ unsigned long last_pulled;
spinlock_t lock;
- unsigned add_ptr;
- unsigned input_rotate;
+ unsigned short add_ptr;
+ unsigned short input_rotate;
int entropy_count;
int entropy_total;
unsigned int initialized:1;
- bool last_data_init;
+ unsigned int limit:1;
+ unsigned int last_data_init:1;
__u8 last_data[EXTRACT_SIZE];
};
+static void push_to_pool(struct work_struct *work);
static __u32 input_pool_data[INPUT_POOL_WORDS];
static __u32 blocking_pool_data[OUTPUT_POOL_WORDS];
static __u32 nonblocking_pool_data[OUTPUT_POOL_WORDS];
@@ -445,7 +441,7 @@ static struct entropy_store input_pool = {
.poolinfo = &poolinfo_table[0],
.name = "input",
.limit = 1,
- .lock = __SPIN_LOCK_UNLOCKED(&input_pool.lock),
+ .lock = __SPIN_LOCK_UNLOCKED(input_pool.lock),
.pool = input_pool_data
};
@@ -454,16 +450,20 @@ static struct entropy_store blocking_pool = {
.name = "blocking",
.limit = 1,
.pull = &input_pool,
- .lock = __SPIN_LOCK_UNLOCKED(&blocking_pool.lock),
- .pool = blocking_pool_data
+ .lock = __SPIN_LOCK_UNLOCKED(blocking_pool.lock),
+ .pool = blocking_pool_data,
+ .push_work = __WORK_INITIALIZER(blocking_pool.push_work,
+ push_to_pool),
};
static struct entropy_store nonblocking_pool = {
.poolinfo = &poolinfo_table[1],
.name = "nonblocking",
.pull = &input_pool,
- .lock = __SPIN_LOCK_UNLOCKED(&nonblocking_pool.lock),
- .pool = nonblocking_pool_data
+ .lock = __SPIN_LOCK_UNLOCKED(nonblocking_pool.lock),
+ .pool = nonblocking_pool_data,
+ .push_work = __WORK_INITIALIZER(nonblocking_pool.push_work,
+ push_to_pool),
};
static __u32 const twist_table[8] = {
@@ -501,7 +501,7 @@ static void _mix_pool_bytes(struct entropy_store *r, const void *in,
/* mix one byte at a time to simplify size handling and churn faster */
while (nbytes--) {
- w = rol32(*bytes++, input_rotate & 31);
+ w = rol32(*bytes++, input_rotate);
i = (i - 1) & wordmask;
/* XOR in the various taps */
@@ -521,7 +521,7 @@ static void _mix_pool_bytes(struct entropy_store *r, const void *in,
* rotation, so that successive passes spread the
* input bits across the pool evenly.
*/
- input_rotate += i ? 7 : 14;
+ input_rotate = (input_rotate + (i ? 7 : 14)) & 31;
}
ACCESS_ONCE(r->input_rotate) = input_rotate;
@@ -564,62 +564,151 @@ struct fast_pool {
* collector. It's hardcoded for an 128 bit pool and assumes that any
* locks that might be needed are taken by the caller.
*/
-static void fast_mix(struct fast_pool *f, const void *in, int nbytes)
+static void fast_mix(struct fast_pool *f, __u32 input[4])
{
- const char *bytes = in;
__u32 w;
- unsigned i = f->count;
unsigned input_rotate = f->rotate;
- while (nbytes--) {
- w = rol32(*bytes++, input_rotate & 31) ^ f->pool[i & 3] ^
- f->pool[(i + 1) & 3];
- f->pool[i & 3] = (w >> 3) ^ twist_table[w & 7];
- input_rotate += (i++ & 3) ? 7 : 14;
- }
- f->count = i;
+ w = rol32(input[0], input_rotate) ^ f->pool[0] ^ f->pool[3];
+ f->pool[0] = (w >> 3) ^ twist_table[w & 7];
+ input_rotate = (input_rotate + 14) & 31;
+ w = rol32(input[1], input_rotate) ^ f->pool[1] ^ f->pool[0];
+ f->pool[1] = (w >> 3) ^ twist_table[w & 7];
+ input_rotate = (input_rotate + 7) & 31;
+ w = rol32(input[2], input_rotate) ^ f->pool[2] ^ f->pool[1];
+ f->pool[2] = (w >> 3) ^ twist_table[w & 7];
+ input_rotate = (input_rotate + 7) & 31;
+ w = rol32(input[3], input_rotate) ^ f->pool[3] ^ f->pool[2];
+ f->pool[3] = (w >> 3) ^ twist_table[w & 7];
+ input_rotate = (input_rotate + 7) & 31;
+
f->rotate = input_rotate;
+ f->count++;
}
/*
- * Credit (or debit) the entropy store with n bits of entropy
+ * Credit (or debit) the entropy store with n bits of entropy.
+ * Use credit_entropy_bits_safe() if the value comes from userspace
+ * or otherwise should be checked for extreme values.
*/
static void credit_entropy_bits(struct entropy_store *r, int nbits)
{
int entropy_count, orig;
+ const int pool_size = r->poolinfo->poolfracbits;
+ int nfrac = nbits << ENTROPY_SHIFT;
if (!nbits)
return;
- DEBUG_ENT("added %d entropy credits to %s\n", nbits, r->name);
retry:
entropy_count = orig = ACCESS_ONCE(r->entropy_count);
- entropy_count += nbits;
+ if (nfrac < 0) {
+ /* Debit */
+ entropy_count += nfrac;
+ } else {
+ /*
+ * Credit: we have to account for the possibility of
+ * overwriting already present entropy. Even in the
+ * ideal case of pure Shannon entropy, new contributions
+ * approach the full value asymptotically:
+ *
+ * entropy <- entropy + (pool_size - entropy) *
+ * (1 - exp(-add_entropy/pool_size))
+ *
+ * For add_entropy <= pool_size/2 then
+ * (1 - exp(-add_entropy/pool_size)) >=
+ * (add_entropy/pool_size)*0.7869...
+ * so we can approximate the exponential with
+ * 3/4*add_entropy/pool_size and still be on the
+ * safe side by adding at most pool_size/2 at a time.
+ *
+ * The use of pool_size-2 in the while statement is to
+ * prevent rounding artifacts from making the loop
+ * arbitrarily long; this limits the loop to log2(pool_size)*2
+ * turns no matter how large nbits is.
+ */
+ int pnfrac = nfrac;
+ const int s = r->poolinfo->poolbitshift + ENTROPY_SHIFT + 2;
+ /* The +2 corresponds to the /4 in the denominator */
+
+ do {
+ unsigned int anfrac = min(pnfrac, pool_size/2);
+ unsigned int add =
+ ((pool_size - entropy_count)*anfrac*3) >> s;
+
+ entropy_count += add;
+ pnfrac -= anfrac;
+ } while (unlikely(entropy_count < pool_size-2 && pnfrac));
+ }
- if (entropy_count < 0) {
- DEBUG_ENT("negative entropy/overflow\n");
+ if (unlikely(entropy_count < 0)) {
+ pr_warn("random: negative entropy/overflow: pool %s count %d\n",
+ r->name, entropy_count);
+ WARN_ON(1);
entropy_count = 0;
- } else if (entropy_count > r->poolinfo->POOLBITS)
- entropy_count = r->poolinfo->POOLBITS;
+ } else if (entropy_count > pool_size)
+ entropy_count = pool_size;
if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
goto retry;
- if (!r->initialized && nbits > 0) {
- r->entropy_total += nbits;
- if (r->entropy_total > 128)
- r->initialized = 1;
+ r->entropy_total += nbits;
+ if (!r->initialized && r->entropy_total > 128) {
+ r->initialized = 1;
+ r->entropy_total = 0;
+ if (r == &nonblocking_pool) {
+ prandom_reseed_late();
+ pr_notice("random: %s pool is initialized\n", r->name);
+ }
}
- trace_credit_entropy_bits(r->name, nbits, entropy_count,
+ trace_credit_entropy_bits(r->name, nbits,
+ entropy_count >> ENTROPY_SHIFT,
r->entropy_total, _RET_IP_);
- /* should we wake readers? */
- if (r == &input_pool && entropy_count >= random_read_wakeup_thresh) {
- wake_up_interruptible(&random_read_wait);
- kill_fasync(&fasync, SIGIO, POLL_IN);
+ if (r == &input_pool) {
+ int entropy_bits = entropy_count >> ENTROPY_SHIFT;
+
+ /* should we wake readers? */
+ if (entropy_bits >= random_read_wakeup_bits) {
+ wake_up_interruptible(&random_read_wait);
+ kill_fasync(&fasync, SIGIO, POLL_IN);
+ }
+ /* If the input pool is getting full, send some
+ * entropy to the two output pools, flipping back and
+ * forth between them, until the output pools are 75%
+ * full.
+ */
+ if (entropy_bits > random_write_wakeup_bits &&
+ r->initialized &&
+ r->entropy_total >= 2*random_read_wakeup_bits) {
+ static struct entropy_store *last = &blocking_pool;
+ struct entropy_store *other = &blocking_pool;
+
+ if (last == &blocking_pool)
+ other = &nonblocking_pool;
+ if (other->entropy_count <=
+ 3 * other->poolinfo->poolfracbits / 4)
+ last = other;
+ if (last->entropy_count <=
+ 3 * last->poolinfo->poolfracbits / 4) {
+ schedule_work(&last->push_work);
+ r->entropy_total = 0;
+ }
+ }
}
}
+static void credit_entropy_bits_safe(struct entropy_store *r, int nbits)
+{
+ const int nbits_max = (int)(~0U >> (ENTROPY_SHIFT + 1));
+
+ /* Cap the value to avoid overflows */
+ nbits = min(nbits, nbits_max);
+ nbits = max(nbits, -nbits_max);
+
+ credit_entropy_bits(r, nbits);
+}
+
/*********************************************************************
*
* Entropy input management
@@ -633,6 +722,8 @@ struct timer_rand_state {
unsigned dont_count_entropy:1;
};
+#define INIT_TIMER_RAND_STATE { INITIAL_JIFFIES, };
+
/*
* Add device- or boot-specific data to the input and nonblocking
* pools to help initialize them to unique values.
@@ -643,16 +734,23 @@ struct timer_rand_state {
*/
void add_device_randomness(const void *buf, unsigned int size)
{
- unsigned long time = get_cycles() ^ jiffies;
+ unsigned long time = random_get_entropy() ^ jiffies;
+ unsigned long flags;
+
+ trace_add_device_randomness(size, _RET_IP_);
+ spin_lock_irqsave(&input_pool.lock, flags);
+ _mix_pool_bytes(&input_pool, buf, size, NULL);
+ _mix_pool_bytes(&input_pool, &time, sizeof(time), NULL);
+ spin_unlock_irqrestore(&input_pool.lock, flags);
- mix_pool_bytes(&input_pool, buf, size, NULL);
- mix_pool_bytes(&input_pool, &time, sizeof(time), NULL);
- mix_pool_bytes(&nonblocking_pool, buf, size, NULL);
- mix_pool_bytes(&nonblocking_pool, &time, sizeof(time), NULL);
+ spin_lock_irqsave(&nonblocking_pool.lock, flags);
+ _mix_pool_bytes(&nonblocking_pool, buf, size, NULL);
+ _mix_pool_bytes(&nonblocking_pool, &time, sizeof(time), NULL);
+ spin_unlock_irqrestore(&nonblocking_pool.lock, flags);
}
EXPORT_SYMBOL(add_device_randomness);
-static struct timer_rand_state input_timer_state;
+static struct timer_rand_state input_timer_state = INIT_TIMER_RAND_STATE;
/*
* This function adds entropy to the entropy "pool" by using timing
@@ -666,6 +764,7 @@ static struct timer_rand_state input_timer_state;
*/
static void add_timer_randomness(struct timer_rand_state *state, unsigned num)
{
+ struct entropy_store *r;
struct {
long jiffies;
unsigned cycles;
@@ -674,15 +773,12 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num)
long delta, delta2, delta3;
preempt_disable();
- /* if over the trickle threshold, use only 1 in 4096 samples */
- if (input_pool.entropy_count > trickle_thresh &&
- ((__this_cpu_inc_return(trickle_count) - 1) & 0xfff))
- goto out;
sample.jiffies = jiffies;
- sample.cycles = get_cycles();
+ sample.cycles = random_get_entropy();
sample.num = num;
- mix_pool_bytes(&input_pool, &sample, sizeof(sample), NULL);
+ r = nonblocking_pool.initialized ? &input_pool : &nonblocking_pool;
+ mix_pool_bytes(r, &sample, sizeof(sample), NULL);
/*
* Calculate number of bits of randomness we probably added.
@@ -716,10 +812,8 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num)
* Round down by 1 bit on general principles,
* and limit entropy entimate to 12 bits.
*/
- credit_entropy_bits(&input_pool,
- min_t(int, fls(delta>>1), 11));
+ credit_entropy_bits(r, min_t(int, fls(delta>>1), 11));
}
-out:
preempt_enable();
}
@@ -732,10 +826,10 @@ void add_input_randomness(unsigned int type, unsigned int code,
if (value == last_value)
return;
- DEBUG_ENT("input event\n");
last_value = value;
add_timer_randomness(&input_timer_state,
(type << 4) ^ code ^ (code >> 4) ^ value);
+ trace_add_input_randomness(ENTROPY_BITS(&input_pool));
}
EXPORT_SYMBOL_GPL(add_input_randomness);
@@ -747,40 +841,56 @@ void add_interrupt_randomness(int irq, int irq_flags)
struct fast_pool *fast_pool = &__get_cpu_var(irq_randomness);
struct pt_regs *regs = get_irq_regs();
unsigned long now = jiffies;
- __u32 input[4], cycles = get_cycles();
-
- input[0] = cycles ^ jiffies;
- input[1] = irq;
- if (regs) {
- __u64 ip = instruction_pointer(regs);
- input[2] = ip;
- input[3] = ip >> 32;
- }
-
- fast_mix(fast_pool, input, sizeof(input));
-
- if ((fast_pool->count & 1023) &&
- !time_after(now, fast_pool->last + HZ))
+ cycles_t cycles = random_get_entropy();
+ __u32 input[4], c_high, j_high;
+ __u64 ip;
+ unsigned long seed;
+ int credit;
+
+ c_high = (sizeof(cycles) > 4) ? cycles >> 32 : 0;
+ j_high = (sizeof(now) > 4) ? now >> 32 : 0;
+ input[0] = cycles ^ j_high ^ irq;
+ input[1] = now ^ c_high;
+ ip = regs ? instruction_pointer(regs) : _RET_IP_;
+ input[2] = ip;
+ input[3] = ip >> 32;
+
+ fast_mix(fast_pool, input);
+
+ if ((fast_pool->count & 63) && !time_after(now, fast_pool->last + HZ))
return;
fast_pool->last = now;
r = nonblocking_pool.initialized ? &input_pool : &nonblocking_pool;
__mix_pool_bytes(r, &fast_pool->pool, sizeof(fast_pool->pool), NULL);
+
/*
* If we don't have a valid cycle counter, and we see
* back-to-back timer interrupts, then skip giving credit for
- * any entropy.
+ * any entropy, otherwise credit 1 bit.
*/
+ credit = 1;
if (cycles == 0) {
if (irq_flags & __IRQF_TIMER) {
if (fast_pool->last_timer_intr)
- return;
+ credit = 0;
fast_pool->last_timer_intr = 1;
} else
fast_pool->last_timer_intr = 0;
}
- credit_entropy_bits(r, 1);
+
+ /*
+ * If we have architectural seed generator, produce a seed and
+ * add it to the pool. For the sake of paranoia count it as
+ * 50% entropic.
+ */
+ if (arch_get_random_seed_long(&seed)) {
+ __mix_pool_bytes(r, &seed, sizeof(seed), NULL);
+ credit += sizeof(seed) * 4;
+ }
+
+ credit_entropy_bits(r, credit);
}
#ifdef CONFIG_BLOCK
@@ -789,11 +899,10 @@ void add_disk_randomness(struct gendisk *disk)
if (!disk || !disk->random)
return;
/* first major is 1, so we get >= 0x200 here */
- DEBUG_ENT("disk event %d:%d\n",
- MAJOR(disk_devt(disk)), MINOR(disk_devt(disk)));
-
add_timer_randomness(disk->random, 0x100 + disk_devt(disk));
+ trace_add_disk_randomness(disk_devt(disk), ENTROPY_BITS(&input_pool));
}
+EXPORT_SYMBOL_GPL(add_disk_randomness);
#endif
/*********************************************************************
@@ -810,96 +919,142 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
* from the primary pool to the secondary extraction pool. We make
* sure we pull enough for a 'catastrophic reseed'.
*/
+static void _xfer_secondary_pool(struct entropy_store *r, size_t nbytes);
static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes)
{
- __u32 tmp[OUTPUT_POOL_WORDS];
+ if (r->limit == 0 && random_min_urandom_seed) {
+ unsigned long now = jiffies;
- if (r->pull && r->entropy_count < nbytes * 8 &&
- r->entropy_count < r->poolinfo->POOLBITS) {
- /* If we're limited, always leave two wakeup worth's BITS */
- int rsvd = r->limit ? 0 : random_read_wakeup_thresh/4;
- int bytes = nbytes;
-
- /* pull at least as many as BYTES as wakeup BITS */
- bytes = max_t(int, bytes, random_read_wakeup_thresh / 8);
- /* but never more than the buffer size */
- bytes = min_t(int, bytes, sizeof(tmp));
-
- DEBUG_ENT("going to reseed %s with %d bits "
- "(%zu of %d requested)\n",
- r->name, bytes * 8, nbytes * 8, r->entropy_count);
-
- bytes = extract_entropy(r->pull, tmp, bytes,
- random_read_wakeup_thresh / 8, rsvd);
- mix_pool_bytes(r, tmp, bytes, NULL);
- credit_entropy_bits(r, bytes*8);
+ if (time_before(now,
+ r->last_pulled + random_min_urandom_seed * HZ))
+ return;
+ r->last_pulled = now;
}
+ if (r->pull &&
+ r->entropy_count < (nbytes << (ENTROPY_SHIFT + 3)) &&
+ r->entropy_count < r->poolinfo->poolfracbits)
+ _xfer_secondary_pool(r, nbytes);
+}
+
+static void _xfer_secondary_pool(struct entropy_store *r, size_t nbytes)
+{
+ __u32 tmp[OUTPUT_POOL_WORDS];
+
+ /* For /dev/random's pool, always leave two wakeups' worth */
+ int rsvd_bytes = r->limit ? 0 : random_read_wakeup_bits / 4;
+ int bytes = nbytes;
+
+ /* pull at least as much as a wakeup */
+ bytes = max_t(int, bytes, random_read_wakeup_bits / 8);
+ /* but never more than the buffer size */
+ bytes = min_t(int, bytes, sizeof(tmp));
+
+ trace_xfer_secondary_pool(r->name, bytes * 8, nbytes * 8,
+ ENTROPY_BITS(r), ENTROPY_BITS(r->pull));
+ bytes = extract_entropy(r->pull, tmp, bytes,
+ random_read_wakeup_bits / 8, rsvd_bytes);
+ mix_pool_bytes(r, tmp, bytes, NULL);
+ credit_entropy_bits(r, bytes*8);
}
/*
- * These functions extracts randomness from the "entropy pool", and
- * returns it in a buffer.
- *
- * The min parameter specifies the minimum amount we can pull before
- * failing to avoid races that defeat catastrophic reseeding while the
- * reserved parameter indicates how much entropy we must leave in the
- * pool after each pull to avoid starving other readers.
- *
- * Note: extract_entropy() assumes that .poolwords is a multiple of 16 words.
+ * Used as a workqueue function so that when the input pool is getting
+ * full, we can "spill over" some entropy to the output pools. That
+ * way the output pools can store some of the excess entropy instead
+ * of letting it go to waste.
*/
+static void push_to_pool(struct work_struct *work)
+{
+ struct entropy_store *r = container_of(work, struct entropy_store,
+ push_work);
+ BUG_ON(!r);
+ _xfer_secondary_pool(r, random_read_wakeup_bits/8);
+ trace_push_to_pool(r->name, r->entropy_count >> ENTROPY_SHIFT,
+ r->pull->entropy_count >> ENTROPY_SHIFT);
+}
+/*
+ * This function decides how many bytes to actually take from the
+ * given pool, and also debits the entropy count accordingly.
+ */
static size_t account(struct entropy_store *r, size_t nbytes, int min,
int reserved)
{
- unsigned long flags;
-
- /* Hold lock while accounting */
- spin_lock_irqsave(&r->lock, flags);
+ int entropy_count, orig;
+ size_t ibytes, nfrac;
- BUG_ON(r->entropy_count > r->poolinfo->POOLBITS);
- DEBUG_ENT("trying to extract %zu bits from %s\n",
- nbytes * 8, r->name);
+ BUG_ON(r->entropy_count > r->poolinfo->poolfracbits);
/* Can we pull enough? */
- if (r->entropy_count / 8 < min + reserved) {
- nbytes = 0;
- } else {
- /* If limited, never pull more than available */
- if (r->limit && nbytes + reserved >= r->entropy_count / 8)
- nbytes = r->entropy_count/8 - reserved;
-
- if (r->entropy_count / 8 >= nbytes + reserved)
- r->entropy_count -= nbytes*8;
- else
- r->entropy_count = reserved;
-
- if (r->entropy_count < random_write_wakeup_thresh) {
- wake_up_interruptible(&random_write_wait);
- kill_fasync(&fasync, SIGIO, POLL_OUT);
- }
+retry:
+ entropy_count = orig = ACCESS_ONCE(r->entropy_count);
+ ibytes = nbytes;
+ /* If limited, never pull more than available */
+ if (r->limit) {
+ int have_bytes = entropy_count >> (ENTROPY_SHIFT + 3);
+
+ if ((have_bytes -= reserved) < 0)
+ have_bytes = 0;
+ ibytes = min_t(size_t, ibytes, have_bytes);
}
+ if (ibytes < min)
+ ibytes = 0;
- DEBUG_ENT("debiting %zu entropy credits from %s%s\n",
- nbytes * 8, r->name, r->limit ? "" : " (unlimited)");
+ if (unlikely(entropy_count < 0)) {
+ pr_warn("random: negative entropy count: pool %s count %d\n",
+ r->name, entropy_count);
+ WARN_ON(1);
+ entropy_count = 0;
+ }
+ nfrac = ibytes << (ENTROPY_SHIFT + 3);
+ if ((size_t) entropy_count > nfrac)
+ entropy_count -= nfrac;
+ else
+ entropy_count = 0;
- spin_unlock_irqrestore(&r->lock, flags);
+ if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
+ goto retry;
+
+ trace_debit_entropy(r->name, 8 * ibytes);
+ if (ibytes &&
+ (r->entropy_count >> ENTROPY_SHIFT) < random_write_wakeup_bits) {
+ wake_up_interruptible(&random_write_wait);
+ kill_fasync(&fasync, SIGIO, POLL_OUT);
+ }
- return nbytes;
+ return ibytes;
}
+/*
+ * This function does the actual extraction for extract_entropy and
+ * extract_entropy_user.
+ *
+ * Note: we assume that .poolwords is a multiple of 16 words.
+ */
static void extract_buf(struct entropy_store *r, __u8 *out)
{
int i;
union {
__u32 w[5];
- unsigned long l[LONGS(EXTRACT_SIZE)];
+ unsigned long l[LONGS(20)];
} hash;
__u32 workspace[SHA_WORKSPACE_WORDS];
__u8 extract[64];
unsigned long flags;
- /* Generate a hash across the pool, 16 words (512 bits) at a time */
+ /*
+ * If we have an architectural hardware random number
+ * generator, use it for SHA's initial vector
+ */
sha_init(hash.w);
+ for (i = 0; i < LONGS(20); i++) {
+ unsigned long v;
+ if (!arch_get_random_long(&v))
+ break;
+ hash.l[i] = v;
+ }
+
+ /* Generate a hash across the pool, 16 words (512 bits) at a time */
spin_lock_irqsave(&r->lock, flags);
for (i = 0; i < r->poolinfo->poolwords; i += 16)
sha_transform(hash.w, (__u8 *)(r->pool + i), workspace);
@@ -933,32 +1088,43 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
hash.w[1] ^= hash.w[4];
hash.w[2] ^= rol32(hash.w[2], 16);
- /*
- * If we have a architectural hardware random number
- * generator, mix that in, too.
- */
- for (i = 0; i < LONGS(EXTRACT_SIZE); i++) {
- unsigned long v;
- if (!arch_get_random_long(&v))
- break;
- hash.l[i] ^= v;
- }
-
memcpy(out, &hash, EXTRACT_SIZE);
memset(&hash, 0, sizeof(hash));
}
+/*
+ * This function extracts randomness from the "entropy pool", and
+ * returns it in a buffer.
+ *
+ * The min parameter specifies the minimum amount we can pull before
+ * failing to avoid races that defeat catastrophic reseeding while the
+ * reserved parameter indicates how much entropy we must leave in the
+ * pool after each pull to avoid starving other readers.
+ */
static ssize_t extract_entropy(struct entropy_store *r, void *buf,
size_t nbytes, int min, int reserved)
{
ssize_t ret = 0, i;
__u8 tmp[EXTRACT_SIZE];
+ unsigned long flags;
/* if last_data isn't primed, we need EXTRACT_SIZE extra bytes */
- if (fips_enabled && !r->last_data_init)
- nbytes += EXTRACT_SIZE;
+ if (fips_enabled) {
+ spin_lock_irqsave(&r->lock, flags);
+ if (!r->last_data_init) {
+ r->last_data_init = 1;
+ spin_unlock_irqrestore(&r->lock, flags);
+ trace_extract_entropy(r->name, EXTRACT_SIZE,
+ ENTROPY_BITS(r), _RET_IP_);
+ xfer_secondary_pool(r, EXTRACT_SIZE);
+ extract_buf(r, tmp);
+ spin_lock_irqsave(&r->lock, flags);
+ memcpy(r->last_data, tmp, EXTRACT_SIZE);
+ }
+ spin_unlock_irqrestore(&r->lock, flags);
+ }
- trace_extract_entropy(r->name, nbytes, r->entropy_count, _RET_IP_);
+ trace_extract_entropy(r->name, nbytes, ENTROPY_BITS(r), _RET_IP_);
xfer_secondary_pool(r, nbytes);
nbytes = account(r, nbytes, min, reserved);
@@ -966,19 +1132,6 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
extract_buf(r, tmp);
if (fips_enabled) {
- unsigned long flags;
-
-
- /* prime last_data value if need be, per fips 140-2 */
- if (!r->last_data_init) {
- spin_lock_irqsave(&r->lock, flags);
- memcpy(r->last_data, tmp, EXTRACT_SIZE);
- r->last_data_init = true;
- nbytes -= EXTRACT_SIZE;
- spin_unlock_irqrestore(&r->lock, flags);
- extract_buf(r, tmp);
- }
-
spin_lock_irqsave(&r->lock, flags);
if (!memcmp(tmp, r->last_data, EXTRACT_SIZE))
panic("Hardware RNG duplicated output!\n");
@@ -998,13 +1151,17 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
return ret;
}
+/*
+ * This function extracts randomness from the "entropy pool", and
+ * returns it in a userspace buffer.
+ */
static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
size_t nbytes)
{
ssize_t ret = 0, i;
__u8 tmp[EXTRACT_SIZE];
- trace_extract_entropy_user(r->name, nbytes, r->entropy_count, _RET_IP_);
+ trace_extract_entropy_user(r->name, nbytes, ENTROPY_BITS(r), _RET_IP_);
xfer_secondary_pool(r, nbytes);
nbytes = account(r, nbytes, 0, 0);
@@ -1039,11 +1196,20 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
/*
* This function is the exported kernel interface. It returns some
* number of good random numbers, suitable for key generation, seeding
- * TCP sequence numbers, etc. It does not use the hw random number
- * generator, if available; use get_random_bytes_arch() for that.
+ * TCP sequence numbers, etc. It does not rely on the hardware random
+ * number generator. For random bytes direct from the hardware RNG
+ * (when available), use get_random_bytes_arch().
*/
void get_random_bytes(void *buf, int nbytes)
{
+#if DEBUG_RANDOM_BOOT > 0
+ if (unlikely(nonblocking_pool.initialized == 0))
+ printk(KERN_NOTICE "random: %pF get_random_bytes called "
+ "with %d bits of entropy available\n",
+ (void *) _RET_IP_,
+ nonblocking_pool.entropy_total);
+#endif
+ trace_get_random_bytes(nbytes, _RET_IP_);
extract_entropy(&nonblocking_pool, buf, nbytes, 0, 0);
}
EXPORT_SYMBOL(get_random_bytes);
@@ -1062,7 +1228,7 @@ void get_random_bytes_arch(void *buf, int nbytes)
{
char *p = buf;
- trace_get_random_bytes(nbytes, _RET_IP_);
+ trace_get_random_bytes_arch(nbytes, _RET_IP_);
while (nbytes) {
unsigned long v;
int chunk = min(nbytes, (int)sizeof(unsigned long));
@@ -1096,13 +1262,12 @@ static void init_std_data(struct entropy_store *r)
ktime_t now = ktime_get_real();
unsigned long rv;
- r->entropy_count = 0;
- r->entropy_total = 0;
- r->last_data_init = false;
+ r->last_pulled = jiffies;
mix_pool_bytes(r, &now, sizeof(now), NULL);
- for (i = r->poolinfo->POOLBYTES; i > 0; i -= sizeof(rv)) {
- if (!arch_get_random_long(&rv))
- break;
+ for (i = r->poolinfo->poolbytes; i > 0; i -= sizeof(rv)) {
+ if (!arch_get_random_seed_long(&rv) &&
+ !arch_get_random_long(&rv))
+ rv = random_get_entropy();
mix_pool_bytes(r, &rv, sizeof(rv), NULL);
}
mix_pool_bytes(r, utsname(), sizeof(*(utsname())), NULL);
@@ -1125,7 +1290,7 @@ static int rand_initialize(void)
init_std_data(&nonblocking_pool);
return 0;
}
-module_init(rand_initialize);
+early_initcall(rand_initialize);
#ifdef CONFIG_BLOCK
void rand_initialize_disk(struct gendisk *disk)
@@ -1137,72 +1302,96 @@ void rand_initialize_disk(struct gendisk *disk)
* source.
*/
state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
- if (state)
+ if (state) {
+ state->last_time = INITIAL_JIFFIES;
disk->random = state;
+ }
}
#endif
-static ssize_t
-random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
+/*
+ * Attempt an emergency refill using arch_get_random_seed_long().
+ *
+ * As with add_interrupt_randomness() be paranoid and only
+ * credit the output as 50% entropic.
+ */
+static int arch_random_refill(void)
{
- ssize_t n, retval = 0, count = 0;
+ const unsigned int nlongs = 64; /* Arbitrary number */
+ unsigned int n = 0;
+ unsigned int i;
+ unsigned long buf[nlongs];
- if (nbytes == 0)
+ if (!arch_has_random_seed())
return 0;
- while (nbytes > 0) {
- n = nbytes;
- if (n > SEC_XFER_SIZE)
- n = SEC_XFER_SIZE;
-
- DEBUG_ENT("reading %zu bits\n", n*8);
-
- n = extract_entropy_user(&blocking_pool, buf, n);
-
- if (n < 0) {
- retval = n;
- break;
- }
-
- DEBUG_ENT("read got %zd bits (%zd still needed)\n",
- n*8, (nbytes-n)*8);
+ for (i = 0; i < nlongs; i++) {
+ if (arch_get_random_seed_long(&buf[n]))
+ n++;
+ }
- if (n == 0) {
- if (file->f_flags & O_NONBLOCK) {
- retval = -EAGAIN;
- break;
- }
+ if (n) {
+ unsigned int rand_bytes = n * sizeof(unsigned long);
- DEBUG_ENT("sleeping?\n");
+ mix_pool_bytes(&input_pool, buf, rand_bytes, NULL);
+ credit_entropy_bits(&input_pool, rand_bytes*4);
+ }
- wait_event_interruptible(random_read_wait,
- input_pool.entropy_count >=
- random_read_wakeup_thresh);
+ return n;
+}
- DEBUG_ENT("awake\n");
+static ssize_t
+random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
+{
+ ssize_t n;
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
+ if (nbytes == 0)
+ return 0;
+ nbytes = min_t(size_t, nbytes, SEC_XFER_SIZE);
+ while (1) {
+ n = extract_entropy_user(&blocking_pool, buf, nbytes);
+ if (n < 0)
+ return n;
+ trace_random_read(n*8, (nbytes-n)*8,
+ ENTROPY_BITS(&blocking_pool),
+ ENTROPY_BITS(&input_pool));
+ if (n > 0)
+ return n;
+
+ /* Pool is (near) empty. Maybe wait and retry. */
+
+ /* First try an emergency refill */
+ if (arch_random_refill())
continue;
- }
- count += n;
- buf += n;
- nbytes -= n;
- break; /* This break makes the device work */
- /* like a named pipe */
- }
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
- return (count ? count : retval);
+ wait_event_interruptible(random_read_wait,
+ ENTROPY_BITS(&input_pool) >=
+ random_read_wakeup_bits);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ }
}
static ssize_t
urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
- return extract_entropy_user(&nonblocking_pool, buf, nbytes);
+ int ret;
+
+ if (unlikely(nonblocking_pool.initialized == 0))
+ printk_once(KERN_NOTICE "random: %s urandom read "
+ "with %d bits of entropy available\n",
+ current->comm, nonblocking_pool.entropy_total);
+
+ nbytes = min_t(size_t, nbytes, INT_MAX >> (ENTROPY_SHIFT + 3));
+ ret = extract_entropy_user(&nonblocking_pool, buf, nbytes);
+
+ trace_urandom_read(8 * nbytes, ENTROPY_BITS(&nonblocking_pool),
+ ENTROPY_BITS(&input_pool));
+ return ret;
}
static unsigned int
@@ -1213,9 +1402,9 @@ random_poll(struct file *file, poll_table * wait)
poll_wait(file, &random_read_wait, wait);
poll_wait(file, &random_write_wait, wait);
mask = 0;
- if (input_pool.entropy_count >= random_read_wakeup_thresh)
+ if (ENTROPY_BITS(&input_pool) >= random_read_wakeup_bits)
mask |= POLLIN | POLLRDNORM;
- if (input_pool.entropy_count < random_write_wakeup_thresh)
+ if (ENTROPY_BITS(&input_pool) < random_write_wakeup_bits)
mask |= POLLOUT | POLLWRNORM;
return mask;
}
@@ -1266,7 +1455,8 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
switch (cmd) {
case RNDGETENTCNT:
/* inherently racy, no point locking */
- if (put_user(input_pool.entropy_count, p))
+ ent_count = ENTROPY_BITS(&input_pool);
+ if (put_user(ent_count, p))
return -EFAULT;
return 0;
case RNDADDTOENTCNT:
@@ -1274,7 +1464,7 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
return -EPERM;
if (get_user(ent_count, p))
return -EFAULT;
- credit_entropy_bits(&input_pool, ent_count);
+ credit_entropy_bits_safe(&input_pool, ent_count);
return 0;
case RNDADDENTROPY:
if (!capable(CAP_SYS_ADMIN))
@@ -1289,14 +1479,19 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
size);
if (retval < 0)
return retval;
- credit_entropy_bits(&input_pool, ent_count);
+ credit_entropy_bits_safe(&input_pool, ent_count);
return 0;
case RNDZAPENTCNT:
case RNDCLEARPOOL:
- /* Clear the entropy pool counters. */
+ /*
+ * Clear the entropy pool counters. We no longer clear
+ * the entropy pool, as that's silly.
+ */
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- rand_initialize();
+ input_pool.entropy_count = 0;
+ nonblocking_pool.entropy_count = 0;
+ blocking_pool.entropy_count = 0;
return 0;
default:
return -EINVAL;
@@ -1356,23 +1551,23 @@ EXPORT_SYMBOL(generate_random_uuid);
#include <linux/sysctl.h>
static int min_read_thresh = 8, min_write_thresh;
-static int max_read_thresh = INPUT_POOL_WORDS * 32;
+static int max_read_thresh = OUTPUT_POOL_WORDS * 32;
static int max_write_thresh = INPUT_POOL_WORDS * 32;
static char sysctl_bootid[16];
/*
- * These functions is used to return both the bootid UUID, and random
+ * This function is used to return both the bootid UUID, and random
* UUID. The difference is in whether table->data is NULL; if it is,
* then a new UUID is generated and returned to the user.
*
- * If the user accesses this via the proc interface, it will be returned
- * as an ASCII string in the standard UUID format. If accesses via the
- * sysctl system call, it is returned as 16 bytes of binary data.
+ * If the user accesses this via the proc interface, the UUID will be
+ * returned as an ASCII string in the standard UUID format; if via the
+ * sysctl system call, as 16 bytes of binary data.
*/
-static int proc_do_uuid(ctl_table *table, int write,
+static int proc_do_uuid(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
- ctl_table fake_table;
+ struct ctl_table fake_table;
unsigned char buf[64], tmp_uuid[16], *uuid;
uuid = table->data;
@@ -1396,9 +1591,26 @@ static int proc_do_uuid(ctl_table *table, int write,
return proc_dostring(&fake_table, write, buffer, lenp, ppos);
}
+/*
+ * Return entropy available scaled to integral bits
+ */
+static int proc_do_entropy(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ struct ctl_table fake_table;
+ int entropy_count;
+
+ entropy_count = *(int *)table->data >> ENTROPY_SHIFT;
+
+ fake_table.data = &entropy_count;
+ fake_table.maxlen = sizeof(entropy_count);
+
+ return proc_dointvec(&fake_table, write, buffer, lenp, ppos);
+}
+
static int sysctl_poolsize = INPUT_POOL_WORDS * 32;
-extern ctl_table random_table[];
-ctl_table random_table[] = {
+extern struct ctl_table random_table[];
+struct ctl_table random_table[] = {
{
.procname = "poolsize",
.data = &sysctl_poolsize,
@@ -1410,12 +1622,12 @@ ctl_table random_table[] = {
.procname = "entropy_avail",
.maxlen = sizeof(int),
.mode = 0444,
- .proc_handler = proc_dointvec,
+ .proc_handler = proc_do_entropy,
.data = &input_pool.entropy_count,
},
{
.procname = "read_wakeup_threshold",
- .data = &random_read_wakeup_thresh,
+ .data = &random_read_wakeup_bits,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
@@ -1424,7 +1636,7 @@ ctl_table random_table[] = {
},
{
.procname = "write_wakeup_threshold",
- .data = &random_write_wakeup_thresh,
+ .data = &random_write_wakeup_bits,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
@@ -1432,6 +1644,13 @@ ctl_table random_table[] = {
.extra2 = &max_write_thresh,
},
{
+ .procname = "urandom_min_reseed_secs",
+ .data = &random_min_urandom_seed,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
.procname = "boot_id",
.data = &sysctl_bootid,
.maxlen = 16,
@@ -1450,12 +1669,11 @@ ctl_table random_table[] = {
static u32 random_int_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned;
-static int __init random_int_secret_init(void)
+int random_int_secret_init(void)
{
get_random_bytes(random_int_secret, sizeof(random_int_secret));
return 0;
}
-late_initcall(random_int_secret_init);
/*
* Get a random word for internal kernel use only. Similar to urandom but
@@ -1474,13 +1692,14 @@ unsigned int get_random_int(void)
hash = get_cpu_var(get_random_int_hash);
- hash[0] += current->pid + jiffies + get_cycles();
+ hash[0] += current->pid + jiffies + random_get_entropy();
md5_transform(hash, random_int_secret);
ret = hash[0];
put_cpu_var(get_random_int_hash);
return ret;
}
+EXPORT_SYMBOL(get_random_int);
/*
* randomize_range() returns a start address such that
diff --git a/drivers/char/raw.c b/drivers/char/raw.c
index 54a3a6d0981..0102dc78860 100644
--- a/drivers/char/raw.c
+++ b/drivers/char/raw.c
@@ -80,7 +80,7 @@ static int raw_open(struct inode *inode, struct file *filp)
filp->f_flags |= O_DIRECT;
filp->f_mapping = bdev->bd_inode->i_mapping;
if (++raw_devices[minor].inuse == 1)
- filp->f_path.dentry->d_inode->i_mapping =
+ file_inode(filp)->i_mapping =
bdev->bd_inode->i_mapping;
filp->private_data = bdev;
mutex_unlock(&raw_mutex);
@@ -190,7 +190,7 @@ static int bind_get(int number, dev_t *dev)
struct raw_device_data *rawdev;
struct block_device *bdev;
- if (number <= 0 || number >= MAX_RAW_MINORS)
+ if (number <= 0 || number >= max_raw_minors)
return -EINVAL;
rawdev = &raw_devices[number];
@@ -284,10 +284,10 @@ static long raw_ctl_compat_ioctl(struct file *file, unsigned int cmd,
#endif
static const struct file_operations raw_fops = {
- .read = do_sync_read,
- .aio_read = generic_file_aio_read,
- .write = do_sync_write,
- .aio_write = blkdev_aio_write,
+ .read = new_sync_read,
+ .read_iter = generic_file_read_iter,
+ .write = new_sync_write,
+ .write_iter = blkdev_write_iter,
.fsync = blkdev_fsync,
.open = raw_open,
.release = raw_release,
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index 91470fdbab2..35259961cc3 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -227,7 +227,7 @@ static inline unsigned char rtc_is_updating(void)
#ifdef RTC_IRQ
/*
- * A very tiny interrupt handler. It runs with IRQF_DISABLED set,
+ * A very tiny interrupt handler. It runs with interrupts disabled,
* but there is possibility of conflicting with the set_rtc_mmss()
* call (the rtc irq and the timer irq can easily run at the same
* time in two different CPUs). So we need to serialize
@@ -280,7 +280,7 @@ static irqreturn_t rtc_interrupt(int irq, void *dev_id)
/*
* sysctl-tuning infrastructure.
*/
-static ctl_table rtc_table[] = {
+static struct ctl_table rtc_table[] = {
{
.procname = "max-user-freq",
.data = &rtc_max_user_freq,
@@ -291,7 +291,7 @@ static ctl_table rtc_table[] = {
{ }
};
-static ctl_table rtc_root[] = {
+static struct ctl_table rtc_root[] = {
{
.procname = "rtc",
.mode = 0555,
@@ -300,7 +300,7 @@ static ctl_table rtc_root[] = {
{ }
};
-static ctl_table dev_root[] = {
+static struct ctl_table dev_root[] = {
{
.procname = "dev",
.mode = 0555,
@@ -1040,8 +1040,7 @@ no_irq:
rtc_int_handler_ptr = rtc_interrupt;
}
- if (request_irq(RTC_IRQ, rtc_int_handler_ptr, IRQF_DISABLED,
- "rtc", NULL)) {
+ if (request_irq(RTC_IRQ, rtc_int_handler_ptr, 0, "rtc", NULL)) {
/* Yeah right, seeing as irq 8 doesn't even hit the bus. */
rtc_has_irq = 0;
printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ);
diff --git a/drivers/char/snsc.c b/drivers/char/snsc.c
index 5816b39ff5a..8bab59292a0 100644
--- a/drivers/char/snsc.c
+++ b/drivers/char/snsc.c
@@ -108,8 +108,7 @@ scdrv_open(struct inode *inode, struct file *file)
/* hook this subchannel up to the system controller interrupt */
mutex_lock(&scdrv_mutex);
rv = request_irq(SGI_UART_VECTOR, scdrv_interrupt,
- IRQF_SHARED | IRQF_DISABLED,
- SYSCTL_BASENAME, sd);
+ IRQF_SHARED, SYSCTL_BASENAME, sd);
if (rv) {
ia64_sn_irtr_close(sd->sd_nasid, sd->sd_subch);
kfree(sd);
diff --git a/drivers/char/snsc_event.c b/drivers/char/snsc_event.c
index ee156948b9f..59bcefd6ec7 100644
--- a/drivers/char/snsc_event.c
+++ b/drivers/char/snsc_event.c
@@ -292,8 +292,7 @@ scdrv_event_init(struct sysctl_data_s *scd)
/* hook event subchannel up to the system controller interrupt */
rv = request_irq(SGI_UART_VECTOR, scdrv_event_interrupt,
- IRQF_SHARED | IRQF_DISABLED,
- "system controller events", event_sd);
+ IRQF_SHARED, "system controller events", event_sd);
if (rv) {
printk(KERN_WARNING "%s: irq request failed (%d)\n",
__func__, rv);
diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c
index d780295a147..7cc1fe2241f 100644
--- a/drivers/char/sonypi.c
+++ b/drivers/char/sonypi.c
@@ -876,11 +876,6 @@ found:
if (useinput)
sonypi_report_input_event(event);
-#ifdef CONFIG_ACPI
- if (sonypi_acpi_device)
- acpi_bus_generate_proc_event(sonypi_acpi_device, 1, event);
-#endif
-
kfifo_in_locked(&sonypi_device.fifo, (unsigned char *)&event,
sizeof(event), &sonypi_device.fifo_lock);
kill_fasync(&sonypi_device.fifo_async, SIGIO, POLL_IN);
@@ -938,7 +933,7 @@ static ssize_t sonypi_misc_read(struct file *file, char __user *buf,
}
if (ret > 0) {
- struct inode *inode = file->f_path.dentry->d_inode;
+ struct inode *inode = file_inode(file);
inode->i_atime = current_fs_time(inode->i_sb);
}
@@ -1142,7 +1137,7 @@ static int sonypi_acpi_add(struct acpi_device *device)
return 0;
}
-static int sonypi_acpi_remove(struct acpi_device *device, int type)
+static int sonypi_acpi_remove(struct acpi_device *device)
{
sonypi_acpi_device = NULL;
return 0;
diff --git a/drivers/char/tb0219.c b/drivers/char/tb0219.c
index 34c63f85104..47b9fdfcf08 100644
--- a/drivers/char/tb0219.c
+++ b/drivers/char/tb0219.c
@@ -164,7 +164,7 @@ static ssize_t tanbac_tb0219_read(struct file *file, char __user *buf, size_t le
unsigned int minor;
char value;
- minor = iminor(file->f_path.dentry->d_inode);
+ minor = iminor(file_inode(file));
switch (minor) {
case 0:
value = get_led();
@@ -200,7 +200,7 @@ static ssize_t tanbac_tb0219_write(struct file *file, const char __user *data,
int retval = 0;
char c;
- minor = iminor(file->f_path.dentry->d_inode);
+ minor = iminor(file_inode(file));
switch (minor) {
case 0:
type = TYPE_LED;
diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c
index 3b22a606f79..bd377472dcf 100644
--- a/drivers/char/tile-srom.c
+++ b/drivers/char/tile-srom.c
@@ -20,7 +20,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
@@ -273,61 +272,43 @@ static ssize_t srom_write(struct file *filp, const char __user *buf,
}
/* Provide our own implementation so we can use srom->total_size. */
-loff_t srom_llseek(struct file *filp, loff_t offset, int origin)
+loff_t srom_llseek(struct file *file, loff_t offset, int origin)
{
- struct srom_dev *srom = filp->private_data;
-
- if (mutex_lock_interruptible(&srom->lock))
- return -ERESTARTSYS;
-
- switch (origin) {
- case SEEK_END:
- offset += srom->total_size;
- break;
- case SEEK_CUR:
- offset += filp->f_pos;
- break;
- }
-
- if (offset < 0 || offset > srom->total_size) {
- offset = -EINVAL;
- } else {
- filp->f_pos = offset;
- filp->f_version = 0;
- }
-
- mutex_unlock(&srom->lock);
-
- return offset;
+ struct srom_dev *srom = file->private_data;
+ return fixed_size_llseek(file, offset, origin, srom->total_size);
}
-static ssize_t total_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t total_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct srom_dev *srom = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", srom->total_size);
}
+static DEVICE_ATTR_RO(total_size);
-static ssize_t sector_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t sector_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct srom_dev *srom = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", srom->sector_size);
}
+static DEVICE_ATTR_RO(sector_size);
-static ssize_t page_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t page_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct srom_dev *srom = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", srom->page_size);
}
+static DEVICE_ATTR_RO(page_size);
-static struct device_attribute srom_dev_attrs[] = {
- __ATTR(total_size, S_IRUGO, total_show, NULL),
- __ATTR(sector_size, S_IRUGO, sector_show, NULL),
- __ATTR(page_size, S_IRUGO, page_show, NULL),
- __ATTR_NULL
+static struct attribute *srom_dev_attrs[] = {
+ &dev_attr_total_size.attr,
+ &dev_attr_sector_size.attr,
+ &dev_attr_page_size.attr,
+ NULL,
};
+ATTRIBUTE_GROUPS(srom_dev);
static char *srom_devnode(struct device *dev, umode_t *mode)
{
@@ -371,7 +352,7 @@ static int srom_setup_minor(struct srom_dev *srom, int index)
dev = device_create(srom_class, &platform_bus,
MKDEV(srom_major, index), srom, "%d", index);
- return IS_ERR(dev) ? PTR_ERR(dev) : 0;
+ return PTR_ERR_OR_ZERO(dev);
}
/** srom_init() - Initialize the driver's module. */
@@ -440,7 +421,7 @@ static int srom_init(void)
result = PTR_ERR(srom_class);
goto fail_cdev;
}
- srom_class->dev_attrs = srom_dev_attrs;
+ srom_class->dev_groups = srom_dev_groups;
srom_class->devnode = srom_devnode;
/* Do per-partition initialization */
diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c
index e95e0ab0bd8..100cd1de993 100644
--- a/drivers/char/tlclk.c
+++ b/drivers/char/tlclk.c
@@ -222,7 +222,7 @@ static int tlclk_open(struct inode *inode, struct file *filp)
/* This device is wired through the FPGA IO space of the ATCA blade
* we can't share this IRQ */
result = request_irq(telclk_interrupt, &tlclk_interrupt,
- IRQF_DISABLED, "telco_clock", tlclk_interrupt);
+ 0, "telco_clock", tlclk_interrupt);
if (result == -EBUSY)
printk(KERN_ERR "tlclk: Interrupt can't be reserved.\n");
else
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index 915875e431d..c54cac3f8bc 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -33,6 +33,15 @@ config TCG_TIS
from within Linux. To compile this driver as a module, choose
M here; the module will be called tpm_tis.
+config TCG_TIS_I2C_ATMEL
+ tristate "TPM Interface Specification 1.2 Interface (I2C - Atmel)"
+ depends on I2C
+ ---help---
+ If you have an Atmel I2C TPM security chip say Yes and it will be
+ accessible from within Linux.
+ To compile this driver as a module, choose M here; the module will
+ be called tpm_tis_i2c_atmel.
+
config TCG_TIS_I2C_INFINEON
tristate "TPM Interface Specification 1.2 Interface (I2C - Infineon)"
depends on I2C
@@ -42,7 +51,17 @@ config TCG_TIS_I2C_INFINEON
Specification 0.20 say Yes and it will be accessible from within
Linux.
To compile this driver as a module, choose M here; the module
- will be called tpm_tis_i2c_infineon.
+ will be called tpm_i2c_infineon.
+
+config TCG_TIS_I2C_NUVOTON
+ tristate "TPM Interface Specification 1.2 Interface (I2C - Nuvoton)"
+ depends on I2C
+ ---help---
+ If you have a TPM security chip with an I2C interface from
+ Nuvoton Technology Corp. say Yes and it will be accessible
+ from within Linux.
+ To compile this driver as a module, choose M here; the module
+ will be called tpm_i2c_nuvoton.
config TCG_NSC
tristate "National Semiconductor TPM Interface"
@@ -55,7 +74,7 @@ config TCG_NSC
config TCG_ATMEL
tristate "Atmel TPM Interface"
- depends on PPC64 || HAS_IOPORT
+ depends on PPC64 || HAS_IOPORT_MAP
---help---
If you have a TPM security chip from Atmel say Yes and it
will be accessible from within Linux. To compile this driver
@@ -75,10 +94,32 @@ config TCG_INFINEON
config TCG_IBMVTPM
tristate "IBM VTPM Interface"
- depends on PPC64
+ depends on PPC_PSERIES
---help---
If you have IBM virtual TPM (VTPM) support say Yes and it
will be accessible from within Linux. To compile this driver
as a module, choose M here; the module will be called tpm_ibmvtpm.
+config TCG_ST33_I2C
+ tristate "STMicroelectronics ST33 I2C TPM"
+ depends on I2C
+ depends on GPIOLIB
+ ---help---
+ If you have a TPM security chip from STMicroelectronics working with
+ an I2C bus say Yes and it will be accessible from within Linux.
+ To compile this driver as a module, choose M here; the module will be
+ called tpm_stm_st33_i2c.
+
+config TCG_XEN
+ tristate "XEN TPM Interface"
+ depends on TCG_TPM && XEN
+ select XEN_XENBUS_FRONTEND
+ ---help---
+ If you want to make TPM support available to a Xen user domain,
+ say Yes and it will be accessible from within Linux. See
+ the manpages for xl, xl.conf, and docs/misc/vtpm.txt in
+ the Xen source repository for more details.
+ To compile this driver as a module, choose M here; the module
+ will be called xen-tpmfront.
+
endif # TCG_TPM
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 5b3fc8bc6c1..4d85dd681b8 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -2,18 +2,23 @@
# Makefile for the kernel tpm device drivers.
#
obj-$(CONFIG_TCG_TPM) += tpm.o
+tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o
+tpm-$(CONFIG_ACPI) += tpm_ppi.o
+
ifdef CONFIG_ACPI
- obj-$(CONFIG_TCG_TPM) += tpm_bios.o
- tpm_bios-objs += tpm_eventlog.o tpm_acpi.o tpm_ppi.o
+ tpm-y += tpm_eventlog.o tpm_acpi.o
else
ifdef CONFIG_TCG_IBMVTPM
- obj-$(CONFIG_TCG_TPM) += tpm_bios.o
- tpm_bios-objs += tpm_eventlog.o tpm_of.o
+ tpm-y += tpm_eventlog.o tpm_of.o
endif
endif
obj-$(CONFIG_TCG_TIS) += tpm_tis.o
+obj-$(CONFIG_TCG_TIS_I2C_ATMEL) += tpm_i2c_atmel.o
obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
+obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o
obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
+obj-$(CONFIG_TCG_ST33_I2C) += tpm_i2c_stm_st33.o
+obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o
diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
new file mode 100644
index 00000000000..d9b774e02a1
--- /dev/null
+++ b/drivers/char/tpm/tpm-dev.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Copyright (C) 2013 Obsidian Research Corp
+ * Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
+ *
+ * Device file system interface to the TPM
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ */
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include "tpm.h"
+
+struct file_priv {
+ struct tpm_chip *chip;
+
+ /* Data passed to and from the tpm via the read/write calls */
+ atomic_t data_pending;
+ struct mutex buffer_mutex;
+
+ struct timer_list user_read_timer; /* user needs to claim result */
+ struct work_struct work;
+
+ u8 data_buffer[TPM_BUFSIZE];
+};
+
+static void user_reader_timeout(unsigned long ptr)
+{
+ struct file_priv *priv = (struct file_priv *)ptr;
+
+ schedule_work(&priv->work);
+}
+
+static void timeout_work(struct work_struct *work)
+{
+ struct file_priv *priv = container_of(work, struct file_priv, work);
+
+ mutex_lock(&priv->buffer_mutex);
+ atomic_set(&priv->data_pending, 0);
+ memset(priv->data_buffer, 0, sizeof(priv->data_buffer));
+ mutex_unlock(&priv->buffer_mutex);
+}
+
+static int tpm_open(struct inode *inode, struct file *file)
+{
+ struct miscdevice *misc = file->private_data;
+ struct tpm_chip *chip = container_of(misc, struct tpm_chip,
+ vendor.miscdev);
+ struct file_priv *priv;
+
+ /* It's assured that the chip will be opened just once,
+ * by the check of is_open variable, which is protected
+ * by driver_lock. */
+ if (test_and_set_bit(0, &chip->is_open)) {
+ dev_dbg(chip->dev, "Another process owns this TPM\n");
+ return -EBUSY;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL) {
+ clear_bit(0, &chip->is_open);
+ return -ENOMEM;
+ }
+
+ priv->chip = chip;
+ atomic_set(&priv->data_pending, 0);
+ mutex_init(&priv->buffer_mutex);
+ setup_timer(&priv->user_read_timer, user_reader_timeout,
+ (unsigned long)priv);
+ INIT_WORK(&priv->work, timeout_work);
+
+ file->private_data = priv;
+ get_device(chip->dev);
+ return 0;
+}
+
+static ssize_t tpm_read(struct file *file, char __user *buf,
+ size_t size, loff_t *off)
+{
+ struct file_priv *priv = file->private_data;
+ ssize_t ret_size;
+ int rc;
+
+ del_singleshot_timer_sync(&priv->user_read_timer);
+ flush_work(&priv->work);
+ ret_size = atomic_read(&priv->data_pending);
+ if (ret_size > 0) { /* relay data */
+ ssize_t orig_ret_size = ret_size;
+ if (size < ret_size)
+ ret_size = size;
+
+ mutex_lock(&priv->buffer_mutex);
+ rc = copy_to_user(buf, priv->data_buffer, ret_size);
+ memset(priv->data_buffer, 0, orig_ret_size);
+ if (rc)
+ ret_size = -EFAULT;
+
+ mutex_unlock(&priv->buffer_mutex);
+ }
+
+ atomic_set(&priv->data_pending, 0);
+
+ return ret_size;
+}
+
+static ssize_t tpm_write(struct file *file, const char __user *buf,
+ size_t size, loff_t *off)
+{
+ struct file_priv *priv = file->private_data;
+ size_t in_size = size;
+ ssize_t out_size;
+
+ /* cannot perform a write until the read has cleared
+ either via tpm_read or a user_read_timer timeout.
+ This also prevents splitted buffered writes from blocking here.
+ */
+ if (atomic_read(&priv->data_pending) != 0)
+ return -EBUSY;
+
+ if (in_size > TPM_BUFSIZE)
+ return -E2BIG;
+
+ mutex_lock(&priv->buffer_mutex);
+
+ if (copy_from_user
+ (priv->data_buffer, (void __user *) buf, in_size)) {
+ mutex_unlock(&priv->buffer_mutex);
+ return -EFAULT;
+ }
+
+ /* atomic tpm command send and result receive */
+ out_size = tpm_transmit(priv->chip, priv->data_buffer,
+ sizeof(priv->data_buffer));
+ if (out_size < 0) {
+ mutex_unlock(&priv->buffer_mutex);
+ return out_size;
+ }
+
+ atomic_set(&priv->data_pending, out_size);
+ mutex_unlock(&priv->buffer_mutex);
+
+ /* Set a timeout by which the reader must come claim the result */
+ mod_timer(&priv->user_read_timer, jiffies + (60 * HZ));
+
+ return in_size;
+}
+
+/*
+ * Called on file close
+ */
+static int tpm_release(struct inode *inode, struct file *file)
+{
+ struct file_priv *priv = file->private_data;
+
+ del_singleshot_timer_sync(&priv->user_read_timer);
+ flush_work(&priv->work);
+ file->private_data = NULL;
+ atomic_set(&priv->data_pending, 0);
+ clear_bit(0, &priv->chip->is_open);
+ put_device(priv->chip->dev);
+ kfree(priv);
+ return 0;
+}
+
+static const struct file_operations tpm_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .open = tpm_open,
+ .read = tpm_read,
+ .write = tpm_write,
+ .release = tpm_release,
+};
+
+int tpm_dev_add_device(struct tpm_chip *chip)
+{
+ int rc;
+
+ chip->vendor.miscdev.fops = &tpm_fops;
+ if (chip->dev_num == 0)
+ chip->vendor.miscdev.minor = TPM_MINOR;
+ else
+ chip->vendor.miscdev.minor = MISC_DYNAMIC_MINOR;
+
+ chip->vendor.miscdev.name = chip->devname;
+ chip->vendor.miscdev.parent = chip->dev;
+
+ rc = misc_register(&chip->vendor.miscdev);
+ if (rc) {
+ chip->vendor.miscdev.name = NULL;
+ dev_err(chip->dev,
+ "unable to misc_register %s, minor %d err=%d\n",
+ chip->vendor.miscdev.name,
+ chip->vendor.miscdev.minor, rc);
+ }
+ return rc;
+}
+
+void tpm_dev_del_device(struct tpm_chip *chip)
+{
+ if (chip->vendor.miscdev.name)
+ misc_deregister(&chip->vendor.miscdev);
+}
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm-interface.c
index 93211df52aa..62e10fd1e1c 100644
--- a/drivers/char/tpm/tpm.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -10,13 +10,13 @@
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
*
* Device driver for TCG/TCPA TPM (trusted platform module).
- * Specifications at www.trustedcomputinggroup.org
+ * Specifications at www.trustedcomputinggroup.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2 of the
* License.
- *
+ *
* Note, the TPM chip is not interrupt driven (only polling)
* and can have very long timeouts (minutes!). Hence the unusual
* calls to msleep.
@@ -32,16 +32,10 @@
#include "tpm.h"
#include "tpm_eventlog.h"
-enum tpm_duration {
- TPM_SHORT = 0,
- TPM_MEDIUM = 1,
- TPM_LONG = 2,
- TPM_UNDEFINED,
-};
-
#define TPM_MAX_ORDINAL 243
-#define TPM_MAX_PROTECTED_ORDINAL 12
-#define TPM_PROTECTED_ORDINAL_MASK 0xFF
+#define TSC_MAX_ORDINAL 12
+#define TPM_PROTECTED_COMMAND 0x00
+#define TPM_CONNECTION_COMMAND 0x40
/*
* Bug workaround - some TPM's don't flush the most
@@ -65,21 +59,6 @@ static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);
* values of the SHORT, MEDIUM, and LONG durations are retrieved
* from the chip during initialization with a call to tpm_get_timeouts.
*/
-static const u8 tpm_protected_ordinal_duration[TPM_MAX_PROTECTED_ORDINAL] = {
- TPM_UNDEFINED, /* 0 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED, /* 5 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_SHORT, /* 10 */
- TPM_SHORT,
-};
-
static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = {
TPM_UNDEFINED, /* 0 */
TPM_UNDEFINED,
@@ -326,23 +305,6 @@ static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = {
TPM_MEDIUM,
};
-static void user_reader_timeout(unsigned long ptr)
-{
- struct tpm_chip *chip = (struct tpm_chip *) ptr;
-
- schedule_work(&chip->work);
-}
-
-static void timeout_work(struct work_struct *work)
-{
- struct tpm_chip *chip = container_of(work, struct tpm_chip, work);
-
- mutex_lock(&chip->buffer_mutex);
- atomic_set(&chip->data_pending, 0);
- memset(chip->data_buffer, 0, TPM_BUFSIZE);
- mutex_unlock(&chip->buffer_mutex);
-}
-
/*
* Returns max number of jiffies to wait
*/
@@ -351,14 +313,11 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip,
{
int duration_idx = TPM_UNDEFINED;
int duration = 0;
+ u8 category = (ordinal >> 24) & 0xFF;
- if (ordinal < TPM_MAX_ORDINAL)
+ if ((category == TPM_PROTECTED_COMMAND && ordinal < TPM_MAX_ORDINAL) ||
+ (category == TPM_CONNECTION_COMMAND && ordinal < TSC_MAX_ORDINAL))
duration_idx = tpm_ordinal_duration[ordinal];
- else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) <
- TPM_MAX_PROTECTED_ORDINAL)
- duration_idx =
- tpm_protected_ordinal_duration[ordinal &
- TPM_PROTECTED_ORDINAL_MASK];
if (duration_idx != TPM_UNDEFINED)
duration = chip->vendor.duration[duration_idx];
@@ -372,8 +331,8 @@ EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
/*
* Internal kernel interface to transmit TPM commands
*/
-static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
- size_t bufsiz)
+ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
+ size_t bufsiz)
{
ssize_t rc;
u32 count, ordinal;
@@ -388,13 +347,14 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
return -ENODATA;
if (count > bufsiz) {
dev_err(chip->dev,
- "invalid count value %x %zx \n", count, bufsiz);
+ "invalid count value %x %zx\n", count, bufsiz);
return -E2BIG;
}
mutex_lock(&chip->tpm_mutex);
- if ((rc = chip->vendor.send(chip, (u8 *) buf, count)) < 0) {
+ rc = chip->ops->send(chip, (u8 *) buf, count);
+ if (rc < 0) {
dev_err(chip->dev,
"tpm_transmit: tpm_send: error %zd\n", rc);
goto out;
@@ -405,12 +365,12 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
do {
- u8 status = chip->vendor.status(chip);
- if ((status & chip->vendor.req_complete_mask) ==
- chip->vendor.req_complete_val)
+ u8 status = chip->ops->status(chip);
+ if ((status & chip->ops->req_complete_mask) ==
+ chip->ops->req_complete_val)
goto out_recv;
- if ((status == chip->vendor.req_canceled)) {
+ if (chip->ops->req_canceled(chip, status)) {
dev_err(chip->dev, "Operation Canceled\n");
rc = -ECANCELED;
goto out;
@@ -420,13 +380,13 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
rmb();
} while (time_before(jiffies, stop));
- chip->vendor.cancel(chip);
+ chip->ops->cancel(chip);
dev_err(chip->dev, "Operation Timed out\n");
rc = -ETIME;
goto out;
out_recv:
- rc = chip->vendor.recv(chip, (u8 *) buf, bufsiz);
+ rc = chip->ops->recv(chip, (u8 *) buf, bufsiz);
if (rc < 0)
dev_err(chip->dev,
"tpm_transmit: tpm_recv: error %zd\n", rc);
@@ -438,44 +398,25 @@ out:
#define TPM_DIGEST_SIZE 20
#define TPM_RET_CODE_IDX 6
-enum tpm_capabilities {
- TPM_CAP_FLAG = cpu_to_be32(4),
- TPM_CAP_PROP = cpu_to_be32(5),
- CAP_VERSION_1_1 = cpu_to_be32(0x06),
- CAP_VERSION_1_2 = cpu_to_be32(0x1A)
-};
-
-enum tpm_sub_capabilities {
- TPM_CAP_PROP_PCR = cpu_to_be32(0x101),
- TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103),
- TPM_CAP_FLAG_PERM = cpu_to_be32(0x108),
- TPM_CAP_FLAG_VOL = cpu_to_be32(0x109),
- TPM_CAP_PROP_OWNER = cpu_to_be32(0x111),
- TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115),
- TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120),
-
-};
-
static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
int len, const char *desc)
{
int err;
- len = tpm_transmit(chip,(u8 *) cmd, len);
+ len = tpm_transmit(chip, (u8 *) cmd, len);
if (len < 0)
return len;
else if (len < TPM_HEADER_SIZE)
return -EFAULT;
err = be32_to_cpu(cmd->header.out.return_code);
- if (err != 0)
+ if (err != 0 && desc)
dev_err(chip->dev, "A TPM error (%d) occurred %s\n", err, desc);
return err;
}
#define TPM_INTERNAL_RESULT_SIZE 200
-#define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
#define TPM_ORD_GET_CAP cpu_to_be32(101)
#define TPM_ORD_GET_RANDOM cpu_to_be32(70)
@@ -528,6 +469,25 @@ void tpm_gen_interrupt(struct tpm_chip *chip)
}
EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
+#define TPM_ORD_STARTUP cpu_to_be32(153)
+#define TPM_ST_CLEAR cpu_to_be16(1)
+#define TPM_ST_STATE cpu_to_be16(2)
+#define TPM_ST_DEACTIVATED cpu_to_be16(3)
+static const struct tpm_input_header tpm_startup_header = {
+ .tag = TPM_TAG_RQU_COMMAND,
+ .length = cpu_to_be32(12),
+ .ordinal = TPM_ORD_STARTUP
+};
+
+static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
+{
+ struct tpm_cmd_t start_cmd;
+ start_cmd.header.in = tpm_startup_header;
+ start_cmd.params.startup_in.startup_type = startup_type;
+ return transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE,
+ "attempting to start the TPM");
+}
+
int tpm_get_timeouts(struct tpm_chip *chip)
{
struct tpm_cmd_t tpm_cmd;
@@ -541,11 +501,28 @@ int tpm_get_timeouts(struct tpm_chip *chip)
tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
+ rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
- rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
- "attempting to determine the timeouts");
- if (rc)
+ if (rc == TPM_ERR_INVALID_POSTINIT) {
+ /* The TPM is not started, we are the first to talk to it.
+ Execute a startup command. */
+ dev_info(chip->dev, "Issuing TPM_STARTUP");
+ if (tpm_startup(chip, TPM_ST_CLEAR))
+ return rc;
+
+ tpm_cmd.header.in = tpm_getcap_header;
+ tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+ tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
+ rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
+ NULL);
+ }
+ if (rc) {
+ dev_err(chip->dev,
+ "A TPM error (%zd) occurred attempting to determine the timeouts\n",
+ rc);
goto duration;
+ }
if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
be32_to_cpu(tpm_cmd.header.out.length)
@@ -639,70 +616,6 @@ static int tpm_continue_selftest(struct tpm_chip *chip)
return rc;
}
-ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
- char *buf)
-{
- cap_t cap;
- ssize_t rc;
-
- rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
- "attempting to determine the permanent enabled state");
- if (rc)
- return 0;
-
- rc = sprintf(buf, "%d\n", !cap.perm_flags.disable);
- return rc;
-}
-EXPORT_SYMBOL_GPL(tpm_show_enabled);
-
-ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr,
- char *buf)
-{
- cap_t cap;
- ssize_t rc;
-
- rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
- "attempting to determine the permanent active state");
- if (rc)
- return 0;
-
- rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated);
- return rc;
-}
-EXPORT_SYMBOL_GPL(tpm_show_active);
-
-ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr,
- char *buf)
-{
- cap_t cap;
- ssize_t rc;
-
- rc = tpm_getcap(dev, TPM_CAP_PROP_OWNER, &cap,
- "attempting to determine the owner state");
- if (rc)
- return 0;
-
- rc = sprintf(buf, "%d\n", cap.owned);
- return rc;
-}
-EXPORT_SYMBOL_GPL(tpm_show_owned);
-
-ssize_t tpm_show_temp_deactivated(struct device * dev,
- struct device_attribute * attr, char *buf)
-{
- cap_t cap;
- ssize_t rc;
-
- rc = tpm_getcap(dev, TPM_CAP_FLAG_VOL, &cap,
- "attempting to determine the temporary state");
- if (rc)
- return 0;
-
- rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated);
- return rc;
-}
-EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated);
-
/*
* tpm_chip_find_get - return tpm_chip for given chip number
*/
@@ -732,7 +645,7 @@ static struct tpm_input_header pcrread_header = {
.ordinal = TPM_ORDINAL_PCRREAD
};
-static int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
+int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
{
int rc;
struct tpm_cmd_t cmd;
@@ -750,10 +663,10 @@ static int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
/**
* tpm_pcr_read - read a pcr value
- * @chip_num: tpm idx # or ANY
+ * @chip_num: tpm idx # or ANY
* @pcr_idx: pcr idx to retrieve
- * @res_buf: TPM_PCR value
- * size of res_buf is 20 bytes (or NULL if you don't care)
+ * @res_buf: TPM_PCR value
+ * size of res_buf is 20 bytes (or NULL if you don't care)
*
* The TPM driver should be built-in, but for whatever reason it
* isn't, protect against the chip disappearing, by incrementing
@@ -767,7 +680,7 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf)
chip = tpm_chip_find_get(chip_num);
if (chip == NULL)
return -ENODEV;
- rc = __tpm_pcr_read(chip, pcr_idx, res_buf);
+ rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf);
tpm_chip_put(chip);
return rc;
}
@@ -775,9 +688,9 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read);
/**
* tpm_pcr_extend - extend pcr value with hash
- * @chip_num: tpm idx # or AN&
+ * @chip_num: tpm idx # or AN&
* @pcr_idx: pcr idx to extend
- * @hash: hash value used to extend pcr value
+ * @hash: hash value used to extend pcr value
*
* The TPM driver should be built-in, but for whatever reason it
* isn't, protect against the chip disappearing, by incrementing
@@ -824,12 +737,11 @@ int tpm_do_selftest(struct tpm_chip *chip)
{
int rc;
unsigned int loops;
- unsigned int delay_msec = 1000;
+ unsigned int delay_msec = 100;
unsigned long duration;
struct tpm_cmd_t cmd;
- duration = tpm_calc_ordinal_duration(chip,
- TPM_ORD_CONTINUE_SELFTEST);
+ duration = tpm_calc_ordinal_duration(chip, TPM_ORD_CONTINUE_SELFTEST);
loops = jiffies_to_msecs(duration) / delay_msec;
@@ -845,6 +757,14 @@ int tpm_do_selftest(struct tpm_chip *chip)
cmd.header.in = pcrread_header;
cmd.params.pcrread_in.pcr_idx = cpu_to_be32(0);
rc = tpm_transmit(chip, (u8 *) &cmd, READ_PCR_RESULT_SIZE);
+ /* Some buggy TPMs will not respond to tpm_tis_ready() for
+ * around 300ms while the self test is ongoing, keep trying
+ * until the self test duration expires. */
+ if (rc == -ETIME) {
+ dev_info(chip->dev, HW_ERR "TPM command timed out during continue self test");
+ msleep(delay_msec);
+ continue;
+ }
if (rc < TPM_HEADER_SIZE)
return -EFAULT;
@@ -884,206 +804,31 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen)
}
EXPORT_SYMBOL_GPL(tpm_send);
-ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr,
- char *buf)
+static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask,
+ bool check_cancel, bool *canceled)
{
- cap_t cap;
- u8 digest[TPM_DIGEST_SIZE];
- ssize_t rc;
- int i, j, num_pcrs;
- char *str = buf;
- struct tpm_chip *chip = dev_get_drvdata(dev);
-
- rc = tpm_getcap(dev, TPM_CAP_PROP_PCR, &cap,
- "attempting to determine the number of PCRS");
- if (rc)
- return 0;
-
- num_pcrs = be32_to_cpu(cap.num_pcrs);
- for (i = 0; i < num_pcrs; i++) {
- rc = __tpm_pcr_read(chip, i, digest);
- if (rc)
- break;
- str += sprintf(str, "PCR-%02d: ", i);
- for (j = 0; j < TPM_DIGEST_SIZE; j++)
- str += sprintf(str, "%02X ", digest[j]);
- str += sprintf(str, "\n");
- }
- return str - buf;
-}
-EXPORT_SYMBOL_GPL(tpm_show_pcrs);
+ u8 status = chip->ops->status(chip);
-#define READ_PUBEK_RESULT_SIZE 314
-#define TPM_ORD_READPUBEK cpu_to_be32(124)
-static struct tpm_input_header tpm_readpubek_header = {
- .tag = TPM_TAG_RQU_COMMAND,
- .length = cpu_to_be32(30),
- .ordinal = TPM_ORD_READPUBEK
-};
-
-ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- u8 *data;
- struct tpm_cmd_t tpm_cmd;
- ssize_t err;
- int i, rc;
- char *str = buf;
-
- struct tpm_chip *chip = dev_get_drvdata(dev);
-
- tpm_cmd.header.in = tpm_readpubek_header;
- err = transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
- "attempting to read the PUBEK");
- if (err)
- goto out;
-
- /*
- ignore header 10 bytes
- algorithm 32 bits (1 == RSA )
- encscheme 16 bits
- sigscheme 16 bits
- parameters (RSA 12->bytes: keybit, #primes, expbit)
- keylenbytes 32 bits
- 256 byte modulus
- ignore checksum 20 bytes
- */
- data = tpm_cmd.params.readpubek_out_buffer;
- str +=
- sprintf(str,
- "Algorithm: %02X %02X %02X %02X\n"
- "Encscheme: %02X %02X\n"
- "Sigscheme: %02X %02X\n"
- "Parameters: %02X %02X %02X %02X "
- "%02X %02X %02X %02X "
- "%02X %02X %02X %02X\n"
- "Modulus length: %d\n"
- "Modulus:\n",
- data[0], data[1], data[2], data[3],
- data[4], data[5],
- data[6], data[7],
- data[12], data[13], data[14], data[15],
- data[16], data[17], data[18], data[19],
- data[20], data[21], data[22], data[23],
- be32_to_cpu(*((__be32 *) (data + 24))));
-
- for (i = 0; i < 256; i++) {
- str += sprintf(str, "%02X ", data[i + 28]);
- if ((i + 1) % 16 == 0)
- str += sprintf(str, "\n");
+ *canceled = false;
+ if ((status & mask) == mask)
+ return true;
+ if (check_cancel && chip->ops->req_canceled(chip, status)) {
+ *canceled = true;
+ return true;
}
-out:
- rc = str - buf;
- return rc;
-}
-EXPORT_SYMBOL_GPL(tpm_show_pubek);
-
-
-ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- cap_t cap;
- ssize_t rc;
- char *str = buf;
-
- rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
- "attempting to determine the manufacturer");
- if (rc)
- return 0;
- str += sprintf(str, "Manufacturer: 0x%x\n",
- be32_to_cpu(cap.manufacturer_id));
-
- rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap,
- "attempting to determine the 1.1 version");
- if (rc)
- return 0;
- str += sprintf(str,
- "TCG version: %d.%d\nFirmware version: %d.%d\n",
- cap.tpm_version.Major, cap.tpm_version.Minor,
- cap.tpm_version.revMajor, cap.tpm_version.revMinor);
- return str - buf;
-}
-EXPORT_SYMBOL_GPL(tpm_show_caps);
-
-ssize_t tpm_show_caps_1_2(struct device * dev,
- struct device_attribute * attr, char *buf)
-{
- cap_t cap;
- ssize_t rc;
- char *str = buf;
-
- rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
- "attempting to determine the manufacturer");
- if (rc)
- return 0;
- str += sprintf(str, "Manufacturer: 0x%x\n",
- be32_to_cpu(cap.manufacturer_id));
- rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap,
- "attempting to determine the 1.2 version");
- if (rc)
- return 0;
- str += sprintf(str,
- "TCG version: %d.%d\nFirmware version: %d.%d\n",
- cap.tpm_version_1_2.Major, cap.tpm_version_1_2.Minor,
- cap.tpm_version_1_2.revMajor,
- cap.tpm_version_1_2.revMinor);
- return str - buf;
-}
-EXPORT_SYMBOL_GPL(tpm_show_caps_1_2);
-
-ssize_t tpm_show_durations(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct tpm_chip *chip = dev_get_drvdata(dev);
-
- if (chip->vendor.duration[TPM_LONG] == 0)
- return 0;
-
- return sprintf(buf, "%d %d %d [%s]\n",
- jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
- jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
- jiffies_to_usecs(chip->vendor.duration[TPM_LONG]),
- chip->vendor.duration_adjusted
- ? "adjusted" : "original");
+ return false;
}
-EXPORT_SYMBOL_GPL(tpm_show_durations);
-
-ssize_t tpm_show_timeouts(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct tpm_chip *chip = dev_get_drvdata(dev);
-
- return sprintf(buf, "%d %d %d %d [%s]\n",
- jiffies_to_usecs(chip->vendor.timeout_a),
- jiffies_to_usecs(chip->vendor.timeout_b),
- jiffies_to_usecs(chip->vendor.timeout_c),
- jiffies_to_usecs(chip->vendor.timeout_d),
- chip->vendor.timeout_adjusted
- ? "adjusted" : "original");
-}
-EXPORT_SYMBOL_GPL(tpm_show_timeouts);
-
-ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct tpm_chip *chip = dev_get_drvdata(dev);
- if (chip == NULL)
- return 0;
-
- chip->vendor.cancel(chip);
- return count;
-}
-EXPORT_SYMBOL_GPL(tpm_store_cancel);
int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
- wait_queue_head_t *queue)
+ wait_queue_head_t *queue, bool check_cancel)
{
unsigned long stop;
long rc;
u8 status;
+ bool canceled = false;
/* check current status */
- status = chip->vendor.status(chip);
+ status = chip->ops->status(chip);
if ((status & mask) == mask)
return 0;
@@ -1095,11 +840,14 @@ again:
if ((long)timeout <= 0)
return -ETIME;
rc = wait_event_interruptible_timeout(*queue,
- ((chip->vendor.status(chip)
- & mask) == mask),
- timeout);
- if (rc > 0)
+ wait_for_tpm_stat_cond(chip, mask, check_cancel,
+ &canceled),
+ timeout);
+ if (rc > 0) {
+ if (canceled)
+ return -ECANCELED;
return 0;
+ }
if (rc == -ERESTARTSYS && freezing(current)) {
clear_thread_flag(TIF_SIGPENDING);
goto again;
@@ -1107,7 +855,7 @@ again:
} else {
do {
msleep(TPM_TIMEOUT);
- status = chip->vendor.status(chip);
+ status = chip->ops->status(chip);
if ((status & mask) == mask)
return 0;
} while (time_before(jiffies, stop));
@@ -1115,140 +863,6 @@ again:
return -ETIME;
}
EXPORT_SYMBOL_GPL(wait_for_tpm_stat);
-/*
- * Device file system interface to the TPM
- *
- * It's assured that the chip will be opened just once,
- * by the check of is_open variable, which is protected
- * by driver_lock.
- */
-int tpm_open(struct inode *inode, struct file *file)
-{
- int minor = iminor(inode);
- struct tpm_chip *chip = NULL, *pos;
-
- rcu_read_lock();
- list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
- if (pos->vendor.miscdev.minor == minor) {
- chip = pos;
- get_device(chip->dev);
- break;
- }
- }
- rcu_read_unlock();
-
- if (!chip)
- return -ENODEV;
-
- if (test_and_set_bit(0, &chip->is_open)) {
- dev_dbg(chip->dev, "Another process owns this TPM\n");
- put_device(chip->dev);
- return -EBUSY;
- }
-
- chip->data_buffer = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
- if (chip->data_buffer == NULL) {
- clear_bit(0, &chip->is_open);
- put_device(chip->dev);
- return -ENOMEM;
- }
-
- atomic_set(&chip->data_pending, 0);
-
- file->private_data = chip;
- return 0;
-}
-EXPORT_SYMBOL_GPL(tpm_open);
-
-/*
- * Called on file close
- */
-int tpm_release(struct inode *inode, struct file *file)
-{
- struct tpm_chip *chip = file->private_data;
-
- del_singleshot_timer_sync(&chip->user_read_timer);
- flush_work(&chip->work);
- file->private_data = NULL;
- atomic_set(&chip->data_pending, 0);
- kzfree(chip->data_buffer);
- clear_bit(0, &chip->is_open);
- put_device(chip->dev);
- return 0;
-}
-EXPORT_SYMBOL_GPL(tpm_release);
-
-ssize_t tpm_write(struct file *file, const char __user *buf,
- size_t size, loff_t *off)
-{
- struct tpm_chip *chip = file->private_data;
- size_t in_size = size;
- ssize_t out_size;
-
- /* cannot perform a write until the read has cleared
- either via tpm_read or a user_read_timer timeout.
- This also prevents splitted buffered writes from blocking here.
- */
- if (atomic_read(&chip->data_pending) != 0)
- return -EBUSY;
-
- if (in_size > TPM_BUFSIZE)
- return -E2BIG;
-
- mutex_lock(&chip->buffer_mutex);
-
- if (copy_from_user
- (chip->data_buffer, (void __user *) buf, in_size)) {
- mutex_unlock(&chip->buffer_mutex);
- return -EFAULT;
- }
-
- /* atomic tpm command send and result receive */
- out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE);
- if (out_size < 0) {
- mutex_unlock(&chip->buffer_mutex);
- return out_size;
- }
-
- atomic_set(&chip->data_pending, out_size);
- mutex_unlock(&chip->buffer_mutex);
-
- /* Set a timeout by which the reader must come claim the result */
- mod_timer(&chip->user_read_timer, jiffies + (60 * HZ));
-
- return in_size;
-}
-EXPORT_SYMBOL_GPL(tpm_write);
-
-ssize_t tpm_read(struct file *file, char __user *buf,
- size_t size, loff_t *off)
-{
- struct tpm_chip *chip = file->private_data;
- ssize_t ret_size;
- int rc;
-
- del_singleshot_timer_sync(&chip->user_read_timer);
- flush_work(&chip->work);
- ret_size = atomic_read(&chip->data_pending);
- if (ret_size > 0) { /* relay data */
- ssize_t orig_ret_size = ret_size;
- if (size < ret_size)
- ret_size = size;
-
- mutex_lock(&chip->buffer_mutex);
- rc = copy_to_user(buf, chip->data_buffer, ret_size);
- memset(chip->data_buffer, 0, orig_ret_size);
- if (rc)
- ret_size = -EFAULT;
-
- mutex_unlock(&chip->buffer_mutex);
- }
-
- atomic_set(&chip->data_pending, 0);
-
- return ret_size;
-}
-EXPORT_SYMBOL_GPL(tpm_read);
void tpm_remove_hardware(struct device *dev)
{
@@ -1264,8 +878,8 @@ void tpm_remove_hardware(struct device *dev)
spin_unlock(&driver_lock);
synchronize_rcu();
- misc_deregister(&chip->vendor.miscdev);
- sysfs_remove_group(&dev->kobj, chip->vendor.attr_group);
+ tpm_dev_del_device(chip);
+ tpm_sysfs_del_device(chip);
tpm_remove_ppi(&dev->kobj);
tpm_bios_log_teardown(chip->bios_dir);
@@ -1291,7 +905,7 @@ int tpm_pm_suspend(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
struct tpm_cmd_t cmd;
- int rc;
+ int rc, try;
u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 };
@@ -1309,9 +923,32 @@ int tpm_pm_suspend(struct device *dev)
}
/* now do the actual savestate */
- cmd.header.in = savestate_header;
- rc = transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE,
- "sending savestate before suspend");
+ for (try = 0; try < TPM_RETRY; try++) {
+ cmd.header.in = savestate_header;
+ rc = transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, NULL);
+
+ /*
+ * If the TPM indicates that it is too busy to respond to
+ * this command then retry before giving up. It can take
+ * several seconds for this TPM to be ready.
+ *
+ * This can happen if the TPM has already been sent the
+ * SaveState command before the driver has loaded. TCG 1.2
+ * specification states that any communication after SaveState
+ * may cause the TPM to invalidate previously saved state.
+ */
+ if (rc != TPM_WARN_RETRY)
+ break;
+ msleep(TPM_TIMEOUT_RETRY);
+ }
+
+ if (rc)
+ dev_err(chip->dev,
+ "Error (%d) sending savestate before suspend\n", rc);
+ else if (try > 0)
+ dev_warn(chip->dev, "TPM savestate took %dms\n",
+ try * TPM_TIMEOUT_RETRY);
+
return rc;
}
EXPORT_SYMBOL_GPL(tpm_pm_suspend);
@@ -1390,11 +1027,7 @@ void tpm_dev_vendor_release(struct tpm_chip *chip)
if (!chip)
return;
- if (chip->vendor.release)
- chip->vendor.release(chip->dev);
-
clear_bit(chip->dev_num, dev_mask);
- kfree(chip->vendor.miscdev.name);
}
EXPORT_SYMBOL_GPL(tpm_dev_vendor_release);
@@ -1415,81 +1048,56 @@ static void tpm_dev_release(struct device *dev)
chip->release(dev);
kfree(chip);
}
-EXPORT_SYMBOL_GPL(tpm_dev_release);
/*
- * Called from tpm_<specific>.c probe function only for devices
+ * Called from tpm_<specific>.c probe function only for devices
* the driver has determined it should claim. Prior to calling
* this function the specific probe function has called pci_enable_device
* upon errant exit from this function specific probe function should call
* pci_disable_device
*/
struct tpm_chip *tpm_register_hardware(struct device *dev,
- const struct tpm_vendor_specific *entry)
+ const struct tpm_class_ops *ops)
{
-#define DEVNAME_SIZE 7
-
- char *devname;
struct tpm_chip *chip;
/* Driver specific per-device data */
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- devname = kmalloc(DEVNAME_SIZE, GFP_KERNEL);
- if (chip == NULL || devname == NULL)
- goto out_free;
+ if (chip == NULL)
+ return NULL;
- mutex_init(&chip->buffer_mutex);
mutex_init(&chip->tpm_mutex);
INIT_LIST_HEAD(&chip->list);
- INIT_WORK(&chip->work, timeout_work);
-
- setup_timer(&chip->user_read_timer, user_reader_timeout,
- (unsigned long)chip);
-
- memcpy(&chip->vendor, entry, sizeof(struct tpm_vendor_specific));
-
+ chip->ops = ops;
chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES);
if (chip->dev_num >= TPM_NUM_DEVICES) {
dev_err(dev, "No available tpm device numbers\n");
goto out_free;
- } else if (chip->dev_num == 0)
- chip->vendor.miscdev.minor = TPM_MINOR;
- else
- chip->vendor.miscdev.minor = MISC_DYNAMIC_MINOR;
+ }
set_bit(chip->dev_num, dev_mask);
- scnprintf(devname, DEVNAME_SIZE, "%s%d", "tpm", chip->dev_num);
- chip->vendor.miscdev.name = devname;
+ scnprintf(chip->devname, sizeof(chip->devname), "%s%d", "tpm",
+ chip->dev_num);
- chip->vendor.miscdev.parent = dev;
chip->dev = get_device(dev);
chip->release = dev->release;
dev->release = tpm_dev_release;
dev_set_drvdata(dev, chip);
- if (misc_register(&chip->vendor.miscdev)) {
- dev_err(chip->dev,
- "unable to misc_register %s, minor %d\n",
- chip->vendor.miscdev.name,
- chip->vendor.miscdev.minor);
+ if (tpm_dev_add_device(chip))
goto put_device;
- }
- if (sysfs_create_group(&dev->kobj, chip->vendor.attr_group)) {
- misc_deregister(&chip->vendor.miscdev);
- goto put_device;
- }
+ if (tpm_sysfs_add_device(chip))
+ goto del_misc;
- if (tpm_add_ppi(&dev->kobj)) {
- misc_deregister(&chip->vendor.miscdev);
- goto put_device;
- }
+ if (tpm_add_ppi(&dev->kobj))
+ goto del_misc;
- chip->bios_dir = tpm_bios_log_setup(devname);
+ chip->bios_dir = tpm_bios_log_setup(chip->devname);
/* Make chip available */
spin_lock(&driver_lock);
@@ -1498,11 +1106,12 @@ struct tpm_chip *tpm_register_hardware(struct device *dev,
return chip;
+del_misc:
+ tpm_dev_del_device(chip);
put_device:
put_device(chip->dev);
out_free:
kfree(chip);
- kfree(devname);
return NULL;
}
EXPORT_SYMBOL_GPL(tpm_register_hardware);
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
new file mode 100644
index 00000000000..01730a27ae0
--- /dev/null
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Copyright (C) 2013 Obsidian Research Corp
+ * Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
+ *
+ * sysfs filesystem inspection interface to the TPM
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ */
+#include <linux/device.h>
+#include "tpm.h"
+
+/* XXX for now this helper is duplicated in tpm-interface.c */
+static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
+ int len, const char *desc)
+{
+ int err;
+
+ len = tpm_transmit(chip, (u8 *) cmd, len);
+ if (len < 0)
+ return len;
+ else if (len < TPM_HEADER_SIZE)
+ return -EFAULT;
+
+ err = be32_to_cpu(cmd->header.out.return_code);
+ if (err != 0 && desc)
+ dev_err(chip->dev, "A TPM error (%d) occurred %s\n", err, desc);
+
+ return err;
+}
+
+#define READ_PUBEK_RESULT_SIZE 314
+#define TPM_ORD_READPUBEK cpu_to_be32(124)
+static struct tpm_input_header tpm_readpubek_header = {
+ .tag = TPM_TAG_RQU_COMMAND,
+ .length = cpu_to_be32(30),
+ .ordinal = TPM_ORD_READPUBEK
+};
+static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ u8 *data;
+ struct tpm_cmd_t tpm_cmd;
+ ssize_t err;
+ int i, rc;
+ char *str = buf;
+
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+
+ tpm_cmd.header.in = tpm_readpubek_header;
+ err = transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
+ "attempting to read the PUBEK");
+ if (err)
+ goto out;
+
+ /*
+ ignore header 10 bytes
+ algorithm 32 bits (1 == RSA )
+ encscheme 16 bits
+ sigscheme 16 bits
+ parameters (RSA 12->bytes: keybit, #primes, expbit)
+ keylenbytes 32 bits
+ 256 byte modulus
+ ignore checksum 20 bytes
+ */
+ data = tpm_cmd.params.readpubek_out_buffer;
+ str +=
+ sprintf(str,
+ "Algorithm: %02X %02X %02X %02X\n"
+ "Encscheme: %02X %02X\n"
+ "Sigscheme: %02X %02X\n"
+ "Parameters: %02X %02X %02X %02X "
+ "%02X %02X %02X %02X "
+ "%02X %02X %02X %02X\n"
+ "Modulus length: %d\n"
+ "Modulus:\n",
+ data[0], data[1], data[2], data[3],
+ data[4], data[5],
+ data[6], data[7],
+ data[12], data[13], data[14], data[15],
+ data[16], data[17], data[18], data[19],
+ data[20], data[21], data[22], data[23],
+ be32_to_cpu(*((__be32 *) (data + 24))));
+
+ for (i = 0; i < 256; i++) {
+ str += sprintf(str, "%02X ", data[i + 28]);
+ if ((i + 1) % 16 == 0)
+ str += sprintf(str, "\n");
+ }
+out:
+ rc = str - buf;
+ return rc;
+}
+static DEVICE_ATTR_RO(pubek);
+
+static ssize_t pcrs_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ cap_t cap;
+ u8 digest[TPM_DIGEST_SIZE];
+ ssize_t rc;
+ int i, j, num_pcrs;
+ char *str = buf;
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+
+ rc = tpm_getcap(dev, TPM_CAP_PROP_PCR, &cap,
+ "attempting to determine the number of PCRS");
+ if (rc)
+ return 0;
+
+ num_pcrs = be32_to_cpu(cap.num_pcrs);
+ for (i = 0; i < num_pcrs; i++) {
+ rc = tpm_pcr_read_dev(chip, i, digest);
+ if (rc)
+ break;
+ str += sprintf(str, "PCR-%02d: ", i);
+ for (j = 0; j < TPM_DIGEST_SIZE; j++)
+ str += sprintf(str, "%02X ", digest[j]);
+ str += sprintf(str, "\n");
+ }
+ return str - buf;
+}
+static DEVICE_ATTR_RO(pcrs);
+
+static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ cap_t cap;
+ ssize_t rc;
+
+ rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
+ "attempting to determine the permanent enabled state");
+ if (rc)
+ return 0;
+
+ rc = sprintf(buf, "%d\n", !cap.perm_flags.disable);
+ return rc;
+}
+static DEVICE_ATTR_RO(enabled);
+
+static ssize_t active_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ cap_t cap;
+ ssize_t rc;
+
+ rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
+ "attempting to determine the permanent active state");
+ if (rc)
+ return 0;
+
+ rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated);
+ return rc;
+}
+static DEVICE_ATTR_RO(active);
+
+static ssize_t owned_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ cap_t cap;
+ ssize_t rc;
+
+ rc = tpm_getcap(dev, TPM_CAP_PROP_OWNER, &cap,
+ "attempting to determine the owner state");
+ if (rc)
+ return 0;
+
+ rc = sprintf(buf, "%d\n", cap.owned);
+ return rc;
+}
+static DEVICE_ATTR_RO(owned);
+
+static ssize_t temp_deactivated_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ cap_t cap;
+ ssize_t rc;
+
+ rc = tpm_getcap(dev, TPM_CAP_FLAG_VOL, &cap,
+ "attempting to determine the temporary state");
+ if (rc)
+ return 0;
+
+ rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated);
+ return rc;
+}
+static DEVICE_ATTR_RO(temp_deactivated);
+
+static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ cap_t cap;
+ ssize_t rc;
+ char *str = buf;
+
+ rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
+ "attempting to determine the manufacturer");
+ if (rc)
+ return 0;
+ str += sprintf(str, "Manufacturer: 0x%x\n",
+ be32_to_cpu(cap.manufacturer_id));
+
+ /* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */
+ rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap,
+ "attempting to determine the 1.2 version");
+ if (!rc) {
+ str += sprintf(str,
+ "TCG version: %d.%d\nFirmware version: %d.%d\n",
+ cap.tpm_version_1_2.Major,
+ cap.tpm_version_1_2.Minor,
+ cap.tpm_version_1_2.revMajor,
+ cap.tpm_version_1_2.revMinor);
+ } else {
+ /* Otherwise just use TPM_STRUCT_VER */
+ rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap,
+ "attempting to determine the 1.1 version");
+ if (rc)
+ return 0;
+ str += sprintf(str,
+ "TCG version: %d.%d\nFirmware version: %d.%d\n",
+ cap.tpm_version.Major,
+ cap.tpm_version.Minor,
+ cap.tpm_version.revMajor,
+ cap.tpm_version.revMinor);
+ }
+
+ return str - buf;
+}
+static DEVICE_ATTR_RO(caps);
+
+static ssize_t cancel_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+ if (chip == NULL)
+ return 0;
+
+ chip->ops->cancel(chip);
+ return count;
+}
+static DEVICE_ATTR_WO(cancel);
+
+static ssize_t durations_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+
+ if (chip->vendor.duration[TPM_LONG] == 0)
+ return 0;
+
+ return sprintf(buf, "%d %d %d [%s]\n",
+ jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
+ jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
+ jiffies_to_usecs(chip->vendor.duration[TPM_LONG]),
+ chip->vendor.duration_adjusted
+ ? "adjusted" : "original");
+}
+static DEVICE_ATTR_RO(durations);
+
+static ssize_t timeouts_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d %d %d %d [%s]\n",
+ jiffies_to_usecs(chip->vendor.timeout_a),
+ jiffies_to_usecs(chip->vendor.timeout_b),
+ jiffies_to_usecs(chip->vendor.timeout_c),
+ jiffies_to_usecs(chip->vendor.timeout_d),
+ chip->vendor.timeout_adjusted
+ ? "adjusted" : "original");
+}
+static DEVICE_ATTR_RO(timeouts);
+
+static struct attribute *tpm_dev_attrs[] = {
+ &dev_attr_pubek.attr,
+ &dev_attr_pcrs.attr,
+ &dev_attr_enabled.attr,
+ &dev_attr_active.attr,
+ &dev_attr_owned.attr,
+ &dev_attr_temp_deactivated.attr,
+ &dev_attr_caps.attr,
+ &dev_attr_cancel.attr,
+ &dev_attr_durations.attr,
+ &dev_attr_timeouts.attr,
+ NULL,
+};
+
+static const struct attribute_group tpm_dev_group = {
+ .attrs = tpm_dev_attrs,
+};
+
+int tpm_sysfs_add_device(struct tpm_chip *chip)
+{
+ int err;
+ err = sysfs_create_group(&chip->dev->kobj,
+ &tpm_dev_group);
+
+ if (err)
+ dev_err(chip->dev,
+ "failed to create sysfs attributes, %d\n", err);
+ return err;
+}
+
+void tpm_sysfs_del_device(struct tpm_chip *chip)
+{
+ sysfs_remove_group(&chip->dev->kobj, &tpm_dev_group);
+}
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 8ef7649a50a..e4d0888d2ea 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -32,10 +32,12 @@ enum tpm_const {
TPM_MINOR = 224, /* officially assigned */
TPM_BUFSIZE = 4096,
TPM_NUM_DEVICES = 256,
+ TPM_RETRY = 50, /* 5 seconds */
};
enum tpm_timeout {
TPM_TIMEOUT = 5, /* msecs */
+ TPM_TIMEOUT_RETRY = 100 /* msecs */
};
/* TPM addresses */
@@ -44,40 +46,24 @@ enum tpm_addr {
TPM_ADDR = 0x4E,
};
+/* Indexes the duration array */
+enum tpm_duration {
+ TPM_SHORT = 0,
+ TPM_MEDIUM = 1,
+ TPM_LONG = 2,
+ TPM_UNDEFINED,
+};
+
+#define TPM_WARN_RETRY 0x800
#define TPM_WARN_DOING_SELFTEST 0x802
#define TPM_ERR_DEACTIVATED 0x6
#define TPM_ERR_DISABLED 0x7
+#define TPM_ERR_INVALID_POSTINIT 38
#define TPM_HEADER_SIZE 10
-extern ssize_t tpm_show_pubek(struct device *, struct device_attribute *attr,
- char *);
-extern ssize_t tpm_show_pcrs(struct device *, struct device_attribute *attr,
- char *);
-extern ssize_t tpm_show_caps(struct device *, struct device_attribute *attr,
- char *);
-extern ssize_t tpm_show_caps_1_2(struct device *, struct device_attribute *attr,
- char *);
-extern ssize_t tpm_store_cancel(struct device *, struct device_attribute *attr,
- const char *, size_t);
-extern ssize_t tpm_show_enabled(struct device *, struct device_attribute *attr,
- char *);
-extern ssize_t tpm_show_active(struct device *, struct device_attribute *attr,
- char *);
-extern ssize_t tpm_show_owned(struct device *, struct device_attribute *attr,
- char *);
-extern ssize_t tpm_show_temp_deactivated(struct device *,
- struct device_attribute *attr, char *);
-extern ssize_t tpm_show_durations(struct device *,
- struct device_attribute *attr, char *);
-extern ssize_t tpm_show_timeouts(struct device *,
- struct device_attribute *attr, char *);
-
struct tpm_chip;
struct tpm_vendor_specific {
- const u8 req_complete_mask;
- const u8 req_complete_val;
- const u8 req_canceled;
void __iomem *iobase; /* ioremapped address */
unsigned long base; /* TPM base address */
@@ -87,41 +73,36 @@ struct tpm_vendor_specific {
int region_size;
int have_region;
- int (*recv) (struct tpm_chip *, u8 *, size_t);
- int (*send) (struct tpm_chip *, u8 *, size_t);
- void (*cancel) (struct tpm_chip *);
- u8 (*status) (struct tpm_chip *);
- void (*release) (struct device *);
struct miscdevice miscdev;
- struct attribute_group *attr_group;
struct list_head list;
int locality;
unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */
bool timeout_adjusted;
unsigned long duration[3]; /* jiffies */
bool duration_adjusted;
- void *data;
+ void *priv;
wait_queue_head_t read_queue;
wait_queue_head_t int_queue;
+
+ u16 manufacturer_id;
};
+#define TPM_VPRIV(c) (c)->vendor.priv
+
#define TPM_VID_INTEL 0x8086
+#define TPM_VID_WINBOND 0x1050
+#define TPM_VID_STM 0x104A
struct tpm_chip {
struct device *dev; /* Device stuff */
+ const struct tpm_class_ops *ops;
int dev_num; /* /dev/tpm# */
+ char devname[7];
unsigned long is_open; /* only one allowed */
int time_expired;
- /* Data passed to and from the tpm via the read/write calls */
- u8 *data_buffer;
- atomic_t data_pending;
- struct mutex buffer_mutex;
-
- struct timer_list user_read_timer; /* user needs to claim result */
- struct work_struct work;
struct mutex tpm_mutex; /* tpm is processing */
struct tpm_vendor_specific vendor;
@@ -154,13 +135,15 @@ struct tpm_input_header {
__be16 tag;
__be32 length;
__be32 ordinal;
-}__attribute__((packed));
+} __packed;
struct tpm_output_header {
__be16 tag;
__be32 length;
__be32 return_code;
-}__attribute__((packed));
+} __packed;
+
+#define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
struct stclear_flags_t {
__be16 tag;
@@ -169,14 +152,14 @@ struct stclear_flags_t {
u8 physicalPresence;
u8 physicalPresenceLock;
u8 bGlobalLock;
-}__attribute__((packed));
+} __packed;
struct tpm_version_t {
u8 Major;
u8 Minor;
u8 revMajor;
u8 revMinor;
-}__attribute__((packed));
+} __packed;
struct tpm_version_1_2_t {
__be16 tag;
@@ -184,20 +167,20 @@ struct tpm_version_1_2_t {
u8 Minor;
u8 revMajor;
u8 revMinor;
-}__attribute__((packed));
+} __packed;
struct timeout_t {
__be32 a;
__be32 b;
__be32 c;
__be32 d;
-}__attribute__((packed));
+} __packed;
struct duration_t {
__be32 tpm_short;
__be32 tpm_medium;
__be32 tpm_long;
-}__attribute__((packed));
+} __packed;
struct permanent_flags_t {
__be16 tag;
@@ -221,7 +204,7 @@ struct permanent_flags_t {
u8 tpmEstablished;
u8 maintenanceDone;
u8 disableFullDALogicInfo;
-}__attribute__((packed));
+} __packed;
typedef union {
struct permanent_flags_t perm_flags;
@@ -235,16 +218,34 @@ typedef union {
struct duration_t duration;
} cap_t;
+enum tpm_capabilities {
+ TPM_CAP_FLAG = cpu_to_be32(4),
+ TPM_CAP_PROP = cpu_to_be32(5),
+ CAP_VERSION_1_1 = cpu_to_be32(0x06),
+ CAP_VERSION_1_2 = cpu_to_be32(0x1A)
+};
+
+enum tpm_sub_capabilities {
+ TPM_CAP_PROP_PCR = cpu_to_be32(0x101),
+ TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103),
+ TPM_CAP_FLAG_PERM = cpu_to_be32(0x108),
+ TPM_CAP_FLAG_VOL = cpu_to_be32(0x109),
+ TPM_CAP_PROP_OWNER = cpu_to_be32(0x111),
+ TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115),
+ TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120),
+
+};
+
struct tpm_getcap_params_in {
__be32 cap;
__be32 subcap_size;
__be32 subcap;
-}__attribute__((packed));
+} __packed;
struct tpm_getcap_params_out {
__be32 cap_size;
cap_t cap;
-}__attribute__((packed));
+} __packed;
struct tpm_readpubek_params_out {
u8 algorithm[4];
@@ -255,26 +256,25 @@ struct tpm_readpubek_params_out {
__be32 keysize;
u8 modulus[256];
u8 checksum[20];
-}__attribute__((packed));
+} __packed;
typedef union {
struct tpm_input_header in;
struct tpm_output_header out;
} tpm_cmd_header;
-#define TPM_DIGEST_SIZE 20
struct tpm_pcrread_out {
u8 pcr_result[TPM_DIGEST_SIZE];
-}__attribute__((packed));
+} __packed;
struct tpm_pcrread_in {
__be32 pcr_idx;
-}__attribute__((packed));
+} __packed;
struct tpm_pcrextend_in {
__be32 pcr_idx;
u8 hash[TPM_DIGEST_SIZE];
-}__attribute__((packed));
+} __packed;
/* 128 bytes is an arbitrary cap. This could be as large as TPM_BUFSIZE - 18
* bytes, but 128 is still a relatively large number of random bytes and
@@ -285,11 +285,15 @@ struct tpm_pcrextend_in {
struct tpm_getrandom_out {
__be32 rng_data_len;
u8 rng_data[TPM_MAX_RNG_DATA];
-}__attribute__((packed));
+} __packed;
struct tpm_getrandom_in {
__be32 num_bytes;
-}__attribute__((packed));
+} __packed;
+
+struct tpm_startup_in {
+ __be16 startup_type;
+} __packed;
typedef union {
struct tpm_getcap_params_out getcap_out;
@@ -301,32 +305,37 @@ typedef union {
struct tpm_pcrextend_in pcrextend_in;
struct tpm_getrandom_in getrandom_in;
struct tpm_getrandom_out getrandom_out;
+ struct tpm_startup_in startup_in;
} tpm_cmd_params;
struct tpm_cmd_t {
tpm_cmd_header header;
tpm_cmd_params params;
-}__attribute__((packed));
+} __packed;
ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *);
+ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
+ size_t bufsiz);
extern int tpm_get_timeouts(struct tpm_chip *);
extern void tpm_gen_interrupt(struct tpm_chip *);
extern int tpm_do_selftest(struct tpm_chip *);
extern unsigned long tpm_calc_ordinal_duration(struct tpm_chip *, u32);
extern struct tpm_chip* tpm_register_hardware(struct device *,
- const struct tpm_vendor_specific *);
-extern int tpm_open(struct inode *, struct file *);
-extern int tpm_release(struct inode *, struct file *);
+ const struct tpm_class_ops *ops);
extern void tpm_dev_vendor_release(struct tpm_chip *);
-extern ssize_t tpm_write(struct file *, const char __user *, size_t,
- loff_t *);
-extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *);
extern void tpm_remove_hardware(struct device *);
extern int tpm_pm_suspend(struct device *);
extern int tpm_pm_resume(struct device *);
extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
- wait_queue_head_t *);
+ wait_queue_head_t *, bool);
+
+int tpm_dev_add_device(struct tpm_chip *chip);
+void tpm_dev_del_device(struct tpm_chip *chip);
+int tpm_sysfs_add_device(struct tpm_chip *chip);
+void tpm_sysfs_del_device(struct tpm_chip *chip);
+
+int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf);
#ifdef CONFIG_ACPI
extern int tpm_add_ppi(struct kobject *);
diff --git a/drivers/char/tpm/tpm_acpi.c b/drivers/char/tpm/tpm_acpi.c
index 56051d0c97a..565a9478cb9 100644
--- a/drivers/char/tpm/tpm_acpi.c
+++ b/drivers/char/tpm/tpm_acpi.c
@@ -23,7 +23,7 @@
#include <linux/security.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <acpi/acpi.h>
+#include <linux/acpi.h>
#include "tpm.h"
#include "tpm_eventlog.h"
@@ -33,13 +33,13 @@ struct acpi_tcpa {
u16 platform_class;
union {
struct client_hdr {
- u32 log_max_len __attribute__ ((packed));
- u64 log_start_addr __attribute__ ((packed));
+ u32 log_max_len __packed;
+ u64 log_start_addr __packed;
} client;
struct server_hdr {
u16 reserved;
- u64 log_max_len __attribute__ ((packed));
- u64 log_start_addr __attribute__ ((packed));
+ u64 log_max_len __packed;
+ u64 log_start_addr __packed;
} server;
};
};
@@ -95,7 +95,7 @@ int read_log(struct tpm_bios_log *log)
log->bios_event_log_end = log->bios_event_log + len;
- virt = acpi_os_map_memory(start, len);
+ virt = acpi_os_map_iomem(start, len);
if (!virt) {
kfree(log->bios_event_log);
printk("%s: ERROR - Unable to map memory\n", __func__);
@@ -104,6 +104,6 @@ int read_log(struct tpm_bios_log *log)
memcpy_fromio(log->bios_event_log, virt, len);
- acpi_os_unmap_memory(virt, len);
+ acpi_os_unmap_iomem(virt, len);
return 0;
}
diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c
index 678d57019dc..6069d13ae4a 100644
--- a/drivers/char/tpm/tpm_atmel.c
+++ b/drivers/char/tpm/tpm_atmel.c
@@ -116,40 +116,19 @@ static u8 tpm_atml_status(struct tpm_chip *chip)
return ioread8(chip->vendor.iobase + 1);
}
-static const struct file_operations atmel_ops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .open = tpm_open,
- .read = tpm_read,
- .write = tpm_write,
- .release = tpm_release,
-};
-
-static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
-static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
-static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
-static DEVICE_ATTR(cancel, S_IWUSR |S_IWGRP, NULL, tpm_store_cancel);
-
-static struct attribute* atmel_attrs[] = {
- &dev_attr_pubek.attr,
- &dev_attr_pcrs.attr,
- &dev_attr_caps.attr,
- &dev_attr_cancel.attr,
- NULL,
-};
-
-static struct attribute_group atmel_attr_grp = { .attrs = atmel_attrs };
+static bool tpm_atml_req_canceled(struct tpm_chip *chip, u8 status)
+{
+ return (status == ATML_STATUS_READY);
+}
-static const struct tpm_vendor_specific tpm_atmel = {
+static const struct tpm_class_ops tpm_atmel = {
.recv = tpm_atml_recv,
.send = tpm_atml_send,
.cancel = tpm_atml_cancel,
.status = tpm_atml_status,
.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
.req_complete_val = ATML_STATUS_DATA_AVAIL,
- .req_canceled = ATML_STATUS_READY,
- .attr_group = &atmel_attr_grp,
- .miscdev = { .fops = &atmel_ops, },
+ .req_canceled = tpm_atml_req_canceled,
};
static struct platform_device *pdev;
@@ -197,7 +176,7 @@ static int __init init_atmel(void)
have_region =
(atmel_request_region
- (tpm_atmel.base, region_size, "tpm_atmel0") == NULL) ? 0 : 1;
+ (base, region_size, "tpm_atmel0") == NULL) ? 0 : 1;
pdev = platform_device_register_simple("tpm_atmel", -1, NULL, 0);
if (IS_ERR(pdev)) {
diff --git a/drivers/char/tpm/tpm_eventlog.c b/drivers/char/tpm/tpm_eventlog.c
index 84ddc557b8f..59f7cb28260 100644
--- a/drivers/char/tpm/tpm_eventlog.c
+++ b/drivers/char/tpm/tpm_eventlog.c
@@ -406,7 +406,6 @@ out_tpm:
out:
return NULL;
}
-EXPORT_SYMBOL_GPL(tpm_bios_log_setup);
void tpm_bios_log_teardown(struct dentry **lst)
{
@@ -415,5 +414,3 @@ void tpm_bios_log_teardown(struct dentry **lst)
for (i = 0; i < 3; i++)
securityfs_remove(lst[i]);
}
-EXPORT_SYMBOL_GPL(tpm_bios_log_teardown);
-MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c
new file mode 100644
index 00000000000..77272925dee
--- /dev/null
+++ b/drivers/char/tpm/tpm_i2c_atmel.c
@@ -0,0 +1,244 @@
+/*
+ * ATMEL I2C TPM AT97SC3204T
+ *
+ * Copyright (C) 2012 V Lab Technologies
+ * Teddy Reed <teddy@prosauce.org>
+ * Copyright (C) 2013, Obsidian Research Corp.
+ * Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
+ * Device driver for ATMEL I2C TPMs.
+ *
+ * Teddy Reed determined the basic I2C command flow, unlike other I2C TPM
+ * devices the raw TCG formatted TPM command data is written via I2C and then
+ * raw TCG formatted TPM command data is returned via I2C.
+ *
+ * TGC status/locality/etc functions seen in the LPC implementation do not
+ * seem to be present.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/>.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include "tpm.h"
+
+#define I2C_DRIVER_NAME "tpm_i2c_atmel"
+
+#define TPM_I2C_SHORT_TIMEOUT 750 /* ms */
+#define TPM_I2C_LONG_TIMEOUT 2000 /* 2 sec */
+
+#define ATMEL_STS_OK 1
+
+struct priv_data {
+ size_t len;
+ /* This is the amount we read on the first try. 25 was chosen to fit a
+ * fair number of read responses in the buffer so a 2nd retry can be
+ * avoided in small message cases. */
+ u8 buffer[sizeof(struct tpm_output_header) + 25];
+};
+
+static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+ struct priv_data *priv = chip->vendor.priv;
+ struct i2c_client *client = to_i2c_client(chip->dev);
+ s32 status;
+
+ priv->len = 0;
+
+ if (len <= 2)
+ return -EIO;
+
+ status = i2c_master_send(client, buf, len);
+
+ dev_dbg(chip->dev,
+ "%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__,
+ (int)min_t(size_t, 64, len), buf, len, status);
+ return status;
+}
+
+static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ struct priv_data *priv = chip->vendor.priv;
+ struct i2c_client *client = to_i2c_client(chip->dev);
+ struct tpm_output_header *hdr =
+ (struct tpm_output_header *)priv->buffer;
+ u32 expected_len;
+ int rc;
+
+ if (priv->len == 0)
+ return -EIO;
+
+ /* Get the message size from the message header, if we didn't get the
+ * whole message in read_status then we need to re-read the
+ * message. */
+ expected_len = be32_to_cpu(hdr->length);
+ if (expected_len > count)
+ return -ENOMEM;
+
+ if (priv->len >= expected_len) {
+ dev_dbg(chip->dev,
+ "%s early(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
+ (int)min_t(size_t, 64, expected_len), buf, count,
+ expected_len);
+ memcpy(buf, priv->buffer, expected_len);
+ return expected_len;
+ }
+
+ rc = i2c_master_recv(client, buf, expected_len);
+ dev_dbg(chip->dev,
+ "%s reread(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
+ (int)min_t(size_t, 64, expected_len), buf, count,
+ expected_len);
+ return rc;
+}
+
+static void i2c_atmel_cancel(struct tpm_chip *chip)
+{
+ dev_err(chip->dev, "TPM operation cancellation was requested, but is not supported");
+}
+
+static u8 i2c_atmel_read_status(struct tpm_chip *chip)
+{
+ struct priv_data *priv = chip->vendor.priv;
+ struct i2c_client *client = to_i2c_client(chip->dev);
+ int rc;
+
+ /* The TPM fails the I2C read until it is ready, so we do the entire
+ * transfer here and buffer it locally. This way the common code can
+ * properly handle the timeouts. */
+ priv->len = 0;
+ memset(priv->buffer, 0, sizeof(priv->buffer));
+
+
+ /* Once the TPM has completed the command the command remains readable
+ * until another command is issued. */
+ rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer));
+ dev_dbg(chip->dev,
+ "%s: sts=%d", __func__, rc);
+ if (rc <= 0)
+ return 0;
+
+ priv->len = rc;
+
+ return ATMEL_STS_OK;
+}
+
+static bool i2c_atmel_req_canceled(struct tpm_chip *chip, u8 status)
+{
+ return false;
+}
+
+static const struct tpm_class_ops i2c_atmel = {
+ .status = i2c_atmel_read_status,
+ .recv = i2c_atmel_recv,
+ .send = i2c_atmel_send,
+ .cancel = i2c_atmel_cancel,
+ .req_complete_mask = ATMEL_STS_OK,
+ .req_complete_val = ATMEL_STS_OK,
+ .req_canceled = i2c_atmel_req_canceled,
+};
+
+static int i2c_atmel_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc;
+ struct tpm_chip *chip;
+ struct device *dev = &client->dev;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ chip = tpm_register_hardware(dev, &i2c_atmel);
+ if (!chip) {
+ dev_err(dev, "%s() error in tpm_register_hardware\n", __func__);
+ return -ENODEV;
+ }
+
+ chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data),
+ GFP_KERNEL);
+
+ /* Default timeouts */
+ chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+ chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
+ chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+ chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+ chip->vendor.irq = 0;
+
+ /* There is no known way to probe for this device, and all version
+ * information seems to be read via TPM commands. Thus we rely on the
+ * TPM startup process in the common code to detect the device. */
+ if (tpm_get_timeouts(chip)) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ if (tpm_do_selftest(chip)) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ tpm_dev_vendor_release(chip);
+ tpm_remove_hardware(chip->dev);
+ return rc;
+}
+
+static int i2c_atmel_remove(struct i2c_client *client)
+{
+ struct device *dev = &(client->dev);
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+
+ if (chip)
+ tpm_dev_vendor_release(chip);
+ tpm_remove_hardware(dev);
+ kfree(chip);
+ return 0;
+}
+
+static const struct i2c_device_id i2c_atmel_id[] = {
+ {I2C_DRIVER_NAME, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, i2c_atmel_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id i2c_atmel_of_match[] = {
+ {.compatible = "atmel,at97sc3204t"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, i2c_atmel_of_match);
+#endif
+
+static SIMPLE_DEV_PM_OPS(i2c_atmel_pm_ops, tpm_pm_suspend, tpm_pm_resume);
+
+static struct i2c_driver i2c_atmel_driver = {
+ .id_table = i2c_atmel_id,
+ .probe = i2c_atmel_probe,
+ .remove = i2c_atmel_remove,
+ .driver = {
+ .name = I2C_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &i2c_atmel_pm_ops,
+ .of_match_table = of_match_ptr(i2c_atmel_of_match),
+ },
+};
+
+module_i2c_driver(i2c_atmel_driver);
+
+MODULE_AUTHOR("Jason Gunthorpe <jgunthorpe@obsidianresearch.com>");
+MODULE_DESCRIPTION("Atmel TPM I2C Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c
index fb447bd0cb6..472af4bb1b6 100644
--- a/drivers/char/tpm/tpm_i2c_infineon.c
+++ b/drivers/char/tpm/tpm_i2c_infineon.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Infineon Technologies
+ * Copyright (C) 2012,2013 Infineon Technologies
*
* Authors:
* Peter Huewe <peter.huewe@infineon.com>
@@ -21,10 +21,8 @@
*
*
*/
-#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/wait.h>
#include "tpm.h"
@@ -56,17 +54,24 @@
#define TPM_TIMEOUT_US_HI (TPM_TIMEOUT_US_LOW + 2000)
/* expected value for DIDVID register */
-#define TPM_TIS_I2C_DID_VID 0x000b15d1L
+#define TPM_TIS_I2C_DID_VID_9635 0xd1150b00L
+#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L
+
+enum i2c_chip_type {
+ SLB9635,
+ SLB9645,
+ UNKNOWN,
+};
/* Structure to store I2C TPM specific stuff */
struct tpm_inf_dev {
struct i2c_client *client;
u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */
struct tpm_chip *chip;
+ enum i2c_chip_type chip_type;
};
static struct tpm_inf_dev tpm_dev;
-static struct i2c_driver tpm_tis_i2c_driver;
/*
* iic_tpm_read() - read from TPM register
@@ -90,10 +95,20 @@ static struct i2c_driver tpm_tis_i2c_driver;
static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
{
- struct i2c_msg msg1 = { tpm_dev.client->addr, 0, 1, &addr };
- struct i2c_msg msg2 = { tpm_dev.client->addr, I2C_M_RD, len, buffer };
+ struct i2c_msg msg1 = {
+ .addr = tpm_dev.client->addr,
+ .len = 1,
+ .buf = &addr
+ };
+ struct i2c_msg msg2 = {
+ .addr = tpm_dev.client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buffer
+ };
+ struct i2c_msg msgs[] = {msg1, msg2};
- int rc;
+ int rc = 0;
int count;
/* Lock the adapter for the duration of the whole sequence. */
@@ -101,30 +116,53 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
return -EOPNOTSUPP;
i2c_lock_adapter(tpm_dev.client->adapter);
- for (count = 0; count < MAX_COUNT; count++) {
- rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1);
- if (rc > 0)
- break; /* break here to skip sleep */
-
- usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
- }
-
- if (rc <= 0)
- goto out;
-
- /* After the TPM has successfully received the register address it needs
- * some time, thus we're sleeping here again, before retrieving the data
- */
- for (count = 0; count < MAX_COUNT; count++) {
- usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
- rc = __i2c_transfer(tpm_dev.client->adapter, &msg2, 1);
- if (rc > 0)
- break;
+ if (tpm_dev.chip_type == SLB9645) {
+ /* use a combined read for newer chips
+ * unfortunately the smbus functions are not suitable due to
+ * the 32 byte limit of the smbus.
+ * retries should usually not be needed, but are kept just to
+ * be on the safe side.
+ */
+ for (count = 0; count < MAX_COUNT; count++) {
+ rc = __i2c_transfer(tpm_dev.client->adapter, msgs, 2);
+ if (rc > 0)
+ break; /* break here to skip sleep */
+ usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
+ }
+ } else {
+ /* slb9635 protocol should work in all cases */
+ for (count = 0; count < MAX_COUNT; count++) {
+ rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1);
+ if (rc > 0)
+ break; /* break here to skip sleep */
+
+ usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
+ }
+ if (rc <= 0)
+ goto out;
+
+ /* After the TPM has successfully received the register address
+ * it needs some time, thus we're sleeping here again, before
+ * retrieving the data
+ */
+ for (count = 0; count < MAX_COUNT; count++) {
+ usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
+ rc = __i2c_transfer(tpm_dev.client->adapter, &msg2, 1);
+ if (rc > 0)
+ break;
+ }
}
out:
i2c_unlock_adapter(tpm_dev.client->adapter);
+ /* take care of 'guard time' */
+ usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
+
+ /* __i2c_transfer returns the number of successfully transferred
+ * messages.
+ * So rc should be greater than 0 here otherwise we have an error.
+ */
if (rc <= 0)
return -EIO;
@@ -138,7 +176,11 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len,
int rc = -EIO;
int count;
- struct i2c_msg msg1 = { tpm_dev.client->addr, 0, len + 1, tpm_dev.buf };
+ struct i2c_msg msg1 = {
+ .addr = tpm_dev.client->addr,
+ .len = len + 1,
+ .buf = tpm_dev.buf
+ };
if (len > TPM_BUFSIZE)
return -EINVAL;
@@ -154,16 +196,24 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len,
/*
* NOTE: We have to use these special mechanisms here and unfortunately
* cannot rely on the standard behavior of i2c_transfer.
+ * Even for newer chips the smbus functions are not
+ * suitable due to the 32 byte limit of the smbus.
*/
for (count = 0; count < max_count; count++) {
rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1);
if (rc > 0)
break;
-
usleep_range(sleep_low, sleep_hi);
}
i2c_unlock_adapter(tpm_dev.client->adapter);
+ /* take care of 'guard time' */
+ usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
+
+ /* __i2c_transfer returns the number of successfully transferred
+ * messages.
+ * So rc should be greater than 0 here otherwise we have an error.
+ */
if (rc <= 0)
return -EIO;
@@ -283,11 +333,18 @@ static int request_locality(struct tpm_chip *chip, int loc)
static u8 tpm_tis_i2c_status(struct tpm_chip *chip)
{
/* NOTE: since I2C read may fail, return 0 in this case --> time-out */
- u8 buf;
- if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0)
- return 0;
- else
- return buf;
+ u8 buf = 0xFF;
+ u8 i = 0;
+
+ do {
+ if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0)
+ return 0;
+
+ i++;
+ /* if locallity is set STS should not be 0xFF */
+ } while ((buf == 0xFF) && i < 10);
+
+ return buf;
}
static void tpm_tis_i2c_ready(struct tpm_chip *chip)
@@ -328,7 +385,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
/* check current status */
*status = tpm_tis_i2c_status(chip);
- if ((*status & mask) == mask)
+ if ((*status != 0xFF) && (*status & mask) == mask)
return 0;
stop = jiffies + timeout;
@@ -372,7 +429,6 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
/* avoid endless loop in case of broken HW */
if (retries > MAX_COUNT_LONG)
return -EIO;
-
}
return size;
}
@@ -480,7 +536,6 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
rc = -EIO;
goto out_err;
}
-
}
/* write last byte */
@@ -505,54 +560,19 @@ out_err:
return rc;
}
-static const struct file_operations tis_ops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .open = tpm_open,
- .read = tpm_read,
- .write = tpm_write,
- .release = tpm_release,
-};
-
-static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
-static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
-static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
-static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
-static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
-static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL);
-static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
-static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
-static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
-static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
-
-static struct attribute *tis_attrs[] = {
- &dev_attr_pubek.attr,
- &dev_attr_pcrs.attr,
- &dev_attr_enabled.attr,
- &dev_attr_active.attr,
- &dev_attr_owned.attr,
- &dev_attr_temp_deactivated.attr,
- &dev_attr_caps.attr,
- &dev_attr_cancel.attr,
- &dev_attr_durations.attr,
- &dev_attr_timeouts.attr,
- NULL,
-};
-
-static struct attribute_group tis_attr_grp = {
- .attrs = tis_attrs
-};
+static bool tpm_tis_i2c_req_canceled(struct tpm_chip *chip, u8 status)
+{
+ return (status == TPM_STS_COMMAND_READY);
+}
-static struct tpm_vendor_specific tpm_tis_i2c = {
+static const struct tpm_class_ops tpm_tis_i2c = {
.status = tpm_tis_i2c_status,
.recv = tpm_tis_i2c_recv,
.send = tpm_tis_i2c_send,
.cancel = tpm_tis_i2c_ready,
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
- .req_canceled = TPM_STS_COMMAND_READY,
- .attr_group = &tis_attr_grp,
- .miscdev.fops = &tis_ops,
+ .req_canceled = tpm_tis_i2c_req_canceled,
};
static int tpm_tis_i2c_init(struct device *dev)
@@ -563,6 +583,7 @@ static int tpm_tis_i2c_init(struct device *dev)
chip = tpm_register_hardware(dev, &tpm_tis_i2c);
if (!chip) {
+ dev_err(dev, "could not register hardware\n");
rc = -ENODEV;
goto out_err;
}
@@ -577,20 +598,24 @@ static int tpm_tis_i2c_init(struct device *dev)
chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
if (request_locality(chip, 0) != 0) {
+ dev_err(dev, "could not request locality\n");
rc = -ENODEV;
goto out_vendor;
}
/* read four bytes from DID_VID register */
if (iic_tpm_read(TPM_DID_VID(0), (u8 *)&vendor, 4) < 0) {
+ dev_err(dev, "could not read vendor id\n");
rc = -EIO;
goto out_release;
}
- /* create DID_VID register value, after swapping to little-endian */
- vendor = be32_to_cpu((__be32) vendor);
-
- if (vendor != TPM_TIS_I2C_DID_VID) {
+ if (vendor == TPM_TIS_I2C_DID_VID_9645) {
+ tpm_dev.chip_type = SLB9645;
+ } else if (vendor == TPM_TIS_I2C_DID_VID_9635) {
+ tpm_dev.chip_type = SLB9635;
+ } else {
+ dev_err(dev, "vendor id did not match! ID was %08x\n", vendor);
rc = -ENODEV;
goto out_release;
}
@@ -619,37 +644,65 @@ out_vendor:
chip->dev->release = NULL;
chip->release = NULL;
tpm_dev.client = NULL;
- dev_set_drvdata(chip->dev, chip);
out_err:
return rc;
}
static const struct i2c_device_id tpm_tis_i2c_table[] = {
{"tpm_i2c_infineon", 0},
+ {"slb9635tt", 0},
+ {"slb9645tt", 1},
{},
};
MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_table);
+
+#ifdef CONFIG_OF
+static const struct of_device_id tpm_tis_i2c_of_match[] = {
+ {
+ .name = "tpm_i2c_infineon",
+ .type = "tpm",
+ .compatible = "infineon,tpm_i2c_infineon",
+ .data = (void *)0
+ },
+ {
+ .name = "slb9635tt",
+ .type = "tpm",
+ .compatible = "infineon,slb9635tt",
+ .data = (void *)0
+ },
+ {
+ .name = "slb9645tt",
+ .type = "tpm",
+ .compatible = "infineon,slb9645tt",
+ .data = (void *)1
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tpm_tis_i2c_of_match);
+#endif
+
static SIMPLE_DEV_PM_OPS(tpm_tis_i2c_ops, tpm_pm_suspend, tpm_pm_resume);
static int tpm_tis_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int rc;
- if (tpm_dev.client != NULL)
+ struct device *dev = &(client->dev);
+
+ if (tpm_dev.client != NULL) {
+ dev_err(dev, "This driver only supports one client at a time\n");
return -EBUSY; /* We only support one client */
+ }
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- dev_err(&client->dev,
- "no algorithms associated to the i2c bus\n");
+ dev_err(dev, "no algorithms associated to the i2c bus\n");
return -ENODEV;
}
- client->driver = &tpm_tis_i2c_driver;
tpm_dev.client = client;
rc = tpm_tis_i2c_init(&client->dev);
if (rc != 0) {
- client->driver = NULL;
tpm_dev.client = NULL;
rc = -ENODEV;
}
@@ -671,13 +724,11 @@ static int tpm_tis_i2c_remove(struct i2c_client *client)
chip->dev->release = NULL;
chip->release = NULL;
tpm_dev.client = NULL;
- dev_set_drvdata(chip->dev, chip);
return 0;
}
static struct i2c_driver tpm_tis_i2c_driver = {
-
.id_table = tpm_tis_i2c_table,
.probe = tpm_tis_i2c_probe,
.remove = tpm_tis_i2c_remove,
@@ -685,11 +736,12 @@ static struct i2c_driver tpm_tis_i2c_driver = {
.name = "tpm_i2c_infineon",
.owner = THIS_MODULE,
.pm = &tpm_tis_i2c_ops,
+ .of_match_table = of_match_ptr(tpm_tis_i2c_of_match),
},
};
module_i2c_driver(tpm_tis_i2c_driver);
MODULE_AUTHOR("Peter Huewe <peter.huewe@infineon.com>");
MODULE_DESCRIPTION("TPM TIS I2C Infineon Driver");
-MODULE_VERSION("2.1.5");
+MODULE_VERSION("2.2.0");
MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c
new file mode 100644
index 00000000000..7b158efd49f
--- /dev/null
+++ b/drivers/char/tpm/tpm_i2c_nuvoton.c
@@ -0,0 +1,669 @@
+/******************************************************************************
+ * Nuvoton TPM I2C Device Driver Interface for WPCT301/NPCT501,
+ * based on the TCG TPM Interface Spec version 1.2.
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * Copyright (C) 2011, Nuvoton Technology Corporation.
+ * Dan Morav <dan.morav@nuvoton.com>
+ * Copyright (C) 2013, Obsidian Research Corp.
+ * Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/>.
+ *
+ * Nuvoton contact information: APC.Support@nuvoton.com
+ *****************************************************************************/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/i2c.h>
+#include "tpm.h"
+
+/* I2C interface offsets */
+#define TPM_STS 0x00
+#define TPM_BURST_COUNT 0x01
+#define TPM_DATA_FIFO_W 0x20
+#define TPM_DATA_FIFO_R 0x40
+#define TPM_VID_DID_RID 0x60
+/* TPM command header size */
+#define TPM_HEADER_SIZE 10
+#define TPM_RETRY 5
+/*
+ * I2C bus device maximum buffer size w/o counting I2C address or command
+ * i.e. max size required for I2C write is 34 = addr, command, 32 bytes data
+ */
+#define TPM_I2C_MAX_BUF_SIZE 32
+#define TPM_I2C_RETRY_COUNT 32
+#define TPM_I2C_BUS_DELAY 1 /* msec */
+#define TPM_I2C_RETRY_DELAY_SHORT 2 /* msec */
+#define TPM_I2C_RETRY_DELAY_LONG 10 /* msec */
+
+#define I2C_DRIVER_NAME "tpm_i2c_nuvoton"
+
+struct priv_data {
+ unsigned int intrs;
+};
+
+static s32 i2c_nuvoton_read_buf(struct i2c_client *client, u8 offset, u8 size,
+ u8 *data)
+{
+ s32 status;
+
+ status = i2c_smbus_read_i2c_block_data(client, offset, size, data);
+ dev_dbg(&client->dev,
+ "%s(offset=%u size=%u data=%*ph) -> sts=%d\n", __func__,
+ offset, size, (int)size, data, status);
+ return status;
+}
+
+static s32 i2c_nuvoton_write_buf(struct i2c_client *client, u8 offset, u8 size,
+ u8 *data)
+{
+ s32 status;
+
+ status = i2c_smbus_write_i2c_block_data(client, offset, size, data);
+ dev_dbg(&client->dev,
+ "%s(offset=%u size=%u data=%*ph) -> sts=%d\n", __func__,
+ offset, size, (int)size, data, status);
+ return status;
+}
+
+#define TPM_STS_VALID 0x80
+#define TPM_STS_COMMAND_READY 0x40
+#define TPM_STS_GO 0x20
+#define TPM_STS_DATA_AVAIL 0x10
+#define TPM_STS_EXPECT 0x08
+#define TPM_STS_RESPONSE_RETRY 0x02
+#define TPM_STS_ERR_VAL 0x07 /* bit2...bit0 reads always 0 */
+
+#define TPM_I2C_SHORT_TIMEOUT 750 /* ms */
+#define TPM_I2C_LONG_TIMEOUT 2000 /* 2 sec */
+
+/* read TPM_STS register */
+static u8 i2c_nuvoton_read_status(struct tpm_chip *chip)
+{
+ struct i2c_client *client = to_i2c_client(chip->dev);
+ s32 status;
+ u8 data;
+
+ status = i2c_nuvoton_read_buf(client, TPM_STS, 1, &data);
+ if (status <= 0) {
+ dev_err(chip->dev, "%s() error return %d\n", __func__,
+ status);
+ data = TPM_STS_ERR_VAL;
+ }
+
+ return data;
+}
+
+/* write byte to TPM_STS register */
+static s32 i2c_nuvoton_write_status(struct i2c_client *client, u8 data)
+{
+ s32 status;
+ int i;
+
+ /* this causes the current command to be aborted */
+ for (i = 0, status = -1; i < TPM_I2C_RETRY_COUNT && status < 0; i++) {
+ status = i2c_nuvoton_write_buf(client, TPM_STS, 1, &data);
+ msleep(TPM_I2C_BUS_DELAY);
+ }
+ return status;
+}
+
+/* write commandReady to TPM_STS register */
+static void i2c_nuvoton_ready(struct tpm_chip *chip)
+{
+ struct i2c_client *client = to_i2c_client(chip->dev);
+ s32 status;
+
+ /* this causes the current command to be aborted */
+ status = i2c_nuvoton_write_status(client, TPM_STS_COMMAND_READY);
+ if (status < 0)
+ dev_err(chip->dev,
+ "%s() fail to write TPM_STS.commandReady\n", __func__);
+}
+
+/* read burstCount field from TPM_STS register
+ * return -1 on fail to read */
+static int i2c_nuvoton_get_burstcount(struct i2c_client *client,
+ struct tpm_chip *chip)
+{
+ unsigned long stop = jiffies + chip->vendor.timeout_d;
+ s32 status;
+ int burst_count = -1;
+ u8 data;
+
+ /* wait for burstcount to be non-zero */
+ do {
+ /* in I2C burstCount is 1 byte */
+ status = i2c_nuvoton_read_buf(client, TPM_BURST_COUNT, 1,
+ &data);
+ if (status > 0 && data > 0) {
+ burst_count = min_t(u8, TPM_I2C_MAX_BUF_SIZE, data);
+ break;
+ }
+ msleep(TPM_I2C_BUS_DELAY);
+ } while (time_before(jiffies, stop));
+
+ return burst_count;
+}
+
+/*
+ * WPCT301/NPCT501 SINT# supports only dataAvail
+ * any call to this function which is not waiting for dataAvail will
+ * set queue to NULL to avoid waiting for interrupt
+ */
+static bool i2c_nuvoton_check_status(struct tpm_chip *chip, u8 mask, u8 value)
+{
+ u8 status = i2c_nuvoton_read_status(chip);
+ return (status != TPM_STS_ERR_VAL) && ((status & mask) == value);
+}
+
+static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value,
+ u32 timeout, wait_queue_head_t *queue)
+{
+ if (chip->vendor.irq && queue) {
+ s32 rc;
+ struct priv_data *priv = chip->vendor.priv;
+ unsigned int cur_intrs = priv->intrs;
+
+ enable_irq(chip->vendor.irq);
+ rc = wait_event_interruptible_timeout(*queue,
+ cur_intrs != priv->intrs,
+ timeout);
+ if (rc > 0)
+ return 0;
+ /* At this point we know that the SINT pin is asserted, so we
+ * do not need to do i2c_nuvoton_check_status */
+ } else {
+ unsigned long ten_msec, stop;
+ bool status_valid;
+
+ /* check current status */
+ status_valid = i2c_nuvoton_check_status(chip, mask, value);
+ if (status_valid)
+ return 0;
+
+ /* use polling to wait for the event */
+ ten_msec = jiffies + msecs_to_jiffies(TPM_I2C_RETRY_DELAY_LONG);
+ stop = jiffies + timeout;
+ do {
+ if (time_before(jiffies, ten_msec))
+ msleep(TPM_I2C_RETRY_DELAY_SHORT);
+ else
+ msleep(TPM_I2C_RETRY_DELAY_LONG);
+ status_valid = i2c_nuvoton_check_status(chip, mask,
+ value);
+ if (status_valid)
+ return 0;
+ } while (time_before(jiffies, stop));
+ }
+ dev_err(chip->dev, "%s(%02x, %02x) -> timeout\n", __func__, mask,
+ value);
+ return -ETIMEDOUT;
+}
+
+/* wait for dataAvail field to be set in the TPM_STS register */
+static int i2c_nuvoton_wait_for_data_avail(struct tpm_chip *chip, u32 timeout,
+ wait_queue_head_t *queue)
+{
+ return i2c_nuvoton_wait_for_stat(chip,
+ TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ timeout, queue);
+}
+
+/* Read @count bytes into @buf from TPM_RD_FIFO register */
+static int i2c_nuvoton_recv_data(struct i2c_client *client,
+ struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ s32 rc;
+ int burst_count, bytes2read, size = 0;
+
+ while (size < count &&
+ i2c_nuvoton_wait_for_data_avail(chip,
+ chip->vendor.timeout_c,
+ &chip->vendor.read_queue) == 0) {
+ burst_count = i2c_nuvoton_get_burstcount(client, chip);
+ if (burst_count < 0) {
+ dev_err(chip->dev,
+ "%s() fail to read burstCount=%d\n", __func__,
+ burst_count);
+ return -EIO;
+ }
+ bytes2read = min_t(size_t, burst_count, count - size);
+ rc = i2c_nuvoton_read_buf(client, TPM_DATA_FIFO_R,
+ bytes2read, &buf[size]);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "%s() fail on i2c_nuvoton_read_buf()=%d\n",
+ __func__, rc);
+ return -EIO;
+ }
+ dev_dbg(chip->dev, "%s(%d):", __func__, bytes2read);
+ size += bytes2read;
+ }
+
+ return size;
+}
+
+/* Read TPM command results */
+static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ struct device *dev = chip->dev;
+ struct i2c_client *client = to_i2c_client(dev);
+ s32 rc;
+ int expected, status, burst_count, retries, size = 0;
+
+ if (count < TPM_HEADER_SIZE) {
+ i2c_nuvoton_ready(chip); /* return to idle */
+ dev_err(dev, "%s() count < header size\n", __func__);
+ return -EIO;
+ }
+ for (retries = 0; retries < TPM_RETRY; retries++) {
+ if (retries > 0) {
+ /* if this is not the first trial, set responseRetry */
+ i2c_nuvoton_write_status(client,
+ TPM_STS_RESPONSE_RETRY);
+ }
+ /*
+ * read first available (> 10 bytes), including:
+ * tag, paramsize, and result
+ */
+ status = i2c_nuvoton_wait_for_data_avail(
+ chip, chip->vendor.timeout_c, &chip->vendor.read_queue);
+ if (status != 0) {
+ dev_err(dev, "%s() timeout on dataAvail\n", __func__);
+ size = -ETIMEDOUT;
+ continue;
+ }
+ burst_count = i2c_nuvoton_get_burstcount(client, chip);
+ if (burst_count < 0) {
+ dev_err(dev, "%s() fail to get burstCount\n", __func__);
+ size = -EIO;
+ continue;
+ }
+ size = i2c_nuvoton_recv_data(client, chip, buf,
+ burst_count);
+ if (size < TPM_HEADER_SIZE) {
+ dev_err(dev, "%s() fail to read header\n", __func__);
+ size = -EIO;
+ continue;
+ }
+ /*
+ * convert number of expected bytes field from big endian 32 bit
+ * to machine native
+ */
+ expected = be32_to_cpu(*(__be32 *) (buf + 2));
+ if (expected > count) {
+ dev_err(dev, "%s() expected > count\n", __func__);
+ size = -EIO;
+ continue;
+ }
+ rc = i2c_nuvoton_recv_data(client, chip, &buf[size],
+ expected - size);
+ size += rc;
+ if (rc < 0 || size < expected) {
+ dev_err(dev, "%s() fail to read remainder of result\n",
+ __func__);
+ size = -EIO;
+ continue;
+ }
+ if (i2c_nuvoton_wait_for_stat(
+ chip, TPM_STS_VALID | TPM_STS_DATA_AVAIL,
+ TPM_STS_VALID, chip->vendor.timeout_c,
+ NULL)) {
+ dev_err(dev, "%s() error left over data\n", __func__);
+ size = -ETIMEDOUT;
+ continue;
+ }
+ break;
+ }
+ i2c_nuvoton_ready(chip);
+ dev_dbg(chip->dev, "%s() -> %d\n", __func__, size);
+ return size;
+}
+
+/*
+ * Send TPM command.
+ *
+ * If interrupts are used (signaled by an irq set in the vendor structure)
+ * tpm.c can skip polling for the data to be available as the interrupt is
+ * waited for here
+ */
+static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+ struct device *dev = chip->dev;
+ struct i2c_client *client = to_i2c_client(dev);
+ u32 ordinal;
+ size_t count = 0;
+ int burst_count, bytes2write, retries, rc = -EIO;
+
+ for (retries = 0; retries < TPM_RETRY; retries++) {
+ i2c_nuvoton_ready(chip);
+ if (i2c_nuvoton_wait_for_stat(chip, TPM_STS_COMMAND_READY,
+ TPM_STS_COMMAND_READY,
+ chip->vendor.timeout_b, NULL)) {
+ dev_err(dev, "%s() timeout on commandReady\n",
+ __func__);
+ rc = -EIO;
+ continue;
+ }
+ rc = 0;
+ while (count < len - 1) {
+ burst_count = i2c_nuvoton_get_burstcount(client,
+ chip);
+ if (burst_count < 0) {
+ dev_err(dev, "%s() fail get burstCount\n",
+ __func__);
+ rc = -EIO;
+ break;
+ }
+ bytes2write = min_t(size_t, burst_count,
+ len - 1 - count);
+ rc = i2c_nuvoton_write_buf(client, TPM_DATA_FIFO_W,
+ bytes2write, &buf[count]);
+ if (rc < 0) {
+ dev_err(dev, "%s() fail i2cWriteBuf\n",
+ __func__);
+ break;
+ }
+ dev_dbg(dev, "%s(%d):", __func__, bytes2write);
+ count += bytes2write;
+ rc = i2c_nuvoton_wait_for_stat(chip,
+ TPM_STS_VALID |
+ TPM_STS_EXPECT,
+ TPM_STS_VALID |
+ TPM_STS_EXPECT,
+ chip->vendor.timeout_c,
+ NULL);
+ if (rc < 0) {
+ dev_err(dev, "%s() timeout on Expect\n",
+ __func__);
+ rc = -ETIMEDOUT;
+ break;
+ }
+ }
+ if (rc < 0)
+ continue;
+
+ /* write last byte */
+ rc = i2c_nuvoton_write_buf(client, TPM_DATA_FIFO_W, 1,
+ &buf[count]);
+ if (rc < 0) {
+ dev_err(dev, "%s() fail to write last byte\n",
+ __func__);
+ rc = -EIO;
+ continue;
+ }
+ dev_dbg(dev, "%s(last): %02x", __func__, buf[count]);
+ rc = i2c_nuvoton_wait_for_stat(chip,
+ TPM_STS_VALID | TPM_STS_EXPECT,
+ TPM_STS_VALID,
+ chip->vendor.timeout_c, NULL);
+ if (rc) {
+ dev_err(dev, "%s() timeout on Expect to clear\n",
+ __func__);
+ rc = -ETIMEDOUT;
+ continue;
+ }
+ break;
+ }
+ if (rc < 0) {
+ /* retries == TPM_RETRY */
+ i2c_nuvoton_ready(chip);
+ return rc;
+ }
+ /* execute the TPM command */
+ rc = i2c_nuvoton_write_status(client, TPM_STS_GO);
+ if (rc < 0) {
+ dev_err(dev, "%s() fail to write Go\n", __func__);
+ i2c_nuvoton_ready(chip);
+ return rc;
+ }
+ ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+ rc = i2c_nuvoton_wait_for_data_avail(chip,
+ tpm_calc_ordinal_duration(chip,
+ ordinal),
+ &chip->vendor.read_queue);
+ if (rc) {
+ dev_err(dev, "%s() timeout command duration\n", __func__);
+ i2c_nuvoton_ready(chip);
+ return rc;
+ }
+
+ dev_dbg(dev, "%s() -> %zd\n", __func__, len);
+ return len;
+}
+
+static bool i2c_nuvoton_req_canceled(struct tpm_chip *chip, u8 status)
+{
+ return (status == TPM_STS_COMMAND_READY);
+}
+
+static const struct tpm_class_ops tpm_i2c = {
+ .status = i2c_nuvoton_read_status,
+ .recv = i2c_nuvoton_recv,
+ .send = i2c_nuvoton_send,
+ .cancel = i2c_nuvoton_ready,
+ .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ .req_canceled = i2c_nuvoton_req_canceled,
+};
+
+/* The only purpose for the handler is to signal to any waiting threads that
+ * the interrupt is currently being asserted. The driver does not do any
+ * processing triggered by interrupts, and the chip provides no way to mask at
+ * the source (plus that would be slow over I2C). Run the IRQ as a one-shot,
+ * this means it cannot be shared. */
+static irqreturn_t i2c_nuvoton_int_handler(int dummy, void *dev_id)
+{
+ struct tpm_chip *chip = dev_id;
+ struct priv_data *priv = chip->vendor.priv;
+
+ priv->intrs++;
+ wake_up(&chip->vendor.read_queue);
+ disable_irq_nosync(chip->vendor.irq);
+ return IRQ_HANDLED;
+}
+
+static int get_vid(struct i2c_client *client, u32 *res)
+{
+ static const u8 vid_did_rid_value[] = { 0x50, 0x10, 0xfe };
+ u32 temp;
+ s32 rc;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+ rc = i2c_nuvoton_read_buf(client, TPM_VID_DID_RID, 4, (u8 *)&temp);
+ if (rc < 0)
+ return rc;
+
+ /* check WPCT301 values - ignore RID */
+ if (memcmp(&temp, vid_did_rid_value, sizeof(vid_did_rid_value))) {
+ /*
+ * f/w rev 2.81 has an issue where the VID_DID_RID is not
+ * reporting the right value. so give it another chance at
+ * offset 0x20 (FIFO_W).
+ */
+ rc = i2c_nuvoton_read_buf(client, TPM_DATA_FIFO_W, 4,
+ (u8 *) (&temp));
+ if (rc < 0)
+ return rc;
+
+ /* check WPCT301 values - ignore RID */
+ if (memcmp(&temp, vid_did_rid_value,
+ sizeof(vid_did_rid_value)))
+ return -ENODEV;
+ }
+
+ *res = temp;
+ return 0;
+}
+
+static int i2c_nuvoton_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc;
+ struct tpm_chip *chip;
+ struct device *dev = &client->dev;
+ u32 vid = 0;
+
+ rc = get_vid(client, &vid);
+ if (rc)
+ return rc;
+
+ dev_info(dev, "VID: %04X DID: %02X RID: %02X\n", (u16) vid,
+ (u8) (vid >> 16), (u8) (vid >> 24));
+
+ chip = tpm_register_hardware(dev, &tpm_i2c);
+ if (!chip) {
+ dev_err(dev, "%s() error in tpm_register_hardware\n", __func__);
+ return -ENODEV;
+ }
+
+ chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data),
+ GFP_KERNEL);
+ init_waitqueue_head(&chip->vendor.read_queue);
+ init_waitqueue_head(&chip->vendor.int_queue);
+
+ /* Default timeouts */
+ chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+ chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
+ chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+ chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+
+ /*
+ * I2C intfcaps (interrupt capabilitieis) in the chip are hard coded to:
+ * TPM_INTF_INT_LEVEL_LOW | TPM_INTF_DATA_AVAIL_INT
+ * The IRQ should be set in the i2c_board_info (which is done
+ * automatically in of_i2c_register_devices, for device tree users */
+ chip->vendor.irq = client->irq;
+
+ if (chip->vendor.irq) {
+ dev_dbg(dev, "%s() chip-vendor.irq\n", __func__);
+ rc = devm_request_irq(dev, chip->vendor.irq,
+ i2c_nuvoton_int_handler,
+ IRQF_TRIGGER_LOW,
+ chip->vendor.miscdev.name,
+ chip);
+ if (rc) {
+ dev_err(dev, "%s() Unable to request irq: %d for use\n",
+ __func__, chip->vendor.irq);
+ chip->vendor.irq = 0;
+ } else {
+ /* Clear any pending interrupt */
+ i2c_nuvoton_ready(chip);
+ /* - wait for TPM_STS==0xA0 (stsValid, commandReady) */
+ rc = i2c_nuvoton_wait_for_stat(chip,
+ TPM_STS_COMMAND_READY,
+ TPM_STS_COMMAND_READY,
+ chip->vendor.timeout_b,
+ NULL);
+ if (rc == 0) {
+ /*
+ * TIS is in ready state
+ * write dummy byte to enter reception state
+ * TPM_DATA_FIFO_W <- rc (0)
+ */
+ rc = i2c_nuvoton_write_buf(client,
+ TPM_DATA_FIFO_W,
+ 1, (u8 *) (&rc));
+ if (rc < 0)
+ goto out_err;
+ /* TPM_STS <- 0x40 (commandReady) */
+ i2c_nuvoton_ready(chip);
+ } else {
+ /*
+ * timeout_b reached - command was
+ * aborted. TIS should now be in idle state -
+ * only TPM_STS_VALID should be set
+ */
+ if (i2c_nuvoton_read_status(chip) !=
+ TPM_STS_VALID) {
+ rc = -EIO;
+ goto out_err;
+ }
+ }
+ }
+ }
+
+ if (tpm_get_timeouts(chip)) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ if (tpm_do_selftest(chip)) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ tpm_dev_vendor_release(chip);
+ tpm_remove_hardware(chip->dev);
+ return rc;
+}
+
+static int i2c_nuvoton_remove(struct i2c_client *client)
+{
+ struct device *dev = &(client->dev);
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+
+ if (chip)
+ tpm_dev_vendor_release(chip);
+ tpm_remove_hardware(dev);
+ kfree(chip);
+ return 0;
+}
+
+
+static const struct i2c_device_id i2c_nuvoton_id[] = {
+ {I2C_DRIVER_NAME, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, i2c_nuvoton_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id i2c_nuvoton_of_match[] = {
+ {.compatible = "nuvoton,npct501"},
+ {.compatible = "winbond,wpct301"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, i2c_nuvoton_of_match);
+#endif
+
+static SIMPLE_DEV_PM_OPS(i2c_nuvoton_pm_ops, tpm_pm_suspend, tpm_pm_resume);
+
+static struct i2c_driver i2c_nuvoton_driver = {
+ .id_table = i2c_nuvoton_id,
+ .probe = i2c_nuvoton_probe,
+ .remove = i2c_nuvoton_remove,
+ .driver = {
+ .name = I2C_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &i2c_nuvoton_pm_ops,
+ .of_match_table = of_match_ptr(i2c_nuvoton_of_match),
+ },
+};
+
+module_i2c_driver(i2c_nuvoton_driver);
+
+MODULE_AUTHOR("Dan Morav (dan.morav@nuvoton.com)");
+MODULE_DESCRIPTION("Nuvoton TPM I2C Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c
new file mode 100644
index 00000000000..3b7bf216289
--- /dev/null
+++ b/drivers/char/tpm/tpm_i2c_stm_st33.c
@@ -0,0 +1,844 @@
+/*
+ * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24
+ * Copyright (C) 2009, 2010 STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * STMicroelectronics version 1.2.0, Copyright (C) 2010
+ * STMicroelectronics comes with ABSOLUTELY NO WARRANTY.
+ * This is free software, and you are welcome to redistribute it
+ * under certain conditions.
+ *
+ * @Author: Christophe RICARD tpmsupport@st.com
+ *
+ * @File: tpm_stm_st33_i2c.c
+ *
+ * @Synopsis:
+ * 09/15/2010: First shot driver tpm_tis driver for
+ lpc is used as model.
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/sysfs.h>
+#include <linux/gpio.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include "tpm.h"
+#include "tpm_i2c_stm_st33.h"
+
+enum stm33zp24_access {
+ TPM_ACCESS_VALID = 0x80,
+ TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
+ TPM_ACCESS_REQUEST_PENDING = 0x04,
+ TPM_ACCESS_REQUEST_USE = 0x02,
+};
+
+enum stm33zp24_status {
+ TPM_STS_VALID = 0x80,
+ TPM_STS_COMMAND_READY = 0x40,
+ TPM_STS_GO = 0x20,
+ TPM_STS_DATA_AVAIL = 0x10,
+ TPM_STS_DATA_EXPECT = 0x08,
+};
+
+enum stm33zp24_int_flags {
+ TPM_GLOBAL_INT_ENABLE = 0x80,
+ TPM_INTF_CMD_READY_INT = 0x080,
+ TPM_INTF_FIFO_AVALAIBLE_INT = 0x040,
+ TPM_INTF_WAKE_UP_READY_INT = 0x020,
+ TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
+ TPM_INTF_STS_VALID_INT = 0x002,
+ TPM_INTF_DATA_AVAIL_INT = 0x001,
+};
+
+enum tis_defaults {
+ TIS_SHORT_TIMEOUT = 750,
+ TIS_LONG_TIMEOUT = 2000,
+};
+
+/*
+ * write8_reg
+ * Send byte to the TIS register according to the ST33ZP24 I2C protocol.
+ * @param: tpm_register, the tpm tis register where the data should be written
+ * @param: tpm_data, the tpm_data to write inside the tpm_register
+ * @param: tpm_size, The length of the data
+ * @return: Returns negative errno, or else the number of bytes written.
+ */
+static int write8_reg(struct i2c_client *client, u8 tpm_register,
+ u8 *tpm_data, u16 tpm_size)
+{
+ struct st33zp24_platform_data *pin_infos;
+
+ pin_infos = client->dev.platform_data;
+
+ pin_infos->tpm_i2c_buffer[0][0] = tpm_register;
+ memcpy(&pin_infos->tpm_i2c_buffer[0][1], tpm_data, tpm_size);
+ return i2c_master_send(client, pin_infos->tpm_i2c_buffer[0],
+ tpm_size + 1);
+} /* write8_reg() */
+
+/*
+ * read8_reg
+ * Recv byte from the TIS register according to the ST33ZP24 I2C protocol.
+ * @param: tpm_register, the tpm tis register where the data should be read
+ * @param: tpm_data, the TPM response
+ * @param: tpm_size, tpm TPM response size to read.
+ * @return: number of byte read successfully: should be one if success.
+ */
+static int read8_reg(struct i2c_client *client, u8 tpm_register,
+ u8 *tpm_data, int tpm_size)
+{
+ u8 status = 0;
+ u8 data;
+
+ data = TPM_DUMMY_BYTE;
+ status = write8_reg(client, tpm_register, &data, 1);
+ if (status == 2)
+ status = i2c_master_recv(client, tpm_data, tpm_size);
+ return status;
+} /* read8_reg() */
+
+/*
+ * I2C_WRITE_DATA
+ * Send byte to the TIS register according to the ST33ZP24 I2C protocol.
+ * @param: client, the chip description
+ * @param: tpm_register, the tpm tis register where the data should be written
+ * @param: tpm_data, the tpm_data to write inside the tpm_register
+ * @param: tpm_size, The length of the data
+ * @return: number of byte written successfully: should be one if success.
+ */
+#define I2C_WRITE_DATA(client, tpm_register, tpm_data, tpm_size) \
+ (write8_reg(client, tpm_register | \
+ TPM_WRITE_DIRECTION, tpm_data, tpm_size))
+
+/*
+ * I2C_READ_DATA
+ * Recv byte from the TIS register according to the ST33ZP24 I2C protocol.
+ * @param: tpm, the chip description
+ * @param: tpm_register, the tpm tis register where the data should be read
+ * @param: tpm_data, the TPM response
+ * @param: tpm_size, tpm TPM response size to read.
+ * @return: number of byte read successfully: should be one if success.
+ */
+#define I2C_READ_DATA(client, tpm_register, tpm_data, tpm_size) \
+ (read8_reg(client, tpm_register, tpm_data, tpm_size))
+
+/*
+ * clear_interruption
+ * clear the TPM interrupt register.
+ * @param: tpm, the chip description
+ */
+static void clear_interruption(struct i2c_client *client)
+{
+ u8 interrupt;
+ I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1);
+ I2C_WRITE_DATA(client, TPM_INT_STATUS, &interrupt, 1);
+ I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1);
+} /* clear_interruption() */
+
+/*
+ * _wait_for_interrupt_serirq_timeout
+ * @param: tpm, the chip description
+ * @param: timeout, the timeout of the interrupt
+ * @return: the status of the interruption.
+ */
+static long _wait_for_interrupt_serirq_timeout(struct tpm_chip *chip,
+ unsigned long timeout)
+{
+ long status;
+ struct i2c_client *client;
+ struct st33zp24_platform_data *pin_infos;
+
+ client = (struct i2c_client *)TPM_VPRIV(chip);
+ pin_infos = client->dev.platform_data;
+
+ status = wait_for_completion_interruptible_timeout(
+ &pin_infos->irq_detection,
+ timeout);
+ if (status > 0)
+ enable_irq(gpio_to_irq(pin_infos->io_serirq));
+ gpio_direction_input(pin_infos->io_serirq);
+
+ return status;
+} /* wait_for_interrupt_serirq_timeout() */
+
+static int wait_for_serirq_timeout(struct tpm_chip *chip, bool condition,
+ unsigned long timeout)
+{
+ int status = 2;
+ struct i2c_client *client;
+
+ client = (struct i2c_client *)TPM_VPRIV(chip);
+
+ status = _wait_for_interrupt_serirq_timeout(chip, timeout);
+ if (!status) {
+ status = -EBUSY;
+ } else {
+ clear_interruption(client);
+ if (condition)
+ status = 1;
+ }
+ return status;
+}
+
+/*
+ * tpm_stm_i2c_cancel, cancel is not implemented.
+ * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h
+ */
+static void tpm_stm_i2c_cancel(struct tpm_chip *chip)
+{
+ struct i2c_client *client;
+ u8 data;
+
+ client = (struct i2c_client *)TPM_VPRIV(chip);
+
+ data = TPM_STS_COMMAND_READY;
+ I2C_WRITE_DATA(client, TPM_STS, &data, 1);
+ if (chip->vendor.irq)
+ wait_for_serirq_timeout(chip, 1, chip->vendor.timeout_a);
+} /* tpm_stm_i2c_cancel() */
+
+/*
+ * tpm_stm_spi_status return the TPM_STS register
+ * @param: chip, the tpm chip description
+ * @return: the TPM_STS register value.
+ */
+static u8 tpm_stm_i2c_status(struct tpm_chip *chip)
+{
+ struct i2c_client *client;
+ u8 data;
+ client = (struct i2c_client *)TPM_VPRIV(chip);
+
+ I2C_READ_DATA(client, TPM_STS, &data, 1);
+ return data;
+} /* tpm_stm_i2c_status() */
+
+
+/*
+ * check_locality if the locality is active
+ * @param: chip, the tpm chip description
+ * @return: the active locality or -EACCESS.
+ */
+static int check_locality(struct tpm_chip *chip)
+{
+ struct i2c_client *client;
+ u8 data;
+ u8 status;
+
+ client = (struct i2c_client *)TPM_VPRIV(chip);
+
+ status = I2C_READ_DATA(client, TPM_ACCESS, &data, 1);
+ if (status && (data &
+ (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
+ (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
+ return chip->vendor.locality;
+
+ return -EACCES;
+
+} /* check_locality() */
+
+/*
+ * request_locality request the TPM locality
+ * @param: chip, the chip description
+ * @return: the active locality or EACCESS.
+ */
+static int request_locality(struct tpm_chip *chip)
+{
+ unsigned long stop;
+ long rc;
+ struct i2c_client *client;
+ u8 data;
+
+ client = (struct i2c_client *)TPM_VPRIV(chip);
+
+ if (check_locality(chip) == chip->vendor.locality)
+ return chip->vendor.locality;
+
+ data = TPM_ACCESS_REQUEST_USE;
+ rc = I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1);
+ if (rc < 0)
+ goto end;
+
+ if (chip->vendor.irq) {
+ rc = wait_for_serirq_timeout(chip, (check_locality
+ (chip) >= 0),
+ chip->vendor.timeout_a);
+ if (rc > 0)
+ return chip->vendor.locality;
+ } else {
+ stop = jiffies + chip->vendor.timeout_a;
+ do {
+ if (check_locality(chip) >= 0)
+ return chip->vendor.locality;
+ msleep(TPM_TIMEOUT);
+ } while (time_before(jiffies, stop));
+ }
+ rc = -EACCES;
+end:
+ return rc;
+} /* request_locality() */
+
+/*
+ * release_locality release the active locality
+ * @param: chip, the tpm chip description.
+ */
+static void release_locality(struct tpm_chip *chip)
+{
+ struct i2c_client *client;
+ u8 data;
+
+ client = (struct i2c_client *)TPM_VPRIV(chip);
+ data = TPM_ACCESS_ACTIVE_LOCALITY;
+
+ I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1);
+}
+
+/*
+ * get_burstcount return the burstcount address 0x19 0x1A
+ * @param: chip, the chip description
+ * return: the burstcount.
+ */
+static int get_burstcount(struct tpm_chip *chip)
+{
+ unsigned long stop;
+ int burstcnt, status;
+ u8 tpm_reg, temp;
+
+ struct i2c_client *client = (struct i2c_client *)TPM_VPRIV(chip);
+
+ stop = jiffies + chip->vendor.timeout_d;
+ do {
+ tpm_reg = TPM_STS + 1;
+ status = I2C_READ_DATA(client, tpm_reg, &temp, 1);
+ if (status < 0)
+ goto end;
+
+ tpm_reg = tpm_reg + 1;
+ burstcnt = temp;
+ status = I2C_READ_DATA(client, tpm_reg, &temp, 1);
+ if (status < 0)
+ goto end;
+
+ burstcnt |= temp << 8;
+ if (burstcnt)
+ return burstcnt;
+ msleep(TPM_TIMEOUT);
+ } while (time_before(jiffies, stop));
+
+end:
+ return -EBUSY;
+} /* get_burstcount() */
+
+/*
+ * wait_for_stat wait for a TPM_STS value
+ * @param: chip, the tpm chip description
+ * @param: mask, the value mask to wait
+ * @param: timeout, the timeout
+ * @param: queue, the wait queue.
+ * @return: the tpm status, 0 if success, -ETIME if timeout is reached.
+ */
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
+ wait_queue_head_t *queue)
+{
+ unsigned long stop;
+ long rc;
+ u8 status;
+
+ if (chip->vendor.irq) {
+ rc = wait_for_serirq_timeout(chip, ((tpm_stm_i2c_status
+ (chip) & mask) ==
+ mask), timeout);
+ if (rc > 0)
+ return 0;
+ } else {
+ stop = jiffies + timeout;
+ do {
+ msleep(TPM_TIMEOUT);
+ status = tpm_stm_i2c_status(chip);
+ if ((status & mask) == mask)
+ return 0;
+ } while (time_before(jiffies, stop));
+ }
+ return -ETIME;
+} /* wait_for_stat() */
+
+/*
+ * recv_data receive data
+ * @param: chip, the tpm chip description
+ * @param: buf, the buffer where the data are received
+ * @param: count, the number of data to receive
+ * @return: the number of bytes read from TPM FIFO.
+ */
+static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ int size = 0, burstcnt, len;
+ struct i2c_client *client;
+
+ client = (struct i2c_client *)TPM_VPRIV(chip);
+
+ while (size < count &&
+ wait_for_stat(chip,
+ TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ chip->vendor.timeout_c,
+ &chip->vendor.read_queue)
+ == 0) {
+ burstcnt = get_burstcount(chip);
+ if (burstcnt < 0)
+ return burstcnt;
+ len = min_t(int, burstcnt, count - size);
+ I2C_READ_DATA(client, TPM_DATA_FIFO, buf + size, len);
+ size += len;
+ }
+ return size;
+}
+
+/*
+ * tpm_ioserirq_handler the serirq irq handler
+ * @param: irq, the tpm chip description
+ * @param: dev_id, the description of the chip
+ * @return: the status of the handler.
+ */
+static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id)
+{
+ struct tpm_chip *chip = dev_id;
+ struct i2c_client *client;
+ struct st33zp24_platform_data *pin_infos;
+
+ disable_irq_nosync(irq);
+
+ client = (struct i2c_client *)TPM_VPRIV(chip);
+ pin_infos = client->dev.platform_data;
+
+ complete(&pin_infos->irq_detection);
+ return IRQ_HANDLED;
+} /* tpm_ioserirq_handler() */
+
+
+/*
+ * tpm_stm_i2c_send send TPM commands through the I2C bus.
+ *
+ * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h
+ * @param: buf, the buffer to send.
+ * @param: count, the number of bytes to send.
+ * @return: In case of success the number of bytes sent.
+ * In other case, a < 0 value describing the issue.
+ */
+static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf,
+ size_t len)
+{
+ u32 status, i, size;
+ int burstcnt = 0;
+ int ret;
+ u8 data;
+ struct i2c_client *client;
+
+ if (chip == NULL)
+ return -EBUSY;
+ if (len < TPM_HEADER_SIZE)
+ return -EBUSY;
+
+ client = (struct i2c_client *)TPM_VPRIV(chip);
+
+ client->flags = 0;
+
+ ret = request_locality(chip);
+ if (ret < 0)
+ return ret;
+
+ status = tpm_stm_i2c_status(chip);
+ if ((status & TPM_STS_COMMAND_READY) == 0) {
+ tpm_stm_i2c_cancel(chip);
+ if (wait_for_stat
+ (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
+ &chip->vendor.int_queue) < 0) {
+ ret = -ETIME;
+ goto out_err;
+ }
+ }
+
+ for (i = 0; i < len - 1;) {
+ burstcnt = get_burstcount(chip);
+ if (burstcnt < 0)
+ return burstcnt;
+ size = min_t(int, len - i - 1, burstcnt);
+ ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf, size);
+ if (ret < 0)
+ goto out_err;
+
+ i += size;
+ }
+
+ status = tpm_stm_i2c_status(chip);
+ if ((status & TPM_STS_DATA_EXPECT) == 0) {
+ ret = -EIO;
+ goto out_err;
+ }
+
+ ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf + len - 1, 1);
+ if (ret < 0)
+ goto out_err;
+
+ status = tpm_stm_i2c_status(chip);
+ if ((status & TPM_STS_DATA_EXPECT) != 0) {
+ ret = -EIO;
+ goto out_err;
+ }
+
+ data = TPM_STS_GO;
+ I2C_WRITE_DATA(client, TPM_STS, &data, 1);
+
+ return len;
+out_err:
+ tpm_stm_i2c_cancel(chip);
+ release_locality(chip);
+ return ret;
+}
+
+/*
+ * tpm_stm_i2c_recv received TPM response through the I2C bus.
+ * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h.
+ * @param: buf, the buffer to store datas.
+ * @param: count, the number of bytes to send.
+ * @return: In case of success the number of bytes received.
+ * In other case, a < 0 value describing the issue.
+ */
+static int tpm_stm_i2c_recv(struct tpm_chip *chip, unsigned char *buf,
+ size_t count)
+{
+ int size = 0;
+ int expected;
+
+ if (chip == NULL)
+ return -EBUSY;
+
+ if (count < TPM_HEADER_SIZE) {
+ size = -EIO;
+ goto out;
+ }
+
+ size = recv_data(chip, buf, TPM_HEADER_SIZE);
+ if (size < TPM_HEADER_SIZE) {
+ dev_err(chip->dev, "Unable to read header\n");
+ goto out;
+ }
+
+ expected = be32_to_cpu(*(__be32 *)(buf + 2));
+ if (expected > count) {
+ size = -EIO;
+ goto out;
+ }
+
+ size += recv_data(chip, &buf[TPM_HEADER_SIZE],
+ expected - TPM_HEADER_SIZE);
+ if (size < expected) {
+ dev_err(chip->dev, "Unable to read remainder of result\n");
+ size = -ETIME;
+ goto out;
+ }
+
+out:
+ chip->ops->cancel(chip);
+ release_locality(chip);
+ return size;
+}
+
+static bool tpm_st33_i2c_req_canceled(struct tpm_chip *chip, u8 status)
+{
+ return (status == TPM_STS_COMMAND_READY);
+}
+
+static const struct tpm_class_ops st_i2c_tpm = {
+ .send = tpm_stm_i2c_send,
+ .recv = tpm_stm_i2c_recv,
+ .cancel = tpm_stm_i2c_cancel,
+ .status = tpm_stm_i2c_status,
+ .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ .req_canceled = tpm_st33_i2c_req_canceled,
+};
+
+static int interrupts;
+module_param(interrupts, int, 0444);
+MODULE_PARM_DESC(interrupts, "Enable interrupts");
+
+static int power_mgt = 1;
+module_param(power_mgt, int, 0444);
+MODULE_PARM_DESC(power_mgt, "Power Management");
+
+/*
+ * tpm_st33_i2c_probe initialize the TPM device
+ * @param: client, the i2c_client drescription (TPM I2C description).
+ * @param: id, the i2c_device_id struct.
+ * @return: 0 in case of success.
+ * -1 in other case.
+ */
+static int
+tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int err;
+ u8 intmask;
+ struct tpm_chip *chip;
+ struct st33zp24_platform_data *platform_data;
+
+ if (client == NULL) {
+ pr_info("%s: i2c client is NULL. Device not accessible.\n",
+ __func__);
+ err = -ENODEV;
+ goto end;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_info(&client->dev, "client not i2c capable\n");
+ err = -ENODEV;
+ goto end;
+ }
+
+ chip = tpm_register_hardware(&client->dev, &st_i2c_tpm);
+ if (!chip) {
+ dev_info(&client->dev, "fail chip\n");
+ err = -ENODEV;
+ goto end;
+ }
+
+ platform_data = client->dev.platform_data;
+
+ if (!platform_data) {
+ dev_info(&client->dev, "chip not available\n");
+ err = -ENODEV;
+ goto _tpm_clean_answer;
+ }
+
+ platform_data->tpm_i2c_buffer[0] =
+ kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
+ if (platform_data->tpm_i2c_buffer[0] == NULL) {
+ err = -ENOMEM;
+ goto _tpm_clean_answer;
+ }
+ platform_data->tpm_i2c_buffer[1] =
+ kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
+ if (platform_data->tpm_i2c_buffer[1] == NULL) {
+ err = -ENOMEM;
+ goto _tpm_clean_response1;
+ }
+
+ TPM_VPRIV(chip) = client;
+
+ chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+ chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
+ chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+ chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+
+ chip->vendor.locality = LOCALITY0;
+
+ if (power_mgt) {
+ err = gpio_request(platform_data->io_lpcpd, "TPM IO_LPCPD");
+ if (err)
+ goto _gpio_init1;
+ gpio_set_value(platform_data->io_lpcpd, 1);
+ }
+
+ if (interrupts) {
+ init_completion(&platform_data->irq_detection);
+ if (request_locality(chip) != LOCALITY0) {
+ err = -ENODEV;
+ goto _tpm_clean_response2;
+ }
+ err = gpio_request(platform_data->io_serirq, "TPM IO_SERIRQ");
+ if (err)
+ goto _gpio_init2;
+
+ clear_interruption(client);
+ err = request_irq(gpio_to_irq(platform_data->io_serirq),
+ &tpm_ioserirq_handler,
+ IRQF_TRIGGER_HIGH,
+ "TPM SERIRQ management", chip);
+ if (err < 0) {
+ dev_err(chip->dev , "TPM SERIRQ signals %d not available\n",
+ gpio_to_irq(platform_data->io_serirq));
+ goto _irq_set;
+ }
+
+ err = I2C_READ_DATA(client, TPM_INT_ENABLE, &intmask, 1);
+ if (err < 0)
+ goto _irq_set;
+
+ intmask |= TPM_INTF_CMD_READY_INT
+ | TPM_INTF_FIFO_AVALAIBLE_INT
+ | TPM_INTF_WAKE_UP_READY_INT
+ | TPM_INTF_LOCALITY_CHANGE_INT
+ | TPM_INTF_STS_VALID_INT
+ | TPM_INTF_DATA_AVAIL_INT;
+
+ err = I2C_WRITE_DATA(client, TPM_INT_ENABLE, &intmask, 1);
+ if (err < 0)
+ goto _irq_set;
+
+ intmask = TPM_GLOBAL_INT_ENABLE;
+ err = I2C_WRITE_DATA(client, (TPM_INT_ENABLE + 3), &intmask, 1);
+ if (err < 0)
+ goto _irq_set;
+
+ err = I2C_READ_DATA(client, TPM_INT_STATUS, &intmask, 1);
+ if (err < 0)
+ goto _irq_set;
+
+ chip->vendor.irq = interrupts;
+
+ tpm_gen_interrupt(chip);
+ }
+
+ tpm_get_timeouts(chip);
+
+ dev_info(chip->dev, "TPM I2C Initialized\n");
+ return 0;
+_irq_set:
+ free_irq(gpio_to_irq(platform_data->io_serirq), (void *)chip);
+_gpio_init2:
+ if (interrupts)
+ gpio_free(platform_data->io_serirq);
+_gpio_init1:
+ if (power_mgt)
+ gpio_free(platform_data->io_lpcpd);
+_tpm_clean_response2:
+ kzfree(platform_data->tpm_i2c_buffer[1]);
+ platform_data->tpm_i2c_buffer[1] = NULL;
+_tpm_clean_response1:
+ kzfree(platform_data->tpm_i2c_buffer[0]);
+ platform_data->tpm_i2c_buffer[0] = NULL;
+_tpm_clean_answer:
+ tpm_remove_hardware(chip->dev);
+end:
+ pr_info("TPM I2C initialisation fail\n");
+ return err;
+}
+
+/*
+ * tpm_st33_i2c_remove remove the TPM device
+ * @param: client, the i2c_client drescription (TPM I2C description).
+ clear_bit(0, &chip->is_open);
+ * @return: 0 in case of success.
+ */
+static int tpm_st33_i2c_remove(struct i2c_client *client)
+{
+ struct tpm_chip *chip = (struct tpm_chip *)i2c_get_clientdata(client);
+ struct st33zp24_platform_data *pin_infos =
+ ((struct i2c_client *)TPM_VPRIV(chip))->dev.platform_data;
+
+ if (pin_infos != NULL) {
+ free_irq(pin_infos->io_serirq, chip);
+
+ gpio_free(pin_infos->io_serirq);
+ gpio_free(pin_infos->io_lpcpd);
+
+ tpm_remove_hardware(chip->dev);
+
+ if (pin_infos->tpm_i2c_buffer[1] != NULL) {
+ kzfree(pin_infos->tpm_i2c_buffer[1]);
+ pin_infos->tpm_i2c_buffer[1] = NULL;
+ }
+ if (pin_infos->tpm_i2c_buffer[0] != NULL) {
+ kzfree(pin_infos->tpm_i2c_buffer[0]);
+ pin_infos->tpm_i2c_buffer[0] = NULL;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * tpm_st33_i2c_pm_suspend suspend the TPM device
+ * @param: client, the i2c_client drescription (TPM I2C description).
+ * @param: mesg, the power management message.
+ * @return: 0 in case of success.
+ */
+static int tpm_st33_i2c_pm_suspend(struct device *dev)
+{
+ struct st33zp24_platform_data *pin_infos = dev->platform_data;
+ int ret = 0;
+
+ if (power_mgt) {
+ gpio_set_value(pin_infos->io_lpcpd, 0);
+ } else {
+ ret = tpm_pm_suspend(dev);
+ }
+ return ret;
+} /* tpm_st33_i2c_suspend() */
+
+/*
+ * tpm_st33_i2c_pm_resume resume the TPM device
+ * @param: client, the i2c_client drescription (TPM I2C description).
+ * @return: 0 in case of success.
+ */
+static int tpm_st33_i2c_pm_resume(struct device *dev)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+ struct st33zp24_platform_data *pin_infos = dev->platform_data;
+
+ int ret = 0;
+
+ if (power_mgt) {
+ gpio_set_value(pin_infos->io_lpcpd, 1);
+ ret = wait_for_serirq_timeout(chip,
+ (chip->ops->status(chip) &
+ TPM_STS_VALID) == TPM_STS_VALID,
+ chip->vendor.timeout_b);
+ } else {
+ ret = tpm_pm_resume(dev);
+ if (!ret)
+ tpm_do_selftest(chip);
+ }
+ return ret;
+} /* tpm_st33_i2c_pm_resume() */
+#endif
+
+static const struct i2c_device_id tpm_st33_i2c_id[] = {
+ {TPM_ST33_I2C, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, tpm_st33_i2c_id);
+static SIMPLE_DEV_PM_OPS(tpm_st33_i2c_ops, tpm_st33_i2c_pm_suspend,
+ tpm_st33_i2c_pm_resume);
+static struct i2c_driver tpm_st33_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = TPM_ST33_I2C,
+ .pm = &tpm_st33_i2c_ops,
+ },
+ .probe = tpm_st33_i2c_probe,
+ .remove = tpm_st33_i2c_remove,
+ .id_table = tpm_st33_i2c_id
+};
+
+module_i2c_driver(tpm_st33_i2c_driver);
+
+MODULE_AUTHOR("Christophe Ricard (tpmsupport@st.com)");
+MODULE_DESCRIPTION("STM TPM I2C ST33 Driver");
+MODULE_VERSION("1.2.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.h b/drivers/char/tpm/tpm_i2c_stm_st33.h
new file mode 100644
index 00000000000..439a43249aa
--- /dev/null
+++ b/drivers/char/tpm/tpm_i2c_stm_st33.h
@@ -0,0 +1,61 @@
+/*
+ * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24
+ * Copyright (C) 2009, 2010 STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * STMicroelectronics version 1.2.0, Copyright (C) 2010
+ * STMicroelectronics comes with ABSOLUTELY NO WARRANTY.
+ * This is free software, and you are welcome to redistribute it
+ * under certain conditions.
+ *
+ * @Author: Christophe RICARD tpmsupport@st.com
+ *
+ * @File: stm_st33_tpm_i2c.h
+ *
+ * @Date: 09/15/2010
+ */
+#ifndef __STM_ST33_TPM_I2C_MAIN_H__
+#define __STM_ST33_TPM_I2C_MAIN_H__
+
+#define TPM_ACCESS (0x0)
+#define TPM_STS (0x18)
+#define TPM_HASH_END (0x20)
+#define TPM_DATA_FIFO (0x24)
+#define TPM_HASH_DATA (0x24)
+#define TPM_HASH_START (0x28)
+#define TPM_INTF_CAPABILITY (0x14)
+#define TPM_INT_STATUS (0x10)
+#define TPM_INT_ENABLE (0x08)
+
+#define TPM_DUMMY_BYTE 0xAA
+#define TPM_WRITE_DIRECTION 0x80
+#define TPM_HEADER_SIZE 10
+#define TPM_BUFSIZE 2048
+
+#define LOCALITY0 0
+
+#define TPM_ST33_I2C "st33zp24_i2c"
+
+struct st33zp24_platform_data {
+ int io_serirq;
+ int io_lpcpd;
+ struct i2c_client *client;
+ u8 *tpm_i2c_buffer[2]; /* 0 Request 1 Response */
+ struct completion irq_detection;
+ struct mutex lock;
+};
+
+#endif /* __STM_ST33_TPM_I2C_MAIN_H__ */
diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c
index 9978609d93b..af74c57e509 100644
--- a/drivers/char/tpm/tpm_ibmvtpm.c
+++ b/drivers/char/tpm/tpm_ibmvtpm.c
@@ -64,7 +64,7 @@ static struct ibmvtpm_dev *ibmvtpm_get_data(const struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
if (chip)
- return (struct ibmvtpm_dev *)chip->vendor.data;
+ return (struct ibmvtpm_dev *)TPM_VPRIV(chip);
return NULL;
}
@@ -83,7 +83,7 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
u16 len;
int sig;
- ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data;
+ ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip);
if (!ibmvtpm->rtce_buf) {
dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
@@ -98,7 +98,7 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
if (count < len) {
dev_err(ibmvtpm->dev,
- "Invalid size in recv: count=%ld, crq_size=%d\n",
+ "Invalid size in recv: count=%zd, crq_size=%d\n",
count, len);
return -EIO;
}
@@ -127,7 +127,7 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
u64 *word = (u64 *) &crq;
int rc;
- ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data;
+ ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip);
if (!ibmvtpm->rtce_buf) {
dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
@@ -136,7 +136,7 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
if (count > ibmvtpm->rtce_size) {
dev_err(ibmvtpm->dev,
- "Invalid size in send: count=%ld, rtce_size=%d\n",
+ "Invalid size in send: count=%zd, rtce_size=%d\n",
count, ibmvtpm->rtce_size);
return -EIO;
}
@@ -398,52 +398,19 @@ static int tpm_ibmvtpm_resume(struct device *dev)
return rc;
}
-static const struct file_operations ibmvtpm_ops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .open = tpm_open,
- .read = tpm_read,
- .write = tpm_write,
- .release = tpm_release,
-};
-
-static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
-static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
-static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
-static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
-static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
-static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
- NULL);
-static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
-static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
-static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
-static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
-
-static struct attribute *ibmvtpm_attrs[] = {
- &dev_attr_pubek.attr,
- &dev_attr_pcrs.attr,
- &dev_attr_enabled.attr,
- &dev_attr_active.attr,
- &dev_attr_owned.attr,
- &dev_attr_temp_deactivated.attr,
- &dev_attr_caps.attr,
- &dev_attr_cancel.attr,
- &dev_attr_durations.attr,
- &dev_attr_timeouts.attr, NULL,
-};
-
-static struct attribute_group ibmvtpm_attr_grp = { .attrs = ibmvtpm_attrs };
+static bool tpm_ibmvtpm_req_canceled(struct tpm_chip *chip, u8 status)
+{
+ return (status == 0);
+}
-static const struct tpm_vendor_specific tpm_ibmvtpm = {
+static const struct tpm_class_ops tpm_ibmvtpm = {
.recv = tpm_ibmvtpm_recv,
.send = tpm_ibmvtpm_send,
.cancel = tpm_ibmvtpm_cancel,
.status = tpm_ibmvtpm_status,
.req_complete_mask = 0,
.req_complete_val = 0,
- .req_canceled = 0,
- .attr_group = &ibmvtpm_attr_grp,
- .miscdev = { .fops = &ibmvtpm_ops, },
+ .req_canceled = tpm_ibmvtpm_req_canceled,
};
static const struct dev_pm_ops tpm_ibmvtpm_pm_ops = {
@@ -502,7 +469,6 @@ static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq,
dev_err(ibmvtpm->dev, "Unknown crq message type: %d\n", crq->msg);
return;
}
- return;
case IBMVTPM_VALID_CMD:
switch (crq->msg) {
case VTPM_GET_RTCE_BUFFER_SIZE_RES:
@@ -647,7 +613,7 @@ static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev,
ibmvtpm->dev = dev;
ibmvtpm->vdev = vio_dev;
- chip->vendor.data = (void *)ibmvtpm;
+ TPM_VPRIV(chip) = (void *)ibmvtpm;
spin_lock_init(&ibmvtpm->rtce_lock);
diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c
index 2b480c2960b..dc0a2554034 100644
--- a/drivers/char/tpm/tpm_infineon.c
+++ b/drivers/char/tpm/tpm_infineon.c
@@ -371,39 +371,13 @@ static u8 tpm_inf_status(struct tpm_chip *chip)
return tpm_data_in(STAT);
}
-static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
-static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
-static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
-static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
-
-static struct attribute *inf_attrs[] = {
- &dev_attr_pubek.attr,
- &dev_attr_pcrs.attr,
- &dev_attr_caps.attr,
- &dev_attr_cancel.attr,
- NULL,
-};
-
-static struct attribute_group inf_attr_grp = {.attrs = inf_attrs };
-
-static const struct file_operations inf_ops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .open = tpm_open,
- .read = tpm_read,
- .write = tpm_write,
- .release = tpm_release,
-};
-
-static const struct tpm_vendor_specific tpm_inf = {
+static const struct tpm_class_ops tpm_inf = {
.recv = tpm_inf_recv,
.send = tpm_inf_send,
.cancel = tpm_inf_cancel,
.status = tpm_inf_status,
.req_complete_mask = 0,
.req_complete_val = 0,
- .attr_group = &inf_attr_grp,
- .miscdev = {.fops = &inf_ops,},
};
static const struct pnp_device_id tpm_inf_pnp_tbl[] = {
diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c
index 640c9a427b5..3179ec9cffd 100644
--- a/drivers/char/tpm/tpm_nsc.c
+++ b/drivers/char/tpm/tpm_nsc.c
@@ -227,40 +227,19 @@ static u8 tpm_nsc_status(struct tpm_chip *chip)
return inb(chip->vendor.base + NSC_STATUS);
}
-static const struct file_operations nsc_ops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .open = tpm_open,
- .read = tpm_read,
- .write = tpm_write,
- .release = tpm_release,
-};
-
-static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
-static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
-static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
-static DEVICE_ATTR(cancel, S_IWUSR|S_IWGRP, NULL, tpm_store_cancel);
-
-static struct attribute * nsc_attrs[] = {
- &dev_attr_pubek.attr,
- &dev_attr_pcrs.attr,
- &dev_attr_caps.attr,
- &dev_attr_cancel.attr,
- NULL,
-};
-
-static struct attribute_group nsc_attr_grp = { .attrs = nsc_attrs };
+static bool tpm_nsc_req_canceled(struct tpm_chip *chip, u8 status)
+{
+ return (status == NSC_STATUS_RDY);
+}
-static const struct tpm_vendor_specific tpm_nsc = {
+static const struct tpm_class_ops tpm_nsc = {
.recv = tpm_nsc_recv,
.send = tpm_nsc_send,
.cancel = tpm_nsc_cancel,
.status = tpm_nsc_status,
.req_complete_mask = NSC_STATUS_OBF,
.req_complete_val = NSC_STATUS_OBF,
- .req_canceled = NSC_STATUS_RDY,
- .attr_group = &nsc_attr_grp,
- .miscdev = { .fops = &nsc_ops, },
+ .req_canceled = tpm_nsc_req_canceled,
};
static struct platform_device *pdev = NULL;
diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c
index 720ebcf29fd..61dcc8011ec 100644
--- a/drivers/char/tpm/tpm_ppi.c
+++ b/drivers/char/tpm/tpm_ppi.c
@@ -1,16 +1,6 @@
#include <linux/acpi.h>
-#include <acpi/acpi_drivers.h>
#include "tpm.h"
-static const u8 tpm_ppi_uuid[] = {
- 0xA6, 0xFA, 0xDD, 0x3D,
- 0x1B, 0x36,
- 0xB4, 0x4E,
- 0xA4, 0x24,
- 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53
-};
-static char *tpm_device_name = "TPM";
-
#define TPM_PPI_REVISION_ID 1
#define TPM_PPI_FN_VERSION 1
#define TPM_PPI_FN_SUBREQ 2
@@ -24,247 +14,178 @@ static char *tpm_device_name = "TPM";
#define PPI_VS_REQ_END 255
#define PPI_VERSION_LEN 3
+static const u8 tpm_ppi_uuid[] = {
+ 0xA6, 0xFA, 0xDD, 0x3D,
+ 0x1B, 0x36,
+ 0xB4, 0x4E,
+ 0xA4, 0x24,
+ 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53
+};
+
+static char tpm_ppi_version[PPI_VERSION_LEN + 1];
+static acpi_handle tpm_ppi_handle;
+
static acpi_status ppi_callback(acpi_handle handle, u32 level, void *context,
void **return_value)
{
- acpi_status status;
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
- if (strstr(buffer.pointer, context) != NULL) {
- *return_value = handle;
- kfree(buffer.pointer);
- return AE_CTRL_TERMINATE;
+ union acpi_object *obj;
+
+ if (!acpi_check_dsm(handle, tpm_ppi_uuid, TPM_PPI_REVISION_ID,
+ 1 << TPM_PPI_FN_VERSION))
+ return AE_OK;
+
+ /* Cache version string */
+ obj = acpi_evaluate_dsm_typed(handle, tpm_ppi_uuid,
+ TPM_PPI_REVISION_ID, TPM_PPI_FN_VERSION,
+ NULL, ACPI_TYPE_STRING);
+ if (obj) {
+ strlcpy(tpm_ppi_version, obj->string.pointer,
+ PPI_VERSION_LEN + 1);
+ ACPI_FREE(obj);
}
- return AE_OK;
+
+ *return_value = handle;
+
+ return AE_CTRL_TERMINATE;
}
-static inline void ppi_assign_params(union acpi_object params[4],
- u64 function_num)
+static inline union acpi_object *
+tpm_eval_dsm(int func, acpi_object_type type, union acpi_object *argv4)
{
- params[0].type = ACPI_TYPE_BUFFER;
- params[0].buffer.length = sizeof(tpm_ppi_uuid);
- params[0].buffer.pointer = (char *)tpm_ppi_uuid;
- params[1].type = ACPI_TYPE_INTEGER;
- params[1].integer.value = TPM_PPI_REVISION_ID;
- params[2].type = ACPI_TYPE_INTEGER;
- params[2].integer.value = function_num;
- params[3].type = ACPI_TYPE_PACKAGE;
- params[3].package.count = 0;
- params[3].package.elements = NULL;
+ BUG_ON(!tpm_ppi_handle);
+ return acpi_evaluate_dsm_typed(tpm_ppi_handle, tpm_ppi_uuid,
+ TPM_PPI_REVISION_ID, func, argv4, type);
}
static ssize_t tpm_show_ppi_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
- acpi_handle handle;
- acpi_status status;
- struct acpi_object_list input;
- struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object params[4];
- union acpi_object *obj;
-
- input.count = 4;
- ppi_assign_params(params, TPM_PPI_FN_VERSION);
- input.pointer = params;
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, ppi_callback, NULL,
- tpm_device_name, &handle);
- if (ACPI_FAILURE(status))
- return -ENXIO;
-
- status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
- ACPI_TYPE_STRING);
- if (ACPI_FAILURE(status))
- return -ENOMEM;
- obj = (union acpi_object *)output.pointer;
- status = scnprintf(buf, PAGE_SIZE, "%s\n", obj->string.pointer);
- kfree(output.pointer);
- return status;
+ return scnprintf(buf, PAGE_SIZE, "%s\n", tpm_ppi_version);
}
static ssize_t tpm_show_ppi_request(struct device *dev,
struct device_attribute *attr, char *buf)
{
- acpi_handle handle;
- acpi_status status;
- struct acpi_object_list input;
- struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object params[4];
- union acpi_object *ret_obj;
-
- input.count = 4;
- ppi_assign_params(params, TPM_PPI_FN_GETREQ);
- input.pointer = params;
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, ppi_callback, NULL,
- tpm_device_name, &handle);
- if (ACPI_FAILURE(status))
+ ssize_t size = -EINVAL;
+ union acpi_object *obj;
+
+ obj = tpm_eval_dsm(TPM_PPI_FN_GETREQ, ACPI_TYPE_PACKAGE, NULL);
+ if (!obj)
return -ENXIO;
- status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
- ACPI_TYPE_PACKAGE);
- if (ACPI_FAILURE(status))
- return -ENOMEM;
/*
* output.pointer should be of package type, including two integers.
* The first is function return code, 0 means success and 1 means
* error. The second is pending TPM operation requested by the OS, 0
* means none and >0 means operation value.
*/
- ret_obj = ((union acpi_object *)output.pointer)->package.elements;
- if (ret_obj->type == ACPI_TYPE_INTEGER) {
- if (ret_obj->integer.value) {
- status = -EFAULT;
- goto cleanup;
- }
- ret_obj++;
- if (ret_obj->type == ACPI_TYPE_INTEGER)
- status = scnprintf(buf, PAGE_SIZE, "%llu\n",
- ret_obj->integer.value);
+ if (obj->package.count == 2 &&
+ obj->package.elements[0].type == ACPI_TYPE_INTEGER &&
+ obj->package.elements[1].type == ACPI_TYPE_INTEGER) {
+ if (obj->package.elements[0].integer.value)
+ size = -EFAULT;
else
- status = -EINVAL;
- } else {
- status = -EINVAL;
+ size = scnprintf(buf, PAGE_SIZE, "%llu\n",
+ obj->package.elements[1].integer.value);
}
-cleanup:
- kfree(output.pointer);
- return status;
+
+ ACPI_FREE(obj);
+
+ return size;
}
static ssize_t tpm_store_ppi_request(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- char version[PPI_VERSION_LEN + 1];
- acpi_handle handle;
- acpi_status status;
- struct acpi_object_list input;
- struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object params[4];
- union acpi_object obj;
u32 req;
u64 ret;
+ int func = TPM_PPI_FN_SUBREQ;
+ union acpi_object *obj, tmp;
+ union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp);
- input.count = 4;
- ppi_assign_params(params, TPM_PPI_FN_VERSION);
- input.pointer = params;
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, ppi_callback, NULL,
- tpm_device_name, &handle);
- if (ACPI_FAILURE(status))
- return -ENXIO;
-
- status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
- ACPI_TYPE_STRING);
- if (ACPI_FAILURE(status))
- return -ENOMEM;
- strncpy(version,
- ((union acpi_object *)output.pointer)->string.pointer,
- PPI_VERSION_LEN);
- kfree(output.pointer);
- output.length = ACPI_ALLOCATE_BUFFER;
- output.pointer = NULL;
/*
* the function to submit TPM operation request to pre-os environment
* is updated with function index from SUBREQ to SUBREQ2 since PPI
* version 1.1
*/
- if (strcmp(version, "1.1") == -1)
- params[2].integer.value = TPM_PPI_FN_SUBREQ;
- else
- params[2].integer.value = TPM_PPI_FN_SUBREQ2;
+ if (acpi_check_dsm(tpm_ppi_handle, tpm_ppi_uuid, TPM_PPI_REVISION_ID,
+ 1 << TPM_PPI_FN_SUBREQ2))
+ func = TPM_PPI_FN_SUBREQ2;
+
/*
* PPI spec defines params[3].type as ACPI_TYPE_PACKAGE. Some BIOS
* accept buffer/string/integer type, but some BIOS accept buffer/
* string/package type. For PPI version 1.0 and 1.1, use buffer type
* for compatibility, and use package type since 1.2 according to spec.
*/
- if (strcmp(version, "1.2") == -1) {
- params[3].type = ACPI_TYPE_BUFFER;
- params[3].buffer.length = sizeof(req);
- sscanf(buf, "%d", &req);
- params[3].buffer.pointer = (char *)&req;
+ if (strcmp(tpm_ppi_version, "1.2") < 0) {
+ if (sscanf(buf, "%d", &req) != 1)
+ return -EINVAL;
+ argv4.type = ACPI_TYPE_BUFFER;
+ argv4.buffer.length = sizeof(req);
+ argv4.buffer.pointer = (u8 *)&req;
} else {
- params[3].package.count = 1;
- obj.type = ACPI_TYPE_INTEGER;
- sscanf(buf, "%llu", &obj.integer.value);
- params[3].package.elements = &obj;
+ tmp.type = ACPI_TYPE_INTEGER;
+ if (sscanf(buf, "%llu", &tmp.integer.value) != 1)
+ return -EINVAL;
+ }
+
+ obj = tpm_eval_dsm(func, ACPI_TYPE_INTEGER, &argv4);
+ if (!obj) {
+ return -ENXIO;
+ } else {
+ ret = obj->integer.value;
+ ACPI_FREE(obj);
}
- status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
- ACPI_TYPE_INTEGER);
- if (ACPI_FAILURE(status))
- return -ENOMEM;
- ret = ((union acpi_object *)output.pointer)->integer.value;
if (ret == 0)
- status = (acpi_status)count;
- else if (ret == 1)
- status = -EPERM;
- else
- status = -EFAULT;
- kfree(output.pointer);
- return status;
+ return (acpi_status)count;
+
+ return (ret == 1) ? -EPERM : -EFAULT;
}
static ssize_t tpm_show_ppi_transition_action(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- char version[PPI_VERSION_LEN + 1];
- acpi_handle handle;
- acpi_status status;
- struct acpi_object_list input;
- struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object params[4];
u32 ret;
- char *info[] = {
+ acpi_status status;
+ union acpi_object *obj = NULL;
+ union acpi_object tmp = {
+ .buffer.type = ACPI_TYPE_BUFFER,
+ .buffer.length = 0,
+ .buffer.pointer = NULL
+ };
+
+ static char *info[] = {
"None",
"Shutdown",
"Reboot",
"OS Vendor-specific",
"Error",
};
- input.count = 4;
- ppi_assign_params(params, TPM_PPI_FN_VERSION);
- input.pointer = params;
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, ppi_callback, NULL,
- tpm_device_name, &handle);
- if (ACPI_FAILURE(status))
- return -ENXIO;
- status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
- ACPI_TYPE_STRING);
- if (ACPI_FAILURE(status))
- return -ENOMEM;
- strncpy(version,
- ((union acpi_object *)output.pointer)->string.pointer,
- PPI_VERSION_LEN);
/*
* PPI spec defines params[3].type as empty package, but some platforms
* (e.g. Capella with PPI 1.0) need integer/string/buffer type, so for
* compatibility, define params[3].type as buffer, if PPI version < 1.2
*/
- if (strcmp(version, "1.2") == -1) {
- params[3].type = ACPI_TYPE_BUFFER;
- params[3].buffer.length = 0;
- params[3].buffer.pointer = NULL;
+ if (strcmp(tpm_ppi_version, "1.2") < 0)
+ obj = &tmp;
+ obj = tpm_eval_dsm(TPM_PPI_FN_GETACT, ACPI_TYPE_INTEGER, obj);
+ if (!obj) {
+ return -ENXIO;
+ } else {
+ ret = obj->integer.value;
+ ACPI_FREE(obj);
}
- params[2].integer.value = TPM_PPI_FN_GETACT;
- kfree(output.pointer);
- output.length = ACPI_ALLOCATE_BUFFER;
- output.pointer = NULL;
- status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
- ACPI_TYPE_INTEGER);
- if (ACPI_FAILURE(status))
- return -ENOMEM;
- ret = ((union acpi_object *)output.pointer)->integer.value;
+
if (ret < ARRAY_SIZE(info) - 1)
status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]);
else
status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret,
info[ARRAY_SIZE(info)-1]);
- kfree(output.pointer);
return status;
}
@@ -272,27 +193,14 @@ static ssize_t tpm_show_ppi_response(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- acpi_handle handle;
- acpi_status status;
- struct acpi_object_list input;
- struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object params[4];
- union acpi_object *ret_obj;
- u64 req;
-
- input.count = 4;
- ppi_assign_params(params, TPM_PPI_FN_GETRSP);
- input.pointer = params;
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, ppi_callback, NULL,
- tpm_device_name, &handle);
- if (ACPI_FAILURE(status))
+ acpi_status status = -EINVAL;
+ union acpi_object *obj, *ret_obj;
+ u64 req, res;
+
+ obj = tpm_eval_dsm(TPM_PPI_FN_GETRSP, ACPI_TYPE_PACKAGE, NULL);
+ if (!obj)
return -ENXIO;
- status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
- ACPI_TYPE_PACKAGE);
- if (ACPI_FAILURE(status))
- return -ENOMEM;
/*
* parameter output.pointer should be of package type, including
* 3 integers. The first means function return code, the second means
@@ -300,115 +208,82 @@ static ssize_t tpm_show_ppi_response(struct device *dev,
* the most recent TPM operation request. Only if the first is 0, and
* the second integer is not 0, the response makes sense.
*/
- ret_obj = ((union acpi_object *)output.pointer)->package.elements;
- if (ret_obj->type != ACPI_TYPE_INTEGER) {
- status = -EINVAL;
+ ret_obj = obj->package.elements;
+ if (obj->package.count < 3 ||
+ ret_obj[0].type != ACPI_TYPE_INTEGER ||
+ ret_obj[1].type != ACPI_TYPE_INTEGER ||
+ ret_obj[2].type != ACPI_TYPE_INTEGER)
goto cleanup;
- }
- if (ret_obj->integer.value) {
+
+ if (ret_obj[0].integer.value) {
status = -EFAULT;
goto cleanup;
}
- ret_obj++;
- if (ret_obj->type != ACPI_TYPE_INTEGER) {
- status = -EINVAL;
- goto cleanup;
- }
- if (ret_obj->integer.value) {
- req = ret_obj->integer.value;
- ret_obj++;
- if (ret_obj->type != ACPI_TYPE_INTEGER) {
- status = -EINVAL;
- goto cleanup;
- }
- if (ret_obj->integer.value == 0)
+
+ req = ret_obj[1].integer.value;
+ res = ret_obj[2].integer.value;
+ if (req) {
+ if (res == 0)
status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
"0: Success");
- else if (ret_obj->integer.value == 0xFFFFFFF0)
+ else if (res == 0xFFFFFFF0)
status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
"0xFFFFFFF0: User Abort");
- else if (ret_obj->integer.value == 0xFFFFFFF1)
+ else if (res == 0xFFFFFFF1)
status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
"0xFFFFFFF1: BIOS Failure");
- else if (ret_obj->integer.value >= 1 &&
- ret_obj->integer.value <= 0x00000FFF)
+ else if (res >= 1 && res <= 0x00000FFF)
status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
- req, ret_obj->integer.value,
- "Corresponding TPM error");
+ req, res, "Corresponding TPM error");
else
status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
- req, ret_obj->integer.value,
- "Error");
+ req, res, "Error");
} else {
status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n",
- ret_obj->integer.value, "No Recent Request");
+ req, "No Recent Request");
}
+
cleanup:
- kfree(output.pointer);
+ ACPI_FREE(obj);
return status;
}
static ssize_t show_ppi_operations(char *buf, u32 start, u32 end)
{
- char *str = buf;
- char version[PPI_VERSION_LEN];
- acpi_handle handle;
- acpi_status status;
- struct acpi_object_list input;
- struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object params[4];
- union acpi_object obj;
int i;
u32 ret;
- char *info[] = {
+ char *str = buf;
+ union acpi_object *obj, tmp;
+ union acpi_object argv = ACPI_INIT_DSM_ARGV4(1, &tmp);
+
+ static char *info[] = {
"Not implemented",
"BIOS only",
"Blocked for OS by BIOS",
"User required",
"User not required",
};
- input.count = 4;
- ppi_assign_params(params, TPM_PPI_FN_VERSION);
- input.pointer = params;
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, ppi_callback, NULL,
- tpm_device_name, &handle);
- if (ACPI_FAILURE(status))
- return -ENXIO;
- status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
- ACPI_TYPE_STRING);
- if (ACPI_FAILURE(status))
- return -ENOMEM;
-
- strncpy(version,
- ((union acpi_object *)output.pointer)->string.pointer,
- PPI_VERSION_LEN);
- kfree(output.pointer);
- output.length = ACPI_ALLOCATE_BUFFER;
- output.pointer = NULL;
- if (strcmp(version, "1.2") == -1)
+ if (!acpi_check_dsm(tpm_ppi_handle, tpm_ppi_uuid, TPM_PPI_REVISION_ID,
+ 1 << TPM_PPI_FN_GETOPR))
return -EPERM;
- params[2].integer.value = TPM_PPI_FN_GETOPR;
- params[3].package.count = 1;
- obj.type = ACPI_TYPE_INTEGER;
- params[3].package.elements = &obj;
+ tmp.integer.type = ACPI_TYPE_INTEGER;
for (i = start; i <= end; i++) {
- obj.integer.value = i;
- status = acpi_evaluate_object_typed(handle, "_DSM",
- &input, &output, ACPI_TYPE_INTEGER);
- if (ACPI_FAILURE(status))
+ tmp.integer.value = i;
+ obj = tpm_eval_dsm(TPM_PPI_FN_GETOPR, ACPI_TYPE_INTEGER, &argv);
+ if (!obj) {
return -ENOMEM;
+ } else {
+ ret = obj->integer.value;
+ ACPI_FREE(obj);
+ }
- ret = ((union acpi_object *)output.pointer)->integer.value;
if (ret > 0 && ret < ARRAY_SIZE(info))
str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n",
i, ret, info[ret]);
- kfree(output.pointer);
- output.length = ACPI_ALLOCATE_BUFFER;
- output.pointer = NULL;
}
+
return str - buf;
}
@@ -450,14 +325,14 @@ static struct attribute_group ppi_attr_grp = {
int tpm_add_ppi(struct kobject *parent)
{
- return sysfs_create_group(parent, &ppi_attr_grp);
+ /* Cache TPM ACPI handle and version string */
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
+ ppi_callback, NULL, NULL, &tpm_ppi_handle);
+ return tpm_ppi_handle ? sysfs_create_group(parent, &ppi_attr_grp) : 0;
}
-EXPORT_SYMBOL_GPL(tpm_add_ppi);
void tpm_remove_ppi(struct kobject *parent)
{
- sysfs_remove_group(parent, &ppi_attr_grp);
+ if (tpm_ppi_handle)
+ sysfs_remove_group(parent, &ppi_attr_grp);
}
-EXPORT_SYMBOL_GPL(tpm_remove_ppi);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index ea31dafbcac..a9ed2270c25 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -84,6 +84,9 @@ static int is_itpm(struct pnp_dev *dev)
struct acpi_device *acpi = pnp_acpi_device(dev);
struct acpi_hardware_id *id;
+ if (!acpi)
+ return 0;
+
list_for_each_entry(id, &acpi->pnp.ids, list) {
if (!strcmp("INTC0102", id->id))
return 1;
@@ -98,6 +101,22 @@ static inline int is_itpm(struct pnp_dev *dev)
}
#endif
+/* Before we attempt to access the TPM we must see that the valid bit is set.
+ * The specification says that this bit is 0 at reset and remains 0 until the
+ * 'TPM has gone through its self test and initialization and has established
+ * correct values in the other bits.' */
+static int wait_startup(struct tpm_chip *chip, int l)
+{
+ unsigned long stop = jiffies + chip->vendor.timeout_a;
+ do {
+ if (ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
+ TPM_ACCESS_VALID)
+ return 0;
+ msleep(TPM_TIMEOUT);
+ } while (time_before(jiffies, stop));
+ return -1;
+}
+
static int check_locality(struct tpm_chip *chip, int l)
{
if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
@@ -198,7 +217,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
wait_for_tpm_stat(chip,
TPM_STS_DATA_AVAIL | TPM_STS_VALID,
chip->vendor.timeout_c,
- &chip->vendor.read_queue)
+ &chip->vendor.read_queue, true)
== 0) {
burstcnt = get_burstcount(chip);
for (; burstcnt > 0 && size < count; burstcnt--)
@@ -241,7 +260,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
}
wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
- &chip->vendor.int_queue);
+ &chip->vendor.int_queue, false);
status = tpm_tis_status(chip);
if (status & TPM_STS_DATA_AVAIL) { /* retry? */
dev_err(chip->dev, "Error left over data\n");
@@ -277,7 +296,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
tpm_tis_ready(chip);
if (wait_for_tpm_stat
(chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
- &chip->vendor.int_queue) < 0) {
+ &chip->vendor.int_queue, false) < 0) {
rc = -ETIME;
goto out_err;
}
@@ -292,7 +311,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
}
wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
- &chip->vendor.int_queue);
+ &chip->vendor.int_queue, false);
status = tpm_tis_status(chip);
if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
rc = -EIO;
@@ -304,7 +323,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
iowrite8(buf[count],
chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality));
wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
- &chip->vendor.int_queue);
+ &chip->vendor.int_queue, false);
status = tpm_tis_status(chip);
if ((status & TPM_STS_DATA_EXPECT) != 0) {
rc = -EIO;
@@ -342,7 +361,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
if (wait_for_tpm_stat
(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
tpm_calc_ordinal_duration(chip, ordinal),
- &chip->vendor.read_queue) < 0) {
+ &chip->vendor.read_queue, false) < 0) {
rc = -ETIME;
goto out_err;
}
@@ -374,7 +393,7 @@ static int probe_itpm(struct tpm_chip *chip)
if (vendor != TPM_VID_INTEL)
return 0;
- itpm = 0;
+ itpm = false;
rc = tpm_tis_send_data(chip, cmd_getticks, len);
if (rc == 0)
@@ -383,7 +402,7 @@ static int probe_itpm(struct tpm_chip *chip)
tpm_tis_ready(chip);
release_locality(chip, chip->vendor.locality, 0);
- itpm = 1;
+ itpm = true;
rc = tpm_tis_send_data(chip, cmd_getticks, len);
if (rc == 0) {
@@ -400,55 +419,27 @@ out:
return rc;
}
-static const struct file_operations tis_ops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .open = tpm_open,
- .read = tpm_read,
- .write = tpm_write,
- .release = tpm_release,
-};
-
-static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
-static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
-static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
-static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
-static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
-static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
- NULL);
-static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
-static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
-static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
-static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
-
-static struct attribute *tis_attrs[] = {
- &dev_attr_pubek.attr,
- &dev_attr_pcrs.attr,
- &dev_attr_enabled.attr,
- &dev_attr_active.attr,
- &dev_attr_owned.attr,
- &dev_attr_temp_deactivated.attr,
- &dev_attr_caps.attr,
- &dev_attr_cancel.attr,
- &dev_attr_durations.attr,
- &dev_attr_timeouts.attr, NULL,
-};
-
-static struct attribute_group tis_attr_grp = {
- .attrs = tis_attrs
-};
+static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status)
+{
+ switch (chip->vendor.manufacturer_id) {
+ case TPM_VID_WINBOND:
+ return ((status == TPM_STS_VALID) ||
+ (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY)));
+ case TPM_VID_STM:
+ return (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY));
+ default:
+ return (status == TPM_STS_COMMAND_READY);
+ }
+}
-static struct tpm_vendor_specific tpm_tis = {
+static const struct tpm_class_ops tpm_tis = {
.status = tpm_tis_status,
.recv = tpm_tis_recv,
.send = tpm_tis_send,
.cancel = tpm_tis_ready,
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
- .req_canceled = TPM_STS_COMMAND_READY,
- .attr_group = &tis_attr_grp,
- .miscdev = {
- .fops = &tis_ops,},
+ .req_canceled = tpm_tis_req_canceled,
};
static irqreturn_t tis_int_probe(int irq, void *dev_id)
@@ -502,7 +493,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id)
return IRQ_HANDLED;
}
-static bool interrupts = 1;
+static bool interrupts = true;
module_param(interrupts, bool, 0444);
MODULE_PARM_DESC(interrupts, "Enable interrupts");
@@ -528,12 +519,18 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+ if (wait_startup(chip, 0) != 0) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
if (request_locality(chip, 0) != 0) {
rc = -ENODEV;
goto out_err;
}
vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
+ chip->vendor.manufacturer_id = vendor;
dev_info(dev,
"1.2 TPM (device-id 0x%X, rev-id %d)\n",
@@ -545,7 +542,7 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
rc = -ENODEV;
goto out_err;
}
- itpm = (probe == 0) ? 0 : 1;
+ itpm = !!probe;
}
if (itpm)
@@ -705,7 +702,7 @@ out_err:
return rc;
}
-#if defined(CONFIG_PNP) || defined(CONFIG_PM_SLEEP)
+#ifdef CONFIG_PM_SLEEP
static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
{
u32 intmask;
@@ -726,8 +723,25 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
iowrite32(intmask,
chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
}
+
+static int tpm_tis_resume(struct device *dev)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+ int ret;
+
+ if (chip->vendor.irq)
+ tpm_tis_reenable_interrupts(chip);
+
+ ret = tpm_pm_resume(dev);
+ if (!ret)
+ tpm_do_selftest(chip);
+
+ return ret;
+}
#endif
+static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume);
+
#ifdef CONFIG_PNP
static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
const struct pnp_device_id *pnp_id)
@@ -741,34 +755,14 @@ static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
if (pnp_irq_valid(pnp_dev, 0))
irq = pnp_irq(pnp_dev, 0);
else
- interrupts = 0;
+ interrupts = false;
if (is_itpm(pnp_dev))
- itpm = 1;
+ itpm = true;
return tpm_tis_init(&pnp_dev->dev, start, len, irq);
}
-static int tpm_tis_pnp_suspend(struct pnp_dev *dev, pm_message_t msg)
-{
- return tpm_pm_suspend(&dev->dev);
-}
-
-static int tpm_tis_pnp_resume(struct pnp_dev *dev)
-{
- struct tpm_chip *chip = pnp_get_drvdata(dev);
- int ret;
-
- if (chip->vendor.irq)
- tpm_tis_reenable_interrupts(chip);
-
- ret = tpm_pm_resume(&dev->dev);
- if (!ret)
- tpm_do_selftest(chip);
-
- return ret;
-}
-
static struct pnp_device_id tpm_pnp_tbl[] = {
{"PNP0C31", 0}, /* TPM */
{"ATM1200", 0}, /* Atmel */
@@ -797,9 +791,10 @@ static struct pnp_driver tis_pnp_driver = {
.name = "tpm_tis",
.id_table = tpm_pnp_tbl,
.probe = tpm_tis_pnp_init,
- .suspend = tpm_tis_pnp_suspend,
- .resume = tpm_tis_pnp_resume,
.remove = tpm_tis_pnp_remove,
+ .driver = {
+ .pm = &tpm_tis_pm,
+ },
};
#define TIS_HID_USR_IDX sizeof(tpm_pnp_tbl)/sizeof(struct pnp_device_id) -2
@@ -808,20 +803,6 @@ module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id,
MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe");
#endif
-#ifdef CONFIG_PM_SLEEP
-static int tpm_tis_resume(struct device *dev)
-{
- struct tpm_chip *chip = dev_get_drvdata(dev);
-
- if (chip->vendor.irq)
- tpm_tis_reenable_interrupts(chip);
-
- return tpm_pm_resume(dev);
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume);
-
static struct platform_driver tis_drv = {
.driver = {
.name = "tpm_tis",
@@ -846,12 +827,19 @@ static int __init init_tis(void)
rc = platform_driver_register(&tis_drv);
if (rc < 0)
return rc;
- if (IS_ERR(pdev=platform_device_register_simple("tpm_tis", -1, NULL, 0)))
- return PTR_ERR(pdev);
- if((rc=tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0)) != 0) {
- platform_device_unregister(pdev);
- platform_driver_unregister(&tis_drv);
+ pdev = platform_device_register_simple("tpm_tis", -1, NULL, 0);
+ if (IS_ERR(pdev)) {
+ rc = PTR_ERR(pdev);
+ goto err_dev;
}
+ rc = tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0);
+ if (rc)
+ goto err_init;
+ return 0;
+err_init:
+ platform_device_unregister(pdev);
+err_dev:
+ platform_driver_unregister(&tis_drv);
return rc;
}
diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c
new file mode 100644
index 00000000000..2064b452704
--- /dev/null
+++ b/drivers/char/tpm/xen-tpmfront.c
@@ -0,0 +1,397 @@
+/*
+ * Implementation of the Xen vTPM device frontend
+ *
+ * Author: Daniel De Graaf <dgdegra@tycho.nsa.gov>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <xen/xen.h>
+#include <xen/events.h>
+#include <xen/interface/io/tpmif.h>
+#include <xen/grant_table.h>
+#include <xen/xenbus.h>
+#include <xen/page.h>
+#include "tpm.h"
+#include <xen/platform_pci.h>
+
+struct tpm_private {
+ struct tpm_chip *chip;
+ struct xenbus_device *dev;
+
+ struct vtpm_shared_page *shr;
+
+ unsigned int evtchn;
+ int ring_ref;
+ domid_t backend_id;
+};
+
+enum status_bits {
+ VTPM_STATUS_RUNNING = 0x1,
+ VTPM_STATUS_IDLE = 0x2,
+ VTPM_STATUS_RESULT = 0x4,
+ VTPM_STATUS_CANCELED = 0x8,
+};
+
+static u8 vtpm_status(struct tpm_chip *chip)
+{
+ struct tpm_private *priv = TPM_VPRIV(chip);
+ switch (priv->shr->state) {
+ case VTPM_STATE_IDLE:
+ return VTPM_STATUS_IDLE | VTPM_STATUS_CANCELED;
+ case VTPM_STATE_FINISH:
+ return VTPM_STATUS_IDLE | VTPM_STATUS_RESULT;
+ case VTPM_STATE_SUBMIT:
+ case VTPM_STATE_CANCEL: /* cancel requested, not yet canceled */
+ return VTPM_STATUS_RUNNING;
+ default:
+ return 0;
+ }
+}
+
+static bool vtpm_req_canceled(struct tpm_chip *chip, u8 status)
+{
+ return status & VTPM_STATUS_CANCELED;
+}
+
+static void vtpm_cancel(struct tpm_chip *chip)
+{
+ struct tpm_private *priv = TPM_VPRIV(chip);
+ priv->shr->state = VTPM_STATE_CANCEL;
+ wmb();
+ notify_remote_via_evtchn(priv->evtchn);
+}
+
+static unsigned int shr_data_offset(struct vtpm_shared_page *shr)
+{
+ return sizeof(*shr) + sizeof(u32) * shr->nr_extra_pages;
+}
+
+static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ struct tpm_private *priv = TPM_VPRIV(chip);
+ struct vtpm_shared_page *shr = priv->shr;
+ unsigned int offset = shr_data_offset(shr);
+
+ u32 ordinal;
+ unsigned long duration;
+
+ if (offset > PAGE_SIZE)
+ return -EINVAL;
+
+ if (offset + count > PAGE_SIZE)
+ return -EINVAL;
+
+ /* Wait for completion of any existing command or cancellation */
+ if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, chip->vendor.timeout_c,
+ &chip->vendor.read_queue, true) < 0) {
+ vtpm_cancel(chip);
+ return -ETIME;
+ }
+
+ memcpy(offset + (u8 *)shr, buf, count);
+ shr->length = count;
+ barrier();
+ shr->state = VTPM_STATE_SUBMIT;
+ wmb();
+ notify_remote_via_evtchn(priv->evtchn);
+
+ ordinal = be32_to_cpu(((struct tpm_input_header*)buf)->ordinal);
+ duration = tpm_calc_ordinal_duration(chip, ordinal);
+
+ if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, duration,
+ &chip->vendor.read_queue, true) < 0) {
+ /* got a signal or timeout, try to cancel */
+ vtpm_cancel(chip);
+ return -ETIME;
+ }
+
+ return count;
+}
+
+static int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ struct tpm_private *priv = TPM_VPRIV(chip);
+ struct vtpm_shared_page *shr = priv->shr;
+ unsigned int offset = shr_data_offset(shr);
+ size_t length = shr->length;
+
+ if (shr->state == VTPM_STATE_IDLE)
+ return -ECANCELED;
+
+ /* In theory the wait at the end of _send makes this one unnecessary */
+ if (wait_for_tpm_stat(chip, VTPM_STATUS_RESULT, chip->vendor.timeout_c,
+ &chip->vendor.read_queue, true) < 0) {
+ vtpm_cancel(chip);
+ return -ETIME;
+ }
+
+ if (offset > PAGE_SIZE)
+ return -EIO;
+
+ if (offset + length > PAGE_SIZE)
+ length = PAGE_SIZE - offset;
+
+ if (length > count)
+ length = count;
+
+ memcpy(buf, offset + (u8 *)shr, length);
+
+ return length;
+}
+
+static const struct tpm_class_ops tpm_vtpm = {
+ .status = vtpm_status,
+ .recv = vtpm_recv,
+ .send = vtpm_send,
+ .cancel = vtpm_cancel,
+ .req_complete_mask = VTPM_STATUS_IDLE | VTPM_STATUS_RESULT,
+ .req_complete_val = VTPM_STATUS_IDLE | VTPM_STATUS_RESULT,
+ .req_canceled = vtpm_req_canceled,
+};
+
+static irqreturn_t tpmif_interrupt(int dummy, void *dev_id)
+{
+ struct tpm_private *priv = dev_id;
+
+ switch (priv->shr->state) {
+ case VTPM_STATE_IDLE:
+ case VTPM_STATE_FINISH:
+ wake_up_interruptible(&priv->chip->vendor.read_queue);
+ break;
+ case VTPM_STATE_SUBMIT:
+ case VTPM_STATE_CANCEL:
+ default:
+ break;
+ }
+ return IRQ_HANDLED;
+}
+
+static int setup_chip(struct device *dev, struct tpm_private *priv)
+{
+ struct tpm_chip *chip;
+
+ chip = tpm_register_hardware(dev, &tpm_vtpm);
+ if (!chip)
+ return -ENODEV;
+
+ init_waitqueue_head(&chip->vendor.read_queue);
+
+ priv->chip = chip;
+ TPM_VPRIV(chip) = priv;
+
+ return 0;
+}
+
+/* caller must clean up in case of errors */
+static int setup_ring(struct xenbus_device *dev, struct tpm_private *priv)
+{
+ struct xenbus_transaction xbt;
+ const char *message = NULL;
+ int rv;
+
+ priv->shr = (void *)__get_free_page(GFP_KERNEL|__GFP_ZERO);
+ if (!priv->shr) {
+ xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring");
+ return -ENOMEM;
+ }
+
+ rv = xenbus_grant_ring(dev, virt_to_mfn(priv->shr));
+ if (rv < 0)
+ return rv;
+
+ priv->ring_ref = rv;
+
+ rv = xenbus_alloc_evtchn(dev, &priv->evtchn);
+ if (rv)
+ return rv;
+
+ rv = bind_evtchn_to_irqhandler(priv->evtchn, tpmif_interrupt, 0,
+ "tpmif", priv);
+ if (rv <= 0) {
+ xenbus_dev_fatal(dev, rv, "allocating TPM irq");
+ return rv;
+ }
+ priv->chip->vendor.irq = rv;
+
+ again:
+ rv = xenbus_transaction_start(&xbt);
+ if (rv) {
+ xenbus_dev_fatal(dev, rv, "starting transaction");
+ return rv;
+ }
+
+ rv = xenbus_printf(xbt, dev->nodename,
+ "ring-ref", "%u", priv->ring_ref);
+ if (rv) {
+ message = "writing ring-ref";
+ goto abort_transaction;
+ }
+
+ rv = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
+ priv->evtchn);
+ if (rv) {
+ message = "writing event-channel";
+ goto abort_transaction;
+ }
+
+ rv = xenbus_printf(xbt, dev->nodename, "feature-protocol-v2", "1");
+ if (rv) {
+ message = "writing feature-protocol-v2";
+ goto abort_transaction;
+ }
+
+ rv = xenbus_transaction_end(xbt, 0);
+ if (rv == -EAGAIN)
+ goto again;
+ if (rv) {
+ xenbus_dev_fatal(dev, rv, "completing transaction");
+ return rv;
+ }
+
+ xenbus_switch_state(dev, XenbusStateInitialised);
+
+ return 0;
+
+ abort_transaction:
+ xenbus_transaction_end(xbt, 1);
+ if (message)
+ xenbus_dev_error(dev, rv, "%s", message);
+
+ return rv;
+}
+
+static void ring_free(struct tpm_private *priv)
+{
+ if (!priv)
+ return;
+
+ if (priv->ring_ref)
+ gnttab_end_foreign_access(priv->ring_ref, 0,
+ (unsigned long)priv->shr);
+ else
+ free_page((unsigned long)priv->shr);
+
+ if (priv->chip && priv->chip->vendor.irq)
+ unbind_from_irqhandler(priv->chip->vendor.irq, priv);
+
+ kfree(priv);
+}
+
+static int tpmfront_probe(struct xenbus_device *dev,
+ const struct xenbus_device_id *id)
+{
+ struct tpm_private *priv;
+ int rv;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ xenbus_dev_fatal(dev, -ENOMEM, "allocating priv structure");
+ return -ENOMEM;
+ }
+
+ rv = setup_chip(&dev->dev, priv);
+ if (rv) {
+ kfree(priv);
+ return rv;
+ }
+
+ rv = setup_ring(dev, priv);
+ if (rv) {
+ tpm_remove_hardware(&dev->dev);
+ ring_free(priv);
+ return rv;
+ }
+
+ tpm_get_timeouts(priv->chip);
+
+ return rv;
+}
+
+static int tpmfront_remove(struct xenbus_device *dev)
+{
+ struct tpm_chip *chip = dev_get_drvdata(&dev->dev);
+ struct tpm_private *priv = TPM_VPRIV(chip);
+ tpm_remove_hardware(&dev->dev);
+ ring_free(priv);
+ TPM_VPRIV(chip) = NULL;
+ return 0;
+}
+
+static int tpmfront_resume(struct xenbus_device *dev)
+{
+ /* A suspend/resume/migrate will interrupt a vTPM anyway */
+ tpmfront_remove(dev);
+ return tpmfront_probe(dev, NULL);
+}
+
+static void backend_changed(struct xenbus_device *dev,
+ enum xenbus_state backend_state)
+{
+ int val;
+
+ switch (backend_state) {
+ case XenbusStateInitialised:
+ case XenbusStateConnected:
+ if (dev->state == XenbusStateConnected)
+ break;
+
+ if (xenbus_scanf(XBT_NIL, dev->otherend,
+ "feature-protocol-v2", "%d", &val) < 0)
+ val = 0;
+ if (!val) {
+ xenbus_dev_fatal(dev, -EINVAL,
+ "vTPM protocol 2 required");
+ return;
+ }
+ xenbus_switch_state(dev, XenbusStateConnected);
+ break;
+
+ case XenbusStateClosing:
+ case XenbusStateClosed:
+ device_unregister(&dev->dev);
+ xenbus_frontend_closed(dev);
+ break;
+ default:
+ break;
+ }
+}
+
+static const struct xenbus_device_id tpmfront_ids[] = {
+ { "vtpm" },
+ { "" }
+};
+MODULE_ALIAS("xen:vtpm");
+
+static DEFINE_XENBUS_DRIVER(tpmfront, ,
+ .probe = tpmfront_probe,
+ .remove = tpmfront_remove,
+ .resume = tpmfront_resume,
+ .otherend_changed = backend_changed,
+ );
+
+static int __init xen_tpmfront_init(void)
+{
+ if (!xen_domain())
+ return -ENODEV;
+
+ if (!xen_has_pv_devices())
+ return -ENODEV;
+
+ return xenbus_register_frontend(&tpmfront_driver);
+}
+module_init(xen_tpmfront_init);
+
+static void __exit xen_tpmfront_exit(void)
+{
+ xenbus_unregister_driver(&tpmfront_driver);
+}
+module_exit(xen_tpmfront_exit);
+
+MODULE_AUTHOR("Daniel De Graaf <dgdegra@tycho.nsa.gov>");
+MODULE_DESCRIPTION("Xen vTPM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c
index 4945bd3d18d..a15ce4ef39c 100644
--- a/drivers/char/ttyprintk.c
+++ b/drivers/char/ttyprintk.c
@@ -17,7 +17,7 @@
#include <linux/device.h>
#include <linux/serial.h>
#include <linux/tty.h>
-#include <linux/export.h>
+#include <linux/module.h>
struct ttyprintk_port {
struct tty_port port;
@@ -179,7 +179,6 @@ static int __init ttyprintk_init(void)
{
int ret = -ENOMEM;
- tpk_port.port.ops = &null_ops;
mutex_init(&tpk_port.port_write_mutex);
ttyprintk_driver = tty_alloc_driver(1,
@@ -190,6 +189,7 @@ static int __init ttyprintk_init(void)
return PTR_ERR(ttyprintk_driver);
tty_port_init(&tpk_port.port);
+ tpk_port.port.ops = &null_ops;
ttyprintk_driver->driver_name = "ttyprintk";
ttyprintk_driver->name = "ttyprintk";
@@ -210,10 +210,19 @@ static int __init ttyprintk_init(void)
return 0;
error:
- tty_unregister_driver(ttyprintk_driver);
put_tty_driver(ttyprintk_driver);
tty_port_destroy(&tpk_port.port);
- ttyprintk_driver = NULL;
return ret;
}
-module_init(ttyprintk_init);
+
+static void __exit ttyprintk_exit(void)
+{
+ tty_unregister_driver(ttyprintk_driver);
+ put_tty_driver(ttyprintk_driver);
+ tty_port_destroy(&tpk_port.port);
+}
+
+device_initcall(ttyprintk_init);
+module_exit(ttyprintk_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 684b0d53764..60aafb8a1f2 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -61,9 +61,6 @@ struct ports_driver_data {
/* List of all the devices we're handling */
struct list_head portdevs;
- /* Number of devices this driver is handling */
- unsigned int index;
-
/*
* This is used to keep track of the number of hvc consoles
* spawned by this driver. This number is given as the first
@@ -81,8 +78,8 @@ struct ports_driver_data {
};
static struct ports_driver_data pdrvdata;
-DEFINE_SPINLOCK(pdrvdata_lock);
-DECLARE_COMPLETION(early_console_added);
+static DEFINE_SPINLOCK(pdrvdata_lock);
+static DECLARE_COMPLETION(early_console_added);
/* This struct holds information that's relevant only for console ports */
struct console {
@@ -152,7 +149,8 @@ struct ports_device {
spinlock_t ports_lock;
/* To protect the vq operations for the control channel */
- spinlock_t cvq_lock;
+ spinlock_t c_ivq_lock;
+ spinlock_t c_ovq_lock;
/* The current config space is stored here */
struct virtio_console_config config;
@@ -169,9 +167,6 @@ struct ports_device {
/* Array of per-port IO virtqueues */
struct virtqueue **in_vqs, **out_vqs;
- /* Used for numbering devices for sysfs and debugfs */
- unsigned int drv_index;
-
/* Major number for this device. Ports will be created as minors. */
int chr_major;
};
@@ -277,9 +272,12 @@ static struct port *find_port_by_devt_in_portdev(struct ports_device *portdev,
unsigned long flags;
spin_lock_irqsave(&portdev->ports_lock, flags);
- list_for_each_entry(port, &portdev->ports, list)
- if (port->cdev->dev == dev)
+ list_for_each_entry(port, &portdev->ports, list) {
+ if (port->cdev->dev == dev) {
+ kref_get(&port->kref);
goto out;
+ }
+ }
port = NULL;
out:
spin_unlock_irqrestore(&portdev->ports_lock, flags);
@@ -508,7 +506,7 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
sg_init_one(sg, buf->buf, buf->size);
- ret = virtqueue_add_buf(vq, sg, 0, 1, buf, GFP_ATOMIC);
+ ret = virtqueue_add_inbuf(vq, sg, 1, buf, GFP_ATOMIC);
virtqueue_kick(vq);
if (!ret)
ret = vq->num_free;
@@ -575,11 +573,15 @@ static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id,
vq = portdev->c_ovq;
sg_init_one(sg, &cpkt, sizeof(cpkt));
- if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt, GFP_ATOMIC) == 0) {
+
+ spin_lock(&portdev->c_ovq_lock);
+ if (virtqueue_add_outbuf(vq, sg, 1, &cpkt, GFP_ATOMIC) == 0) {
virtqueue_kick(vq);
- while (!virtqueue_get_buf(vq, &len))
+ while (!virtqueue_get_buf(vq, &len)
+ && !virtqueue_is_broken(vq))
cpu_relax();
}
+ spin_unlock(&portdev->c_ovq_lock);
return 0;
}
@@ -624,7 +626,7 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg,
reclaim_consumed_buffers(port);
- err = virtqueue_add_buf(out_vq, sg, nents, 0, data, GFP_ATOMIC);
+ err = virtqueue_add_outbuf(out_vq, sg, nents, data, GFP_ATOMIC);
/* Tell Host to go! */
virtqueue_kick(out_vq);
@@ -649,7 +651,8 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg,
* we need to kmalloc a GFP_ATOMIC buffer each time the
* console driver writes something out.
*/
- while (!virtqueue_get_buf(out_vq, &len))
+ while (!virtqueue_get_buf(out_vq, &len)
+ && !virtqueue_is_broken(out_vq))
cpu_relax();
done:
spin_unlock_irqrestore(&port->outvq_lock, flags);
@@ -748,6 +751,10 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf,
port = filp->private_data;
+ /* Port is hot-unplugged. */
+ if (!port->guest_connected)
+ return -ENODEV;
+
if (!port_has_data(port)) {
/*
* If nothing's connected on the host just return 0 in
@@ -764,7 +771,7 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf,
if (ret < 0)
return ret;
}
- /* Port got hot-unplugged. */
+ /* Port got hot-unplugged while we were waiting above. */
if (!port->guest_connected)
return -ENODEV;
/*
@@ -883,12 +890,10 @@ static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
} else {
/* Failback to copying a page */
struct page *page = alloc_page(GFP_KERNEL);
- char *src = buf->ops->map(pipe, buf, 1);
- char *dst;
+ char *src;
if (!page)
return -ENOMEM;
- dst = kmap(page);
offset = sd->pos & ~PAGE_MASK;
@@ -896,10 +901,9 @@ static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
if (len + offset > PAGE_SIZE)
len = PAGE_SIZE - offset;
- memcpy(dst + offset, src + buf->offset, len);
-
- kunmap(page);
- buf->ops->unmap(pipe, buf, src);
+ src = kmap_atomic(buf->page);
+ memcpy(page_address(page) + offset, src + buf->offset, len);
+ kunmap_atomic(src);
sg_set_page(&(sgl->sg[sgl->n]), page, len, offset);
}
@@ -934,13 +938,25 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
if (is_rproc_serial(port->out_vq->vdev))
return -EINVAL;
+ /*
+ * pipe->nrbufs == 0 means there are no data to transfer,
+ * so this returns just 0 for no data.
+ */
+ pipe_lock(pipe);
+ if (!pipe->nrbufs) {
+ ret = 0;
+ goto error_out;
+ }
+
ret = wait_port_writable(port, filp->f_flags & O_NONBLOCK);
if (ret < 0)
- return ret;
+ goto error_out;
buf = alloc_buf(port->out_vq, 0, pipe->nrbufs);
- if (!buf)
- return -ENOMEM;
+ if (!buf) {
+ ret = -ENOMEM;
+ goto error_out;
+ }
sgl.n = 0;
sgl.len = 0;
@@ -948,12 +964,17 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
sgl.sg = buf->sg;
sg_init_table(sgl.sg, sgl.size);
ret = __splice_from_pipe(pipe, &sd, pipe_to_sg);
+ pipe_unlock(pipe);
if (likely(ret > 0))
ret = __send_to_port(port, buf->sg, sgl.n, sgl.len, buf, true);
if (unlikely(ret <= 0))
free_buf(buf, true);
return ret;
+
+error_out:
+ pipe_unlock(pipe);
+ return ret;
}
static unsigned int port_fops_poll(struct file *filp, poll_table *wait)
@@ -1021,14 +1042,14 @@ static int port_fops_open(struct inode *inode, struct file *filp)
struct port *port;
int ret;
+ /* We get the port with a kref here */
port = find_port_by_devt(cdev->dev);
+ if (!port) {
+ /* Port was unplugged before we could proceed */
+ return -ENXIO;
+ }
filp->private_data = port;
- /* Prevent against a port getting hot-unplugged at the same time */
- spin_lock_irq(&port->portdev->ports_lock);
- kref_get(&port->kref);
- spin_unlock_irq(&port->portdev->ports_lock);
-
/*
* Don't allow opening of console port devices -- that's done
* via /dev/hvc
@@ -1042,7 +1063,7 @@ static int port_fops_open(struct inode *inode, struct file *filp)
spin_lock_irq(&port->inbuf_lock);
if (port->guest_connected) {
spin_unlock_irq(&port->inbuf_lock);
- ret = -EMFILE;
+ ret = -EBUSY;
goto out;
}
@@ -1204,7 +1225,7 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
return hvc_instantiate(0, 0, &hv_ops);
}
-int init_port_console(struct port *port)
+static int init_port_console(struct port *port)
{
int ret;
@@ -1415,7 +1436,7 @@ static int add_port(struct ports_device *portdev, u32 id)
}
port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev,
devt, port, "vport%up%u",
- port->portdev->drv_index, id);
+ port->portdev->vdev->index, id);
if (IS_ERR(port->dev)) {
err = PTR_ERR(port->dev);
dev_err(&port->portdev->vdev->dev,
@@ -1470,7 +1491,7 @@ static int add_port(struct ports_device *portdev, u32 id)
* inspect a port's state at any time
*/
sprintf(debugfs_name, "vport%up%u",
- port->portdev->drv_index, id);
+ port->portdev->vdev->index, id);
port->debugfs_file = debugfs_create_file(debugfs_name, 0444,
pdrvdata.debugfs_dir,
port,
@@ -1500,14 +1521,6 @@ static void remove_port(struct kref *kref)
port = container_of(kref, struct port, kref);
- sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
- device_destroy(pdrvdata.class, port->dev->devt);
- cdev_del(port->cdev);
-
- kfree(port->name);
-
- debugfs_remove(port->debugfs_file);
-
kfree(port);
}
@@ -1515,18 +1528,22 @@ static void remove_port_data(struct port *port)
{
struct port_buffer *buf;
+ spin_lock_irq(&port->inbuf_lock);
/* Remove unused data this port might have received. */
discard_port_data(port);
- reclaim_consumed_buffers(port);
-
/* Remove buffers we queued up for the Host to send us data in. */
while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
free_buf(buf, true);
+ spin_unlock_irq(&port->inbuf_lock);
+
+ spin_lock_irq(&port->outvq_lock);
+ reclaim_consumed_buffers(port);
/* Free pending buffers from the out-queue. */
while ((buf = virtqueue_detach_unused_buf(port->out_vq)))
free_buf(buf, true);
+ spin_unlock_irq(&port->outvq_lock);
}
/*
@@ -1540,14 +1557,18 @@ static void unplug_port(struct port *port)
list_del(&port->list);
spin_unlock_irq(&port->portdev->ports_lock);
+ spin_lock_irq(&port->inbuf_lock);
if (port->guest_connected) {
+ /* Let the app know the port is going down. */
+ send_sigio_to_port(port);
+
+ /* Do this after sigio is actually sent */
port->guest_connected = false;
port->host_connected = false;
- wake_up_interruptible(&port->waitqueue);
- /* Let the app know the port is going down. */
- send_sigio_to_port(port);
+ wake_up_interruptible(&port->waitqueue);
}
+ spin_unlock_irq(&port->inbuf_lock);
if (is_console_port(port)) {
spin_lock_irq(&pdrvdata_lock);
@@ -1565,6 +1586,13 @@ static void unplug_port(struct port *port)
*/
port->portdev = NULL;
+ sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
+ device_destroy(pdrvdata.class, port->dev->devt);
+ cdev_del(port->cdev);
+
+ debugfs_remove(port->debugfs_file);
+ kfree(port->name);
+
/*
* Locks around here are not necessary - a port can't be
* opened after we removed the port struct from ports_list
@@ -1657,7 +1685,9 @@ static void handle_control_message(struct ports_device *portdev,
* If the guest is connected, it'll be interested in
* knowing the host connection state changed.
*/
+ spin_lock_irq(&port->inbuf_lock);
send_sigio_to_port(port);
+ spin_unlock_irq(&port->inbuf_lock);
break;
case VIRTIO_CONSOLE_PORT_NAME:
/*
@@ -1715,23 +1745,23 @@ static void control_work_handler(struct work_struct *work)
portdev = container_of(work, struct ports_device, control_work);
vq = portdev->c_ivq;
- spin_lock(&portdev->cvq_lock);
+ spin_lock(&portdev->c_ivq_lock);
while ((buf = virtqueue_get_buf(vq, &len))) {
- spin_unlock(&portdev->cvq_lock);
+ spin_unlock(&portdev->c_ivq_lock);
buf->len = len;
buf->offset = 0;
handle_control_message(portdev, buf);
- spin_lock(&portdev->cvq_lock);
+ spin_lock(&portdev->c_ivq_lock);
if (add_inbuf(portdev->c_ivq, buf) < 0) {
dev_warn(&portdev->vdev->dev,
"Error adding buffer to queue\n");
free_buf(buf, false);
}
}
- spin_unlock(&portdev->cvq_lock);
+ spin_unlock(&portdev->c_ivq_lock);
}
static void out_intr(struct virtqueue *vq)
@@ -1758,22 +1788,32 @@ static void in_intr(struct virtqueue *vq)
port->inbuf = get_inbuf(port);
/*
- * Don't queue up data when port is closed. This condition
+ * Normally the port should not accept data when the port is
+ * closed. For generic serial ports, the host won't (shouldn't)
+ * send data till the guest is connected. But this condition
* can be reached when a console port is not yet connected (no
- * tty is spawned) and the host sends out data to console
- * ports. For generic serial ports, the host won't
- * (shouldn't) send data till the guest is connected.
+ * tty is spawned) and the other side sends out data over the
+ * vring, or when a remote devices start sending data before
+ * the ports are opened.
+ *
+ * A generic serial port will discard data if not connected,
+ * while console ports and rproc-serial ports accepts data at
+ * any time. rproc-serial is initiated with guest_connected to
+ * false because port_fops_open expects this. Console ports are
+ * hooked up with an HVC console and is initialized with
+ * guest_connected to true.
*/
- if (!port->guest_connected)
+
+ if (!port->guest_connected && !is_rproc_serial(port->portdev->vdev))
discard_port_data(port);
+ /* Send a SIGIO indicating new data in case the process asked for it */
+ send_sigio_to_port(port);
+
spin_unlock_irqrestore(&port->inbuf_lock, flags);
wake_up_interruptible(&port->waitqueue);
- /* Send a SIGIO indicating new data in case the process asked for it */
- send_sigio_to_port(port);
-
if (is_console_port(port) && hvc_poll(port->cons.hvc))
hvc_kick();
}
@@ -1796,12 +1836,8 @@ static void config_intr(struct virtio_device *vdev)
struct port *port;
u16 rows, cols;
- vdev->config->get(vdev,
- offsetof(struct virtio_console_config, cols),
- &cols, sizeof(u16));
- vdev->config->get(vdev,
- offsetof(struct virtio_console_config, rows),
- &rows, sizeof(u16));
+ virtio_cread(vdev, struct virtio_console_config, cols, &cols);
+ virtio_cread(vdev, struct virtio_console_config, rows, &rows);
port = find_port_by_id(portdev, 0);
set_console_size(port, rows, cols);
@@ -1958,16 +1994,12 @@ static int virtcons_probe(struct virtio_device *vdev)
portdev->vdev = vdev;
vdev->priv = portdev;
- spin_lock_irq(&pdrvdata_lock);
- portdev->drv_index = pdrvdata.index++;
- spin_unlock_irq(&pdrvdata_lock);
-
portdev->chr_major = register_chrdev(0, "virtio-portsdev",
&portdev_fops);
if (portdev->chr_major < 0) {
dev_err(&vdev->dev,
"Error %d registering chrdev for device %u\n",
- portdev->chr_major, portdev->drv_index);
+ portdev->chr_major, vdev->index);
err = portdev->chr_major;
goto free;
}
@@ -1977,10 +2009,9 @@ static int virtcons_probe(struct virtio_device *vdev)
/* Don't test MULTIPORT at all if we're rproc: not a valid feature! */
if (!is_rproc_serial(vdev) &&
- virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT,
- offsetof(struct virtio_console_config,
- max_nr_ports),
- &portdev->config.max_nr_ports) == 0) {
+ virtio_cread_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT,
+ struct virtio_console_config, max_nr_ports,
+ &portdev->config.max_nr_ports) == 0) {
multiport = true;
}
@@ -1996,10 +2027,12 @@ static int virtcons_probe(struct virtio_device *vdev)
if (multiport) {
unsigned int nr_added_bufs;
- spin_lock_init(&portdev->cvq_lock);
+ spin_lock_init(&portdev->c_ivq_lock);
+ spin_lock_init(&portdev->c_ovq_lock);
INIT_WORK(&portdev->control_work, &control_work_handler);
- nr_added_bufs = fill_queue(portdev->c_ivq, &portdev->cvq_lock);
+ nr_added_bufs = fill_queue(portdev->c_ivq,
+ &portdev->c_ivq_lock);
if (!nr_added_bufs) {
dev_err(&vdev->dev,
"Error allocating buffers for control queue\n");
@@ -2062,7 +2095,8 @@ static void virtcons_remove(struct virtio_device *vdev)
/* Disable interrupts for vqs */
vdev->config->reset(vdev);
/* Finish up work that's lined up */
- cancel_work_sync(&portdev->control_work);
+ if (use_multiport(portdev))
+ cancel_work_sync(&portdev->control_work);
list_for_each_entry_safe(port, port2, &portdev->ports, list)
unplug_port(port);
@@ -2102,7 +2136,7 @@ static struct virtio_device_id rproc_serial_id_table[] = {
static unsigned int rproc_serial_features[] = {
};
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int virtcons_freeze(struct virtio_device *vdev)
{
struct ports_device *portdev;
@@ -2149,7 +2183,7 @@ static int virtcons_restore(struct virtio_device *vdev)
return ret;
if (use_multiport(portdev))
- fill_queue(portdev->c_ivq, &portdev->cvq_lock);
+ fill_queue(portdev->c_ivq, &portdev->c_ivq_lock);
list_for_each_entry(port, &portdev->ports, list) {
port->in_vq = portdev->in_vqs[port->id];
@@ -2180,7 +2214,7 @@ static struct virtio_driver virtio_console = {
.probe = virtcons_probe,
.remove = virtcons_remove,
.config_changed = config_intr,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
.freeze = virtcons_freeze,
.restore = virtcons_restore,
#endif
@@ -2208,10 +2242,8 @@ static int __init init(void)
}
pdrvdata.debugfs_dir = debugfs_create_dir("virtio-ports", NULL);
- if (!pdrvdata.debugfs_dir) {
- pr_warning("Error %ld creating debugfs dir for virtio-ports\n",
- PTR_ERR(pdrvdata.debugfs_dir));
- }
+ if (!pdrvdata.debugfs_dir)
+ pr_warning("Error creating debugfs dir for virtio-ports\n");
INIT_LIST_HEAD(&pdrvdata.consoles);
INIT_LIST_HEAD(&pdrvdata.portdevs);
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
index 5224da5202d..f6345f932e4 100644
--- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
@@ -721,7 +721,7 @@ static int hwicap_remove(struct device *dev)
{
struct hwicap_drvdata *drvdata;
- drvdata = (struct hwicap_drvdata *)dev_get_drvdata(dev);
+ drvdata = dev_get_drvdata(dev);
if (!drvdata)
return 0;
@@ -731,7 +731,6 @@ static int hwicap_remove(struct device *dev)
iounmap(drvdata->base_address);
release_mem_region(drvdata->mem_start, drvdata->mem_size);
kfree(drvdata);
- dev_set_drvdata(dev, NULL);
mutex_lock(&icap_sem);
probed_devices[MINOR(dev->devt)-XHWICAP_MINOR] = 0;
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.h b/drivers/char/xilinx_hwicap/xilinx_hwicap.h
index d31ee23c9f1..38b145eaf24 100644
--- a/drivers/char/xilinx_hwicap/xilinx_hwicap.h
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.h
@@ -37,7 +37,7 @@
#include <linux/cdev.h>
#include <linux/platform_device.h>
-#include <asm/io.h>
+#include <linux/io.h>
struct hwicap_drvdata {
u32 write_buffer_in_use; /* Always in [0,3] */
@@ -85,7 +85,13 @@ struct hwicap_driver_config {
void (*reset)(struct hwicap_drvdata *drvdata);
};
-/* Number of times to poll the done regsiter */
+/* Number of times to poll the done register. This has to be large
+ * enough to allow an entire configuration to complete. If an entire
+ * page (4kb) is configured at once, that could take up to 4k cycles
+ * with a byte-wide icap interface. In most cases, this driver is
+ * used with a much smaller fifo, but this should be sufficient in the
+ * worst case.
+ */
#define XHI_MAX_RETRIES 5000
/************ Constant Definitions *************/