diff options
Diffstat (limited to 'drivers/char')
128 files changed, 10636 insertions, 5766 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 43643033a3a..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. @@ -66,21 +54,6 @@ config TTY_PRINTK If unsure, say N. -config BRIQ_PANEL - tristate 'Total Impact briQ front panel driver' - depends on PPC_CHRP - ---help--- - The briQ is a small footprint CHRP computer with a frontpanel VFD, a - tristate led and two switches. It is the size of a CDROM drive. - - If you have such one and want anything showing on the VFD then you - must answer Y here. - - To compile this driver as a module, choose M here: the - module will be called briq_panel. - - It's safe to say N here. - config BFIN_OTP tristate "Blackfin On-Chip OTP Memory Support" depends on BLACKFIN && (BF51x || BF52x || BF54x) @@ -174,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. @@ -407,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 @@ -433,8 +407,8 @@ config APPLICOM If unsure, say N. config SONYPI - tristate "Sony Vaio Programmable I/O Control Device support (EXPERIMENTAL)" - depends on EXPERIMENTAL && X86 && PCI && INPUT && !64BIT + tristate "Sony Vaio Programmable I/O Control Device support" + 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. @@ -454,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 @@ -525,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. @@ -548,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" @@ -581,7 +562,7 @@ source "drivers/char/tpm/Kconfig" config TELCLOCK tristate "Telecom clock driver for ATCA SBC" - depends on EXPERIMENTAL && X86 + depends on X86 default n help The telecom clock device is specific to the MPCBL0010 and MPCBL0050 @@ -600,14 +581,6 @@ config DEVPORT source "drivers/s390/char/Kconfig" -config RAMOOPS - tristate "Log panic/oops to a RAM buffer" - depends on HAS_IOMEM - default n - help - This enables panic and oops messages to be logged to a circular - buffer in RAM where it can be read back at some later point. - config MSM_SMD_PKT bool "Enable device interface for some SMD packet ports" default n diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 32762ba769c..a324f9303e3 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -13,10 +13,8 @@ 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_BRIQ_PANEL) += briq_panel.o obj-$(CONFIG_BFIN_OTP) += bfin-otp.o obj-$(CONFIG_PRINTER) += lp.o @@ -51,15 +49,13 @@ 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_IPMI_HANDLER) += ipmi/ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o obj-$(CONFIG_TCG_TPM) += tpm/ obj-$(CONFIG_PS3_FLASH) += ps3flash.o -obj-$(CONFIG_RAMOOPS) += ramoops.o obj-$(CONFIG_JS_RTC) += js-rtc.o js-rtc-y = rtc.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 fd793519ea2..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) { @@ -249,7 +249,7 @@ static const struct agp_bridge_driver ali_m1541_bridge = { }; -static struct agp_device_ids ali_agp_device_ids[] __devinitdata = +static struct agp_device_ids ali_agp_device_ids[] = { { .device_id = PCI_DEVICE_ID_AL_M1541, @@ -299,8 +299,7 @@ static struct agp_device_ids ali_agp_device_ids[] __devinitdata = { }, /* dummy final entry, always present */ }; -static int __devinit agp_ali_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int agp_ali_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct agp_device_ids *devs = ali_agp_device_ids; struct agp_bridge_data *bridge; @@ -374,7 +373,7 @@ found: return agp_add_bridge(bridge); } -static void __devexit agp_ali_remove(struct pci_dev *pdev) +static void agp_ali_remove(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); 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 f7e88787af9..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; } @@ -388,7 +386,7 @@ static const struct agp_bridge_driver amd_irongate_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static struct agp_device_ids amd_agp_device_ids[] __devinitdata = +static struct agp_device_ids amd_agp_device_ids[] = { { .device_id = PCI_DEVICE_ID_AMD_FE_GATE_7006, @@ -405,8 +403,8 @@ static struct agp_device_ids amd_agp_device_ids[] __devinitdata = { }, /* dummy final entry, always present */ }; -static int __devinit agp_amdk7_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int agp_amdk7_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { struct agp_bridge_data *bridge; u8 cap_ptr; @@ -480,7 +478,7 @@ static int __devinit agp_amdk7_probe(struct pci_dev *pdev, return agp_add_bridge(bridge); } -static void __devexit agp_amdk7_remove(struct pci_dev *pdev) +static void agp_amdk7_remove(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index 444f8b6ab41..3b47ed0310e 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -240,7 +240,7 @@ static const struct agp_bridge_driver amd_8151_driver = { }; /* Some basic sanity checks for the aperture. */ -static int __devinit agp_aperture_valid(u64 aper, u32 size) +static int agp_aperture_valid(u64 aper, u32 size) { if (!aperture_valid(aper, size, 32*1024*1024)) return 0; @@ -267,10 +267,8 @@ static int __devinit agp_aperture_valid(u64 aper, u32 size) * to allocate that much memory. But at least error out cleanly instead of * crashing. */ -static __devinit int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp, - u16 cap) +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; @@ -296,9 +294,7 @@ static __devinit int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp, 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 @@ -326,7 +322,7 @@ static __devinit int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp, return 0; } -static __devinit int cache_nbs(struct pci_dev *pdev, u32 cap_ptr) +static int cache_nbs(struct pci_dev *pdev, u32 cap_ptr) { int i; @@ -352,7 +348,7 @@ static __devinit int cache_nbs(struct pci_dev *pdev, u32 cap_ptr) } /* Handle AMD 8151 quirks */ -static void __devinit amd8151_init(struct pci_dev *pdev, struct agp_bridge_data *bridge) +static void amd8151_init(struct pci_dev *pdev, struct agp_bridge_data *bridge) { char *revstring; @@ -390,7 +386,7 @@ static const struct aper_size_info_32 uli_sizes[7] = {8, 2048, 1, 4}, {4, 1024, 0, 3} }; -static int __devinit uli_agp_init(struct pci_dev *pdev) +static int uli_agp_init(struct pci_dev *pdev) { u32 httfea,baseaddr,enuscr; struct pci_dev *dev1; @@ -513,8 +509,8 @@ put: return ret; } -static int __devinit agp_amd64_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int agp_amd64_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { struct agp_bridge_data *bridge; u8 cap_ptr; @@ -579,7 +575,7 @@ static int __devinit agp_amd64_probe(struct pci_dev *pdev, return 0; } -static void __devexit agp_amd64_remove(struct pci_dev *pdev) +static void agp_amd64_remove(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); @@ -736,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 dc30e224349..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 */ @@ -445,7 +444,7 @@ static const struct agp_bridge_driver ati_generic_bridge = { }; -static struct agp_device_ids ati_agp_device_ids[] __devinitdata = +static struct agp_device_ids ati_agp_device_ids[] = { { .device_id = PCI_DEVICE_ID_ATI_RS100, @@ -490,8 +489,7 @@ static struct agp_device_ids ati_agp_device_ids[] __devinitdata = { }, /* dummy final entry, always present */ }; -static int __devinit agp_ati_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int agp_ati_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct agp_device_ids *devs = ati_agp_device_ids; struct agp_bridge_data *bridge; @@ -533,7 +531,7 @@ found: return agp_add_bridge(bridge); } -static void __devexit agp_ati_remove(struct pci_dev *pdev) +static void agp_ati_remove(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c index d607f53d8af..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); @@ -343,8 +342,8 @@ static const struct agp_bridge_driver efficeon_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static int __devinit agp_efficeon_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int agp_efficeon_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { struct agp_bridge_data *bridge; u8 cap_ptr; @@ -407,7 +406,7 @@ static int __devinit agp_efficeon_probe(struct pci_dev *pdev, return agp_add_bridge(bridge); } -static void __devexit agp_efficeon_remove(struct pci_dev *pdev) +static void agp_efficeon_remove(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); 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 17e05d1076b..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> @@ -958,7 +957,7 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge) if (set_memory_uc((unsigned long)table, 1 << page_order)) printk(KERN_WARNING "Could not set GATT table memory to UC!\n"); - bridge->gatt_table = (void *)table; + bridge->gatt_table = (u32 __iomem *)table; #else bridge->gatt_table = ioremap_nocache(virt_to_phys(table), (PAGE_SIZE * (1 << page_order))); @@ -1010,7 +1009,6 @@ int agp_generic_free_gatt_table(struct agp_bridge_data *bridge) case LVL2_APER_SIZE: /* The generic routines can't deal with 2 level gatt's */ return -EINVAL; - break; default: page_order = 0; break; @@ -1077,7 +1075,6 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) case LVL2_APER_SIZE: /* The generic routines can't deal with 2 level gatt's */ return -EINVAL; - break; default: num_entries = 0; break; @@ -1398,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/i460-agp.c b/drivers/char/agp/i460-agp.c index 75b763cb3ea..15b240ea484 100644 --- a/drivers/char/agp/i460-agp.c +++ b/drivers/char/agp/i460-agp.c @@ -587,8 +587,8 @@ const struct agp_bridge_driver intel_i460_driver = { .cant_use_aperture = true, }; -static int __devinit agp_intel_i460_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int agp_intel_i460_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { struct agp_bridge_data *bridge; u8 cap_ptr; @@ -611,7 +611,7 @@ static int __devinit agp_intel_i460_probe(struct pci_dev *pdev, return agp_add_bridge(bridge); } -static void __devexit agp_intel_i460_remove(struct pci_dev *pdev) +static void agp_intel_i460_remove(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); @@ -637,7 +637,7 @@ static struct pci_driver agp_intel_i460_pci_driver = { .name = "agpgart-intel-i460", .id_table = agp_intel_i460_pci_table, .probe = agp_intel_i460_probe, - .remove = __devexit_p(agp_intel_i460_remove), + .remove = agp_intel_i460_remove, }; static int __init agp_intel_i460_init(void) diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index b427711be4b..f9b9ca5d31b 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -12,9 +12,7 @@ #include <asm/smp.h> #include "agp.h" #include "intel-agp.h" - -int intel_agp_enabled; -EXPORT_SYMBOL(intel_agp_enabled); +#include <drm/intel-gtt.h> static int intel_fetch_size(void) { @@ -117,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; @@ -127,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); @@ -147,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; @@ -166,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; @@ -207,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; @@ -217,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); @@ -238,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; @@ -248,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); @@ -267,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; @@ -281,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 */ @@ -302,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; @@ -312,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); @@ -331,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; @@ -341,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); @@ -360,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; @@ -370,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); @@ -389,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; @@ -399,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); @@ -731,8 +721,8 @@ static const struct intel_agp_driver_description { { 0, NULL, NULL } }; -static int __devinit agp_intel_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int agp_intel_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { struct agp_bridge_data *bridge; u8 cap_ptr = 0; @@ -747,7 +737,7 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, bridge->capndx = cap_ptr; - if (intel_gmch_probe(pdev, bridge)) + if (intel_gmch_probe(pdev, NULL, bridge)) goto found_gmch; for (i = 0; intel_agp_chipsets[i].name != NULL; i++) { @@ -813,18 +803,16 @@ static int __devinit 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; } -static void __devexit agp_intel_remove(struct pci_dev *pdev) +static void agp_intel_remove(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); agp_remove_bridge(bridge); - intel_gmch_remove(pdev); + intel_gmch_remove(); agp_put_bridge(bridge); } @@ -850,6 +838,7 @@ static struct pci_device_id agp_intel_pci_table[] = { .subvendor = PCI_ANY_ID, \ .subdevice = PCI_ANY_ID, \ } + ID(PCI_DEVICE_ID_INTEL_82441), /* for HAS2 support */ ID(PCI_DEVICE_ID_INTEL_82443LX_0), ID(PCI_DEVICE_ID_INTEL_82443BX_0), ID(PCI_DEVICE_ID_INTEL_82443GX_0), @@ -897,15 +886,10 @@ static struct pci_device_id agp_intel_pci_table[] = { ID(PCI_DEVICE_ID_INTEL_B43_HB), ID(PCI_DEVICE_ID_INTEL_B43_1_HB), ID(PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB), + ID(PCI_DEVICE_ID_INTEL_IRONLAKE_D2_HB), ID(PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB), ID(PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB), ID(PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB), - ID(PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB), - ID(PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB), - ID(PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_HB), - ID(PCI_DEVICE_ID_INTEL_IVYBRIDGE_HB), - ID(PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_HB), - ID(PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_HB), { } }; @@ -915,7 +899,7 @@ static struct pci_driver agp_intel_pci_driver = { .name = "agpgart-intel", .id_table = agp_intel_pci_table, .probe = agp_intel_probe, - .remove = __devexit_p(agp_intel_remove), + .remove = agp_intel_remove, #ifdef CONFIG_PM .resume = agp_intel_resume, #endif diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h index 5da67f165af..fda073dcd96 100644 --- a/drivers/char/agp/intel-agp.h +++ b/drivers/char/agp/intel-agp.h @@ -55,18 +55,13 @@ #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 #define I810_PTE_VALID 0x00000001 #define I830_PTE_SYSTEM_CACHED 0x00000006 -/* GT PTE cache control fields */ -#define GEN6_PTE_UNCACHED 0x00000002 -#define GEN6_PTE_LLC 0x00000004 -#define GEN6_PTE_LLC_MLC 0x00000006 -#define GEN6_PTE_GFDT 0x00000008 #define I810_SMRAM_MISCC 0x70 #define I810_GFX_MEM_WIN_SIZE 0x00010000 @@ -118,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) @@ -146,29 +141,6 @@ #define INTEL_I7505_AGPCTRL 0x70 #define INTEL_I7505_MCHCFG 0x50 -#define SNB_GMCH_CTRL 0x50 -#define SNB_GMCH_GMS_STOLEN_MASK 0xF8 -#define SNB_GMCH_GMS_STOLEN_32M (1 << 3) -#define SNB_GMCH_GMS_STOLEN_64M (2 << 3) -#define SNB_GMCH_GMS_STOLEN_96M (3 << 3) -#define SNB_GMCH_GMS_STOLEN_128M (4 << 3) -#define SNB_GMCH_GMS_STOLEN_160M (5 << 3) -#define SNB_GMCH_GMS_STOLEN_192M (6 << 3) -#define SNB_GMCH_GMS_STOLEN_224M (7 << 3) -#define SNB_GMCH_GMS_STOLEN_256M (8 << 3) -#define SNB_GMCH_GMS_STOLEN_288M (9 << 3) -#define SNB_GMCH_GMS_STOLEN_320M (0xa << 3) -#define SNB_GMCH_GMS_STOLEN_352M (0xb << 3) -#define SNB_GMCH_GMS_STOLEN_384M (0xc << 3) -#define SNB_GMCH_GMS_STOLEN_416M (0xd << 3) -#define SNB_GMCH_GMS_STOLEN_448M (0xe << 3) -#define SNB_GMCH_GMS_STOLEN_480M (0xf << 3) -#define SNB_GMCH_GMS_STOLEN_512M (0x10 << 3) -#define SNB_GTT_SIZE_0M (0 << 8) -#define SNB_GTT_SIZE_1M (1 << 8) -#define SNB_GTT_SIZE_2M (2 << 8) -#define SNB_GTT_SIZE_MASK (3 << 8) - /* pci devices ids */ #define PCI_DEVICE_ID_INTEL_E7221_HB 0x2588 #define PCI_DEVICE_ID_INTEL_E7221_IG 0x258a @@ -211,31 +183,11 @@ #define PCI_DEVICE_ID_INTEL_G41_HB 0x2E30 #define PCI_DEVICE_ID_INTEL_G41_IG 0x2E32 #define PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB 0x0040 +#define PCI_DEVICE_ID_INTEL_IRONLAKE_D2_HB 0x0069 #define PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG 0x0042 #define PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB 0x0044 #define PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB 0x0062 #define PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB 0x006a #define PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG 0x0046 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB 0x0100 /* Desktop */ -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT1_IG 0x0102 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_IG 0x0112 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_PLUS_IG 0x0122 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB 0x0104 /* Mobile */ -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT1_IG 0x0106 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_IG 0x0116 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_PLUS_IG 0x0126 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_HB 0x0108 /* Server */ -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_IG 0x010A -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_HB 0x0150 /* Desktop */ -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_GT1_IG 0x0152 -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_GT2_IG 0x0162 -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_HB 0x0154 /* Mobile */ -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_GT1_IG 0x0156 -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_GT2_IG 0x0166 -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_HB 0x0158 /* Server */ -#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_GT1_IG 0x015A - -int intel_gmch_probe(struct pci_dev *pdev, - struct agp_bridge_data *bridge); -void intel_gmch_remove(struct pci_dev *pdev); + #endif diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index c92424ca1a5..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,13 +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 gma_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 */ @@ -76,7 +73,18 @@ static struct _intel_private { struct resource ifp_resource; int resource_valid; struct page *scratch_page; - dma_addr_t scratch_page_dma; + 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 @@ -85,40 +93,34 @@ static struct _intel_private { #define IS_IRONLAKE intel_private.driver->is_ironlake #define HAS_PGTBL_EN intel_private.driver->has_pgtbl_enable -int intel_gtt_map_memory(struct page **pages, unsigned int num_entries, - struct scatterlist **sg_list, int *num_sg) +#if IS_ENABLED(CONFIG_AGP_INTEL) +static int intel_gtt_map_memory(struct page **pages, + unsigned int num_entries, + struct sg_table *st) { - struct sg_table st; struct scatterlist *sg; int i; - if (*sg_list) - return 0; /* already mapped (for e.g. resume */ - DBG("try mapping %lu pages\n", (unsigned long)num_entries); - if (sg_alloc_table(&st, num_entries, GFP_KERNEL)) + if (sg_alloc_table(st, num_entries, GFP_KERNEL)) goto err; - *sg_list = sg = st.sgl; - - for (i = 0 ; i < num_entries; i++, sg = sg_next(sg)) + for_each_sg(st->sgl, sg, num_entries, i) sg_set_page(sg, pages[i], PAGE_SIZE, 0); - *num_sg = pci_map_sg(intel_private.pcidev, *sg_list, - num_entries, PCI_DMA_BIDIRECTIONAL); - if (unlikely(!*num_sg)) + if (!pci_map_sg(intel_private.pcidev, + st->sgl, st->nents, PCI_DMA_BIDIRECTIONAL)) goto err; return 0; err: - sg_free_table(&st); + sg_free_table(st); return -ENOMEM; } -EXPORT_SYMBOL(intel_gtt_map_memory); -void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg) +static void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg) { struct sg_table st; DBG("try unmapping %lu pages\n", (unsigned long)mem->page_count); @@ -131,7 +133,6 @@ void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg) sg_free_table(&st); } -EXPORT_SYMBOL(intel_gtt_unmap_memory); static void intel_fake_agp_enable(struct agp_bridge_data *bridge, u32 mode) { @@ -167,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. */ @@ -180,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, ®_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) @@ -190,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) { @@ -208,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) { @@ -288,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) { @@ -300,7 +303,7 @@ 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)) @@ -376,62 +379,6 @@ static unsigned int intel_gtt_stolen_size(void) stolen_size = 0; break; } - } else if (INTEL_GTT_GEN == 6) { - /* - * SandyBridge has new memory control reg at 0x50.w - */ - u16 snb_gmch_ctl; - pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); - switch (snb_gmch_ctl & SNB_GMCH_GMS_STOLEN_MASK) { - case SNB_GMCH_GMS_STOLEN_32M: - stolen_size = MB(32); - break; - case SNB_GMCH_GMS_STOLEN_64M: - stolen_size = MB(64); - break; - case SNB_GMCH_GMS_STOLEN_96M: - stolen_size = MB(96); - break; - case SNB_GMCH_GMS_STOLEN_128M: - stolen_size = MB(128); - break; - case SNB_GMCH_GMS_STOLEN_160M: - stolen_size = MB(160); - break; - case SNB_GMCH_GMS_STOLEN_192M: - stolen_size = MB(192); - break; - case SNB_GMCH_GMS_STOLEN_224M: - stolen_size = MB(224); - break; - case SNB_GMCH_GMS_STOLEN_256M: - stolen_size = MB(256); - break; - case SNB_GMCH_GMS_STOLEN_288M: - stolen_size = MB(288); - break; - case SNB_GMCH_GMS_STOLEN_320M: - stolen_size = MB(320); - break; - case SNB_GMCH_GMS_STOLEN_352M: - stolen_size = MB(352); - break; - case SNB_GMCH_GMS_STOLEN_384M: - stolen_size = MB(384); - break; - case SNB_GMCH_GMS_STOLEN_416M: - stolen_size = MB(416); - break; - case SNB_GMCH_GMS_STOLEN_448M: - stolen_size = MB(448); - break; - case SNB_GMCH_GMS_STOLEN_480M: - stolen_size = MB(480); - break; - case SNB_GMCH_GMS_STOLEN_512M: - stolen_size = MB(512); - break; - } } else { switch (gmch_ctrl & I855_GMCH_GMS_MASK) { case I855_GMCH_GMS_STOLEN_1M: @@ -565,33 +512,13 @@ static unsigned int i965_gtt_total_entries(void) static unsigned int intel_gtt_total_entries(void) { - int size; - if (IS_G33 || INTEL_GTT_GEN == 4 || INTEL_GTT_GEN == 5) return i965_gtt_total_entries(); - else if (INTEL_GTT_GEN == 6) { - u16 snb_gmch_ctl; - - pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); - switch (snb_gmch_ctl & SNB_GTT_SIZE_MASK) { - default: - case SNB_GTT_SIZE_0M: - printk(KERN_ERR "Bad GTT size mask: 0x%04x.\n", snb_gmch_ctl); - size = MB(0); - break; - case SNB_GTT_SIZE_1M: - size = MB(1); - break; - case SNB_GTT_SIZE_2M: - size = MB(2); - break; - } - return size/4; - } else { + else { /* 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; } } @@ -647,17 +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 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 = @@ -669,24 +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); - - gtt_map_size = intel_private.base.gtt_total_entries * 4; - - intel_private.gtt = ioremap(intel_private.gtt_bus_addr, - gtt_map_size); - if (!intel_private.gtt) { + intel_private.gtt_total_entries * 4, + intel_private.gtt_mappable_entries * 4); + + gtt_map_size = intel_private.gtt_total_entries * 4; + + intel_private.gtt = NULL; + 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_phys_addr, + gtt_map_size); + if (intel_private.gtt == NULL) { intel_private.driver->cleanup(); iounmap(intel_private.registers); return -ENOMEM; } +#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) { @@ -694,17 +662,23 @@ static int intel_gtt_init(void) return ret; } + if (INTEL_GTT_GEN <= 2) + bar = I810_GMADR_BAR; + else + 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) { @@ -716,6 +690,7 @@ static int intel_fake_agp_fetch_size(void) return 0; } +#endif static void i830_cleanup(void) { @@ -767,23 +742,10 @@ static void i830_write_entry(dma_addr_t addr, unsigned int entry, writel(addr | pte_flags, intel_private.gtt + entry); } -static bool intel_enable_gtt(void) +bool intel_enable_gtt(void) { - u32 gma_addr; u8 __iomem *reg; - if (INTEL_GTT_GEN <= 2) - pci_read_config_dword(intel_private.pcidev, I810_GMADDR, - &gma_addr); - else - pci_read_config_dword(intel_private.pcidev, I915_GMADDR, - &gma_addr); - - intel_private.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK); - - if (INTEL_GTT_GEN >= 6) - return true; - if (INTEL_GTT_GEN == 2) { u16 gmch_ctrl; @@ -823,23 +785,24 @@ static bool intel_enable_gtt(void) return true; } +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, ®_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; @@ -864,6 +827,7 @@ static int intel_fake_agp_configure(void) return 0; } +#endif static bool i830_check_flags(unsigned int flags) { @@ -878,8 +842,7 @@ static bool i830_check_flags(unsigned int flags) return false; } -void intel_gtt_insert_sg_entries(struct scatterlist *sg_list, - unsigned int sg_len, +void intel_gtt_insert_sg_entries(struct sg_table *st, unsigned int pg_start, unsigned int flags) { @@ -891,12 +854,11 @@ void intel_gtt_insert_sg_entries(struct scatterlist *sg_list, /* sg may merge pages, but we have to separate * per-page addr for GTT */ - for_each_sg(sg_list, sg, sg_len, i) { + for_each_sg(st->sgl, sg, st->nents, i) { len = sg_dma_len(sg) >> PAGE_SHIFT; for (m = 0; m < len; m++) { dma_addr_t addr = sg_dma_address(sg) + (m << PAGE_SHIFT); - intel_private.driver->write_entry(addr, - j, flags); + intel_private.driver->write_entry(addr, j, flags); j++; } } @@ -904,8 +866,11 @@ void intel_gtt_insert_sg_entries(struct scatterlist *sg_list, } EXPORT_SYMBOL(intel_gtt_insert_sg_entries); -void intel_gtt_insert_pages(unsigned int first_entry, unsigned int num_entries, - struct page **pages, unsigned int flags) +#if IS_ENABLED(CONFIG_AGP_INTEL) +static void intel_gtt_insert_pages(unsigned int first_entry, + unsigned int num_entries, + struct page **pages, + unsigned int flags) { int i, j; @@ -916,19 +881,15 @@ void intel_gtt_insert_pages(unsigned int first_entry, unsigned int num_entries, } readl(intel_private.gtt+j-1); } -EXPORT_SYMBOL(intel_gtt_insert_pages); static int intel_fake_agp_insert_entries(struct agp_memory *mem, off_t pg_start, int type) { 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; } @@ -939,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) @@ -951,14 +912,16 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem, if (!mem->is_flushed) global_cache_flush(); - if (intel_private.base.needs_dmar) { - ret = intel_gtt_map_memory(mem->pages, mem->page_count, - &mem->sg_list, &mem->num_sg); + if (intel_private.needs_dmar) { + struct sg_table st; + + ret = intel_gtt_map_memory(mem->pages, mem->page_count, &st); if (ret != 0) return ret; - intel_gtt_insert_sg_entries(mem->sg_list, mem->num_sg, - pg_start, type); + intel_gtt_insert_sg_entries(&st, pg_start, type); + mem->sg_list = st.sgl; + mem->num_sg = st.nents; } else intel_gtt_insert_pages(pg_start, mem->page_count, mem->pages, type); @@ -969,6 +932,7 @@ out_err: mem->is_flushed = true; return ret; } +#endif void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries) { @@ -982,18 +946,16 @@ void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries) } 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; @@ -1026,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) { @@ -1150,101 +1113,36 @@ static void i965_write_entry(dma_addr_t addr, writel(addr | pte_flags, intel_private.gtt + entry); } -static bool gen6_check_flags(unsigned int flags) -{ - return true; -} - -static void gen6_write_entry(dma_addr_t addr, unsigned int entry, - unsigned int flags) -{ - unsigned int type_mask = flags & ~AGP_USER_CACHED_MEMORY_GFDT; - unsigned int gfdt = flags & AGP_USER_CACHED_MEMORY_GFDT; - u32 pte_flags; - - if (type_mask == AGP_USER_MEMORY) - pte_flags = GEN6_PTE_UNCACHED | I810_PTE_VALID; - else if (type_mask == AGP_USER_CACHED_MEMORY_LLC_MLC) { - pte_flags = GEN6_PTE_LLC_MLC | I810_PTE_VALID; - if (gfdt) - pte_flags |= GEN6_PTE_GFDT; - } else { /* set 'normal'/'cached' to LLC by default */ - pte_flags = GEN6_PTE_LLC | I810_PTE_VALID; - if (gfdt) - pte_flags |= GEN6_PTE_GFDT; - } - - /* gen6 has bit11-4 for physical addr bit39-32 */ - addr |= (addr >> 28) & 0xff0; - writel(addr | pte_flags, intel_private.gtt + entry); -} - -static void gen6_cleanup(void) -{ -} - -/* 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; - extern int intel_iommu_gfx_mapped; - - /* 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; + phys_addr_t reg_addr; + int size = KB(512); - pci_read_config_dword(intel_private.pcidev, I915_MMADDR, ®_addr); + reg_addr = pci_resource_start(intel_private.pcidev, I915_MMADR_BAR); - reg_addr &= 0xfff80000; - - intel_private.registers = ioremap(reg_addr, 128 * 4096); + intel_private.registers = ioremap(reg_addr, size); if (!intel_private.registers) return -ENOMEM; - if (INTEL_GTT_GEN == 3) { - u32 gtt_addr; - - pci_read_config_dword(intel_private.pcidev, - I915_PTEADDR, >t_addr); - intel_private.gtt_bus_addr = gtt_addr; - } else { - u32 gtt_offset; - - switch (INTEL_GTT_GEN) { - case 5: - case 6: - gtt_offset = MB(2); - break; - case 4: - default: - gtt_offset = KB(512); - break; - } - intel_private.gtt_bus_addr = reg_addr + gtt_offset; + switch (INTEL_GTT_GEN) { + case 3: + intel_private.gtt_phys_addr = + pci_resource_start(intel_private.pcidev, I915_PTE_BAR); + break; + case 5: + intel_private.gtt_phys_addr = reg_addr + MB(2); + break; + default: + 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, @@ -1266,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, @@ -1346,15 +1245,6 @@ static const struct intel_gtt_driver ironlake_gtt_driver = { .check_flags = i830_check_flags, .chipset_flush = i9xx_chipset_flush, }; -static const struct intel_gtt_driver sandybridge_gtt_driver = { - .gen = 6, - .setup = i9xx_setup, - .cleanup = gen6_cleanup, - .write_entry = gen6_write_entry, - .dma_mask_size = 40, - .check_flags = gen6_check_flags, - .chipset_flush = i9xx_chipset_flush, -}; /* Table to describe Intel GMCH and AGP/PCIE GART drivers. At least one of * driver and gmch_driver must be non-null, and find_gmch will determine @@ -1435,30 +1325,6 @@ static const struct intel_gtt_driver_description { "HD Graphics", &ironlake_gtt_driver }, { PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, "HD Graphics", &ironlake_gtt_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT1_IG, - "Sandybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_IG, - "Sandybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_PLUS_IG, - "Sandybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT1_IG, - "Sandybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_IG, - "Sandybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_PLUS_IG, - "Sandybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_IG, - "Sandybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_IVYBRIDGE_GT1_IG, - "Ivybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_IVYBRIDGE_GT2_IG, - "Ivybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_GT1_IG, - "Ivybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_GT2_IG, - "Ivybridge", &sandybridge_gtt_driver }, - { PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_GT1_IG, - "Ivybridge", &sandybridge_gtt_driver }, { 0, NULL, NULL } }; @@ -1479,14 +1345,32 @@ static int find_gmch(u16 device) return 1; } -int intel_gmch_probe(struct pci_dev *pdev, - struct agp_bridge_data *bridge) +int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, + struct agp_bridge_data *bridge) { int i, mask; - intel_private.driver = NULL; + + /* + * Can be called from the fake agp driver but also directly from + * drm/i915.ko. Hence we need to check whether everything is set up + * already. + */ + if (intel_private.driver) { + intel_private.refcount++; + return 1; + } for (i = 0; intel_gtt_chipsets[i].name != NULL; i++) { - if (find_gmch(intel_gtt_chipsets[i].gmch_chip_id)) { + if (gpu_pdev) { + if (gpu_pdev->device == + intel_gtt_chipsets[i].gmch_chip_id) { + intel_private.pcidev = pci_dev_get(gpu_pdev); + intel_private.driver = + intel_gtt_chipsets[i].gtt_driver; + + break; + } + } else if (find_gmch(intel_gtt_chipsets[i].gmch_chip_id)) { intel_private.driver = intel_gtt_chipsets[i].gtt_driver; break; @@ -1496,13 +1380,19 @@ int intel_gmch_probe(struct pci_dev *pdev, if (!intel_private.driver) return 0; - bridge->driver = &intel_fake_agp_driver; - bridge->dev_private_data = &intel_private; - bridge->dev = 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(pdev); + intel_private.bridge_dev = pci_dev_get(bridge_pdev); - dev_info(&pdev->dev, "Intel %s Chipset\n", intel_gtt_chipsets[i].name); + dev_info(&bridge_pdev->dev, "Intel %s Chipset\n", intel_gtt_chipsets[i].name); mask = intel_private.driver->dma_mask_size; if (pci_set_dma_mask(intel_private.pcidev, DMA_BIT_MASK(mask))) @@ -1512,19 +1402,23 @@ int intel_gmch_probe(struct pci_dev *pdev, pci_set_consistent_dma_mask(intel_private.pcidev, DMA_BIT_MASK(mask)); - /*if (bridge->driver == &intel_810_driver) - return 1;*/ + if (intel_gtt_init() != 0) { + intel_gmch_remove(); - if (intel_gtt_init() != 0) return 0; + } return 1; } EXPORT_SYMBOL(intel_gmch_probe); -const 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); @@ -1535,12 +1429,16 @@ void intel_gtt_chipset_flush(void) } EXPORT_SYMBOL(intel_gtt_chipset_flush); -void intel_gmch_remove(struct pci_dev *pdev) +void intel_gmch_remove(void) { + if (--intel_private.refcount) + return; + if (intel_private.pcidev) pci_dev_put(intel_private.pcidev); if (intel_private.bridge_dev) pci_dev_put(intel_private.bridge_dev); + intel_private.driver = NULL; } EXPORT_SYMBOL(intel_gmch_remove); diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c index b9734a97818..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; @@ -332,8 +333,8 @@ static const struct agp_bridge_driver nvidia_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static int __devinit agp_nvidia_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int agp_nvidia_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { struct agp_bridge_data *bridge; u8 cap_ptr; @@ -388,7 +389,7 @@ static int __devinit agp_nvidia_probe(struct pci_dev *pdev, return agp_add_bridge(bridge); } -static void __devexit agp_nvidia_remove(struct pci_dev *pdev) +static void agp_nvidia_remove(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); @@ -399,8 +400,8 @@ static void __devexit 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 ffa888cd1c8..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> @@ -158,7 +157,6 @@ static int sgi_tioca_insert_memory(struct agp_memory *mem, off_t pg_start, break; case LVL2_APER_SIZE: return -EINVAL; - break; default: num_entries = 0; break; @@ -271,7 +269,7 @@ const struct agp_bridge_driver sgi_tioca_driver = { .num_aperture_sizes = 1, }; -static int __devinit agp_sgi_init(void) +static int agp_sgi_init(void) { unsigned int j; struct tioca_kernel *info; @@ -290,12 +288,11 @@ static int __devinit agp_sgi_init(void) j = 0; list_for_each_entry(info, &tioca_list, ca_list) { - struct list_head *tmp; if (list_empty(info->ca_devices)) continue; - list_for_each(tmp, info->ca_devices) { + list_for_each_entry(pdev, info->ca_devices, bus_list) { u8 cap_ptr; - pdev = pci_dev_b(tmp); + if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8)) continue; cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); @@ -329,7 +326,7 @@ static int __devinit agp_sgi_init(void) return 0; } -static void __devexit agp_sgi_cleanup(void) +static void agp_sgi_cleanup(void) { kfree(sgi_tioca_agp_bridges); sgi_tioca_agp_bridges = NULL; diff --git a/drivers/char/agp/sis-agp.c b/drivers/char/agp/sis-agp.c index 08704ae5395..2c74038da45 100644 --- a/drivers/char/agp/sis-agp.c +++ b/drivers/char/agp/sis-agp.c @@ -17,8 +17,8 @@ #define PCI_DEVICE_ID_SI_662 0x0662 #define PCI_DEVICE_ID_SI_671 0x0671 -static bool __devinitdata agp_sis_force_delay = 0; -static int __devinitdata agp_sis_agp_spec = -1; +static bool agp_sis_force_delay = 0; +static int agp_sis_agp_spec = -1; static int sis_fetch_size(void) { @@ -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, @@ -148,13 +147,13 @@ static struct agp_bridge_driver sis_driver = { }; // chipsets that require the 'delay hack' -static int sis_broken_chipsets[] __devinitdata = { +static int sis_broken_chipsets[] = { PCI_DEVICE_ID_SI_648, PCI_DEVICE_ID_SI_746, 0 // terminator }; -static void __devinit sis_get_driver(struct agp_bridge_data *bridge) +static void sis_get_driver(struct agp_bridge_data *bridge) { int i; @@ -180,8 +179,7 @@ static void __devinit sis_get_driver(struct agp_bridge_data *bridge) } -static int __devinit agp_sis_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int agp_sis_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct agp_bridge_data *bridge; u8 cap_ptr; @@ -211,7 +209,7 @@ static int __devinit agp_sis_probe(struct pci_dev *pdev, return agp_add_bridge(bridge); } -static void __devexit agp_sis_remove(struct pci_dev *pdev) +static void agp_sis_remove(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c index f02f9b07fd4..9b163b49d97 100644 --- a/drivers/char/agp/sworks-agp.c +++ b/drivers/char/agp/sworks-agp.c @@ -445,8 +445,8 @@ static const struct agp_bridge_driver sworks_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static int __devinit agp_serverworks_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int agp_serverworks_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { struct agp_bridge_data *bridge; struct pci_dev *bridge_dev; @@ -518,7 +518,7 @@ static int __devinit agp_serverworks_probe(struct pci_dev *pdev, return agp_add_bridge(bridge); } -static void __devexit agp_serverworks_remove(struct pci_dev *pdev) +static void agp_serverworks_remove(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index a32c492baf5..a56ee9bedd1 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -557,7 +557,7 @@ const struct agp_bridge_driver u3_agp_driver = { .needs_scratch_page = true, }; -static struct agp_device_ids uninorth_agp_device_ids[] __devinitdata = { +static struct agp_device_ids uninorth_agp_device_ids[] = { { .device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP, .chipset_name = "UniNorth", @@ -592,8 +592,8 @@ static struct agp_device_ids uninorth_agp_device_ids[] __devinitdata = { }, }; -static int __devinit agp_uninorth_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int agp_uninorth_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { struct agp_device_ids *devs = uninorth_agp_device_ids; struct agp_bridge_data *bridge; @@ -663,7 +663,7 @@ static int __devinit agp_uninorth_probe(struct pci_dev *pdev, return agp_add_bridge(bridge); } -static void __devexit agp_uninorth_remove(struct pci_dev *pdev) +static void agp_uninorth_remove(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c index 8bc38493740..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, @@ -224,7 +223,7 @@ static const struct agp_bridge_driver via_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static struct agp_device_ids via_agp_device_ids[] __devinitdata = +static struct agp_device_ids via_agp_device_ids[] = { { .device_id = PCI_DEVICE_ID_VIA_82C597_0, @@ -438,8 +437,7 @@ static void check_via_agp3 (struct agp_bridge_data *bridge) } -static int __devinit agp_via_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int agp_via_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct agp_device_ids *devs = via_agp_device_ids; struct agp_bridge_data *bridge; @@ -485,7 +483,7 @@ static int __devinit agp_via_probe(struct pci_dev *pdev, return agp_add_bridge(bridge); } -static void __devexit agp_via_remove(struct pci_dev *pdev) +static void agp_via_remove(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c index f4837a893df..dd9dfa15e9d 100644 --- a/drivers/char/apm-emulation.c +++ b/drivers/char/apm-emulation.c @@ -31,7 +31,6 @@ #include <linux/kthread.h> #include <linux/delay.h> -#include <asm/system.h> /* * The apm_bios device is one of the misc char devices. @@ -302,7 +301,7 @@ apm_ioctl(struct file *filp, u_int cmd, u_long arg) * anything critical, chill a bit on each iteration. */ while (wait_event_freezable(apm_suspend_waitqueue, - as->suspend_state == SUSPEND_DONE)) + as->suspend_state != SUSPEND_ACKED)) msleep(10); break; case SUSPEND_ACKTO: @@ -532,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)) @@ -539,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. @@ -551,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); } } @@ -602,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/briq_panel.c b/drivers/char/briq_panel.c deleted file mode 100644 index 095ab90535c..00000000000 --- a/drivers/char/briq_panel.c +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Drivers for the Total Impact PPC based computer "BRIQ" - * by Dr. Karsten Jeppesen - * - */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/tty.h> -#include <linux/timer.h> -#include <linux/kernel.h> -#include <linux/wait.h> -#include <linux/string.h> -#include <linux/ioport.h> -#include <linux/delay.h> -#include <linux/miscdevice.h> -#include <linux/fs.h> -#include <linux/mm.h> -#include <linux/init.h> - -#include <asm/uaccess.h> -#include <asm/io.h> -#include <asm/prom.h> - -#define BRIQ_PANEL_MINOR 156 -#define BRIQ_PANEL_VFD_IOPORT 0x0390 -#define BRIQ_PANEL_LED_IOPORT 0x0398 -#define BRIQ_PANEL_VER "1.1 (04/20/2002)" -#define BRIQ_PANEL_MSG0 "Loading Linux" - -static int vfd_is_open; -static unsigned char vfd[40]; -static int vfd_cursor; -static unsigned char ledpb, led; - -static void update_vfd(void) -{ - int i; - - /* cursor home */ - outb(0x02, BRIQ_PANEL_VFD_IOPORT); - for (i=0; i<20; i++) - outb(vfd[i], BRIQ_PANEL_VFD_IOPORT + 1); - - /* cursor to next line */ - outb(0xc0, BRIQ_PANEL_VFD_IOPORT); - for (i=20; i<40; i++) - outb(vfd[i], BRIQ_PANEL_VFD_IOPORT + 1); - -} - -static void set_led(char state) -{ - if (state == 'R') - led = 0x01; - else if (state == 'G') - led = 0x02; - else if (state == 'Y') - led = 0x03; - else if (state == 'X') - led = 0x00; - outb(led, BRIQ_PANEL_LED_IOPORT); -} - -static int briq_panel_open(struct inode *ino, struct file *filep) -{ - tty_lock(); - /* enforce single access, vfd_is_open is protected by BKL */ - if (vfd_is_open) { - tty_unlock(); - return -EBUSY; - } - vfd_is_open = 1; - - tty_unlock(); - return 0; -} - -static int briq_panel_release(struct inode *ino, struct file *filep) -{ - if (!vfd_is_open) - return -ENODEV; - - vfd_is_open = 0; - - return 0; -} - -static ssize_t briq_panel_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - unsigned short c; - unsigned char cp; - - if (!vfd_is_open) - return -ENODEV; - - c = (inb(BRIQ_PANEL_LED_IOPORT) & 0x000c) | (ledpb & 0x0003); - set_led(' '); - /* upper button released */ - if ((!(ledpb & 0x0004)) && (c & 0x0004)) { - cp = ' '; - ledpb = c; - if (copy_to_user(buf, &cp, 1)) - return -EFAULT; - return 1; - } - /* lower button released */ - else if ((!(ledpb & 0x0008)) && (c & 0x0008)) { - cp = '\r'; - ledpb = c; - if (copy_to_user(buf, &cp, 1)) - return -EFAULT; - return 1; - } else { - ledpb = c; - return 0; - } -} - -static void scroll_vfd( void ) -{ - int i; - - for (i=0; i<20; i++) { - vfd[i] = vfd[i+20]; - vfd[i+20] = ' '; - } - vfd_cursor = 20; -} - -static ssize_t briq_panel_write(struct file *file, const char __user *buf, size_t len, - loff_t *ppos) -{ - size_t indx = len; - int i, esc = 0; - - if (!vfd_is_open) - return -EBUSY; - - for (;;) { - char c; - if (!indx) - break; - if (get_user(c, buf)) - return -EFAULT; - if (esc) { - set_led(c); - esc = 0; - } else if (c == 27) { - esc = 1; - } else if (c == 12) { - /* do a form feed */ - for (i=0; i<40; i++) - vfd[i] = ' '; - vfd_cursor = 0; - } else if (c == 10) { - if (vfd_cursor < 20) - vfd_cursor = 20; - else if (vfd_cursor < 40) - vfd_cursor = 40; - else if (vfd_cursor < 60) - vfd_cursor = 60; - if (vfd_cursor > 59) - scroll_vfd(); - } else { - /* just a character */ - if (vfd_cursor > 39) - scroll_vfd(); - vfd[vfd_cursor++] = c; - } - indx--; - buf++; - } - update_vfd(); - - return len; -} - -static const struct file_operations briq_panel_fops = { - .owner = THIS_MODULE, - .read = briq_panel_read, - .write = briq_panel_write, - .open = briq_panel_open, - .release = briq_panel_release, - .llseek = noop_llseek, -}; - -static struct miscdevice briq_panel_miscdev = { - BRIQ_PANEL_MINOR, - "briq_panel", - &briq_panel_fops -}; - -static int __init briq_panel_init(void) -{ - struct device_node *root = of_find_node_by_path("/"); - const char *machine; - int i; - - machine = of_get_property(root, "model", NULL); - if (!machine || strncmp(machine, "TotalImpact,BRIQ-1", 18) != 0) { - of_node_put(root); - return -ENODEV; - } - of_node_put(root); - - printk(KERN_INFO - "briq_panel: v%s Dr. Karsten Jeppesen (kj@totalimpact.com)\n", - BRIQ_PANEL_VER); - - if (!request_region(BRIQ_PANEL_VFD_IOPORT, 4, "BRIQ Front Panel")) - return -EBUSY; - - if (!request_region(BRIQ_PANEL_LED_IOPORT, 2, "BRIQ Front Panel")) { - release_region(BRIQ_PANEL_VFD_IOPORT, 4); - return -EBUSY; - } - ledpb = inb(BRIQ_PANEL_LED_IOPORT) & 0x000c; - - if (misc_register(&briq_panel_miscdev) < 0) { - release_region(BRIQ_PANEL_VFD_IOPORT, 4); - release_region(BRIQ_PANEL_LED_IOPORT, 2); - return -EBUSY; - } - - outb(0x38, BRIQ_PANEL_VFD_IOPORT); /* Function set */ - outb(0x01, BRIQ_PANEL_VFD_IOPORT); /* Clear display */ - outb(0x0c, BRIQ_PANEL_VFD_IOPORT); /* Display on */ - outb(0x06, BRIQ_PANEL_VFD_IOPORT); /* Entry normal */ - for (i=0; i<40; i++) - vfd[i]=' '; -#ifndef MODULE - vfd[0] = 'L'; - vfd[1] = 'o'; - vfd[2] = 'a'; - vfd[3] = 'd'; - vfd[4] = 'i'; - vfd[5] = 'n'; - vfd[6] = 'g'; - vfd[7] = ' '; - vfd[8] = '.'; - vfd[9] = '.'; - vfd[10] = '.'; -#endif /* !MODULE */ - - update_vfd(); - - return 0; -} - -static void __exit briq_panel_exit(void) -{ - misc_deregister(&briq_panel_miscdev); - release_region(BRIQ_PANEL_VFD_IOPORT, 4); - release_region(BRIQ_PANEL_LED_IOPORT, 2); -} - -module_init(briq_panel_init); -module_exit(briq_panel_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Karsten Jeppesen <karsten@jeppesens.com>"); -MODULE_DESCRIPTION("Driver for the Total Impact briQ front panel"); diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c index 0c688232aab..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) { @@ -297,7 +302,6 @@ static int __init bsr_init(void) struct device_node *np; dev_t bsr_dev; int ret = -ENODEV; - int result; np = of_find_compatible_node(NULL, NULL, "ibm,bsr"); if (!np) @@ -306,13 +310,14 @@ static int __init bsr_init(void) bsr_class = class_create(THIS_MODULE, "bsr"); if (IS_ERR(bsr_class)) { printk(KERN_ERR "class_create() failed for bsr_class\n"); + ret = PTR_ERR(bsr_class); goto out_err_1; } - bsr_class->dev_attrs = bsr_dev_attrs; + bsr_class->dev_groups = bsr_dev_groups; - result = alloc_chrdev_region(&bsr_dev, 0, BSR_MAX_DEVS, "bsr"); + ret = alloc_chrdev_region(&bsr_dev, 0, BSR_MAX_DEVS, "bsr"); bsr_major = MAJOR(bsr_dev); - if (result < 0) { + if (ret < 0) { printk(KERN_ERR "alloc_chrdev_region() failed for bsr\n"); goto out_err_2; } diff --git a/drivers/char/ds1302.c b/drivers/char/ds1302.c index ed8303f9890..7d34b203718 100644 --- a/drivers/char/ds1302.c +++ b/drivers/char/ds1302.c @@ -24,7 +24,6 @@ #include <linux/uaccess.h> #include <linux/io.h> -#include <asm/system.h> #include <asm/rtc.h> #if defined(CONFIG_M32R) #include <asm/m32r.h> diff --git a/drivers/char/ds1620.c b/drivers/char/ds1620.c index aab9605f0b4..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> @@ -74,21 +75,21 @@ static inline void netwinder_ds1620_reset(void) static inline void netwinder_lock(unsigned long *flags) { - spin_lock_irqsave(&nw_gpio_lock, *flags); + raw_spin_lock_irqsave(&nw_gpio_lock, *flags); } static inline void netwinder_unlock(unsigned long *flags) { - spin_unlock_irqrestore(&nw_gpio_lock, *flags); + raw_spin_unlock_irqrestore(&nw_gpio_lock, *flags); } static inline void netwinder_set_fan(int i) { unsigned long flags; - spin_lock_irqsave(&nw_gpio_lock, flags); + raw_spin_lock_irqsave(&nw_gpio_lock, flags); nw_gpio_modify_op(GPIO_FAN, i ? GPIO_FAN : 0); - spin_unlock_irqrestore(&nw_gpio_lock, flags); + raw_spin_unlock_irqrestore(&nw_gpio_lock, flags); } static inline int netwinder_get_fan(void) @@ -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 53c524e7b82..e39e7402e62 100644 --- a/drivers/char/efirtc.c +++ b/drivers/char/efirtc.c @@ -34,10 +34,10 @@ #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> -#include <asm/system.h> #define EFI_RTC_VERSION "0.4" @@ -297,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; @@ -317,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) { @@ -390,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 f773a9dd14f..4f943759d37 100644 --- a/drivers/char/genrtc.c +++ b/drivers/char/genrtc.c @@ -52,11 +52,11 @@ #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> #include <asm/uaccess.h> -#include <asm/system.h> #include <asm/rtc.h> /* @@ -387,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", @@ -407,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" @@ -443,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" @@ -456,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 0833896cf6f..d5d4cd82b9f 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -34,16 +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/system.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. @@ -368,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; @@ -383,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) { @@ -499,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); @@ -738,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, @@ -749,7 +752,7 @@ static ctl_table hpet_table[] = { {} }; -static ctl_table hpet_root[] = { +static struct ctl_table hpet_root[] = { { .procname = "hpet", .maxlen = 0, @@ -759,7 +762,7 @@ static ctl_table hpet_root[] = { {} }; -static ctl_table dev_root[] = { +static struct ctl_table dev_root[] = { { .procname = "dev", .maxlen = 0, @@ -817,7 +820,7 @@ static unsigned long __hpet_calibrate(struct hpets *hpetp) static unsigned long hpet_calibrate(struct hpets *hpetp) { - unsigned long ret = -1; + unsigned long ret = ~0UL; unsigned long tmp; /* @@ -907,8 +910,8 @@ int hpet_alloc(struct hpet_data *hdp) hpetp->hp_which, hdp->hd_phys_address, hpetp->hp_ntimer > 1 ? "s" : ""); for (i = 0; i < hpetp->hp_ntimer; i++) - printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); - printk("\n"); + printk(KERN_CONT "%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); + printk(KERN_CONT "\n"); temp = hpetp->hp_tick_freq; remainder = do_div(temp, 1000000); @@ -984,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, @@ -1002,6 +1003,9 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) irqp = &res->data.extended_irq; for (i = 0; i < irqp->interrupt_count; i++) { + if (hdp->hd_nirqs >= HPET_MAX_TIMERS) + break; + irq = acpi_register_gsi(NULL, irqp->interrupts[i], irqp->triggering, irqp->polarity); if (irq < 0) @@ -1039,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 0689bf6b018..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,7 +64,7 @@ config HW_RANDOM_AMD config HW_RANDOM_ATMEL tristate "Atmel Random Number Generator support" - depends on HW_RANDOM && ARCH_AT91SAM9G45 + depends on ARCH_AT91 && HAVE_CLK default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number @@ -73,9 +75,35 @@ config HW_RANDOM_ATMEL If unsure, say Y. +config HW_RANDOM_BCM63XX + tristate "Broadcom BCM63xx Random Number Generator support" + depends on BCM63XX + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on the Broadcom BCM63xx SoCs. + + To compile this driver as a module, choose M here: the + module will be called bcm63xx-rng + + 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 @@ -88,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 @@ -101,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 @@ -113,12 +141,12 @@ config HW_RANDOM_VIA If unsure, say Y. config HW_RANDOM_IXP4XX - tristate "Intel IXP4xx NPU HW Random Number Generator support" - depends on HW_RANDOM && ARCH_IXP4XX + tristate "Intel IXP4xx NPU HW Pseudo-Random Number Generator support" + depends on ARCH_IXP4XX default HW_RANDOM ---help--- - This driver provides kernel-side support for the Random - Number Generator hardware found on the Intel IXP4xx NPU. + This driver provides kernel-side support for the Pseudo-Random + Number Generator hardware found on the Intel IXP45x/46x NPU. To compile this driver as a module, choose M here: the module will be called ixp4xx-rng. @@ -127,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 @@ -154,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 @@ -167,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. @@ -177,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 @@ -190,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. @@ -202,7 +244,8 @@ config HW_RANDOM_MXC_RNGA config HW_RANDOM_NOMADIK tristate "ST-Ericsson Nomadik Random Number Generator support" - depends on HW_RANDOM && PLAT_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). @@ -212,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. @@ -236,6 +268,73 @@ config HW_RANDOM_PPC4XX If unsure, say N. +config HW_RANDOM_PSERIES + tristate "pSeries HW Random Number Generator support" + depends on PPC64 && IBMVIO + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on POWER7+ machines and above + + To compile this driver as a module, choose M here: the + module will be called pseries-rng. + + 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 ARCH_EXYNOS + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on EXYNOS SOCs. + + To compile this driver as a module, choose M here: the + module will be called exynos-rng. + + If unsure, say Y. + +config HW_RANDOM_TPM + tristate "TPM HW Random Number Generator support" + depends on TCG_TPM + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator in the Trusted Platform Module + + To compile this driver as a module, choose M here: the + 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" diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index b2ff5265a99..199ed283e14 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -8,17 +8,24 @@ obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o obj-$(CONFIG_HW_RANDOM_ATMEL) += atmel-rng.o +obj-$(CONFIG_HW_RANDOM_BCM63XX) += bcm63xx-rng.o obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o obj-$(CONFIG_HW_RANDOM_N2RNG) += n2-rng.o 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 f518b99f53f..851bc7e20ad 100644 --- a/drivers/char/hw_random/atmel-rng.c +++ b/drivers/char/hw_random/atmel-rng.c @@ -34,8 +34,15 @@ static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max, u32 *data = buf; /* data ready? */ - if (readl(trng->base + TRNG_ODATA) & 1) { + if (readl(trng->base + TRNG_ISR) & 1) { *data = readl(trng->base + TRNG_ODATA); + /* + ensure data ready is only set again AFTER the next data + word is ready in case it got set between checking ISR + and reading ODATA, so we don't risk re-reading the + same word + */ + readl(trng->base + TRNG_ISR); return 4; } else return 0; @@ -47,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; @@ -85,13 +85,10 @@ static int atmel_trng_probe(struct platform_device *pdev) err_register: clk_disable(trng->clk); -err_enable: - clk_put(trng->clk); - return ret; } -static int __devexit atmel_trng_remove(struct platform_device *pdev) +static int atmel_trng_remove(struct platform_device *pdev) { struct atmel_trng *trng = platform_get_drvdata(pdev); @@ -99,9 +96,6 @@ static int __devexit 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; } @@ -131,7 +125,7 @@ static const struct dev_pm_ops atmel_trng_pm_ops = { static struct platform_driver atmel_trng_driver = { .probe = atmel_trng_probe, - .remove = __devexit_p(atmel_trng_remove), + .remove = atmel_trng_remove, .driver = { .name = "atmel-trng", .owner = THIS_MODULE, 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 new file mode 100644 index 00000000000..36581ea562c --- /dev/null +++ b/drivers/char/hw_random/bcm63xx-rng.c @@ -0,0 +1,173 @@ +/* + * Broadcom BCM63xx Random Number Generator support + * + * Copyright (C) 2011, Florian Fainelli <florian@openwrt.org> + * Copyright (C) 2009, Broadcom Corporation + * + */ +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/hw_random.h> + +#include <bcm63xx_io.h> +#include <bcm63xx_regs.h> + +struct bcm63xx_rng_priv { + struct clk *clk; + void __iomem *regs; +}; + +#define to_rng_priv(rng) ((struct bcm63xx_rng_priv *)rng->priv) + +static int bcm63xx_rng_init(struct hwrng *rng) +{ + struct bcm63xx_rng_priv *priv = to_rng_priv(rng); + u32 val; + + val = bcm_readl(priv->regs + RNG_CTRL); + val |= RNG_EN; + bcm_writel(val, priv->regs + RNG_CTRL); + + return 0; +} + +static void bcm63xx_rng_cleanup(struct hwrng *rng) +{ + struct bcm63xx_rng_priv *priv = to_rng_priv(rng); + u32 val; + + val = bcm_readl(priv->regs + RNG_CTRL); + val &= ~RNG_EN; + bcm_writel(val, priv->regs + RNG_CTRL); +} + +static int bcm63xx_rng_data_present(struct hwrng *rng, int wait) +{ + struct bcm63xx_rng_priv *priv = to_rng_priv(rng); + + return bcm_readl(priv->regs + RNG_STAT) & RNG_AVAIL_MASK; +} + +static int bcm63xx_rng_data_read(struct hwrng *rng, u32 *data) +{ + struct bcm63xx_rng_priv *priv = to_rng_priv(rng); + + *data = bcm_readl(priv->regs + RNG_DATA); + + return 4; +} + +static int bcm63xx_rng_probe(struct platform_device *pdev) +{ + struct resource *r; + struct clk *clk; + int ret; + struct bcm63xx_rng_priv *priv; + struct hwrng *rng; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no iomem resource\n"); + ret = -ENXIO; + goto out; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "no memory for private structure\n"); + ret = -ENOMEM; + goto out; + } + + rng = kzalloc(sizeof(*rng), GFP_KERNEL); + if (!rng) { + dev_err(&pdev->dev, "no memory for rng structure\n"); + ret = -ENOMEM; + goto out_free_priv; + } + + platform_set_drvdata(pdev, rng); + rng->priv = (unsigned long)priv; + rng->name = pdev->name; + rng->init = bcm63xx_rng_init; + rng->cleanup = bcm63xx_rng_cleanup; + rng->data_present = bcm63xx_rng_data_present; + rng->data_read = bcm63xx_rng_data_read; + + clk = clk_get(&pdev->dev, "ipsec"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "no clock for device\n"); + ret = PTR_ERR(clk); + goto out_free_rng; + } + + priv->clk = clk; + + if (!devm_request_mem_region(&pdev->dev, r->start, + resource_size(r), pdev->name)) { + dev_err(&pdev->dev, "request mem failed"); + ret = -ENOMEM; + goto out_free_rng; + } + + priv->regs = devm_ioremap_nocache(&pdev->dev, r->start, + resource_size(r)); + if (!priv->regs) { + dev_err(&pdev->dev, "ioremap failed"); + ret = -ENOMEM; + goto out_free_rng; + } + + clk_enable(clk); + + ret = hwrng_register(rng); + if (ret) { + dev_err(&pdev->dev, "failed to register rng device\n"); + goto out_clk_disable; + } + + dev_info(&pdev->dev, "registered RNG driver\n"); + + return 0; + +out_clk_disable: + clk_disable(clk); +out_free_rng: + kfree(rng); +out_free_priv: + kfree(priv); +out: + return ret; +} + +static int bcm63xx_rng_remove(struct platform_device *pdev) +{ + struct hwrng *rng = platform_get_drvdata(pdev); + struct bcm63xx_rng_priv *priv = to_rng_priv(rng); + + hwrng_unregister(rng); + clk_disable(priv->clk); + kfree(priv); + kfree(rng); + + return 0; +} + +static struct platform_driver bcm63xx_rng_driver = { + .probe = bcm63xx_rng_probe, + .remove = bcm63xx_rng_remove, + .driver = { + .name = "bcm63xx-rng", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(bcm63xx_rng_driver); + +MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); +MODULE_DESCRIPTION("Broadcom BCM63xx RNG driver"); +MODULE_LICENSE("GPL"); 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 new file mode 100644 index 00000000000..9f8277cc44b --- /dev/null +++ b/drivers/char/hw_random/exynos-rng.c @@ -0,0 +1,183 @@ +/* + * exynos-rng.c - Random Number Generator driver for the exynos + * + * Copyright (C) 2012 Samsung Electronics + * Jonghwa Lee <jonghwa3.lee@smasung.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; + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/hw_random.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> +#include <linux/err.h> + +#define EXYNOS_PRNG_STATUS_OFFSET 0x10 +#define EXYNOS_PRNG_SEED_OFFSET 0x140 +#define EXYNOS_PRNG_OUT1_OFFSET 0x160 +#define SEED_SETTING_DONE BIT(1) +#define PRNG_START 0x18 +#define PRNG_DONE BIT(5) +#define EXYNOS_AUTOSUSPEND_DELAY 100 + +struct exynos_rng { + struct device *dev; + struct hwrng rng; + void __iomem *mem; + struct clk *clk; +}; + +static u32 exynos_rng_readl(struct exynos_rng *rng, u32 offset) +{ + return __raw_readl(rng->mem + offset); +} + +static void exynos_rng_writel(struct exynos_rng *rng, u32 val, u32 offset) +{ + __raw_writel(val, rng->mem + offset); +} + +static int exynos_init(struct hwrng *rng) +{ + struct exynos_rng *exynos_rng = container_of(rng, + struct exynos_rng, rng); + int i; + int ret = 0; + + pm_runtime_get_sync(exynos_rng->dev); + + for (i = 0 ; i < 5 ; i++) + exynos_rng_writel(exynos_rng, jiffies, + EXYNOS_PRNG_SEED_OFFSET + 4*i); + + if (!(exynos_rng_readl(exynos_rng, EXYNOS_PRNG_STATUS_OFFSET) + & SEED_SETTING_DONE)) + ret = -EIO; + + pm_runtime_put_noidle(exynos_rng->dev); + + return ret; +} + +static int exynos_read(struct hwrng *rng, void *buf, + size_t max, bool wait) +{ + struct exynos_rng *exynos_rng = container_of(rng, + struct exynos_rng, rng); + u32 *data = buf; + + pm_runtime_get_sync(exynos_rng->dev); + + exynos_rng_writel(exynos_rng, PRNG_START, 0); + + while (!(exynos_rng_readl(exynos_rng, + EXYNOS_PRNG_STATUS_OFFSET) & PRNG_DONE)) + cpu_relax(); + + exynos_rng_writel(exynos_rng, PRNG_DONE, EXYNOS_PRNG_STATUS_OFFSET); + + *data = exynos_rng_readl(exynos_rng, EXYNOS_PRNG_OUT1_OFFSET); + + pm_runtime_mark_last_busy(exynos_rng->dev); + pm_runtime_autosuspend(exynos_rng->dev); + + return 4; +} + +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); + if (!exynos_rng) + return -ENOMEM; + + exynos_rng->dev = &pdev->dev; + exynos_rng->rng.name = "exynos"; + exynos_rng->rng.init = exynos_init; + exynos_rng->rng.read = exynos_read; + exynos_rng->clk = devm_clk_get(&pdev->dev, "secss"); + if (IS_ERR(exynos_rng->clk)) { + dev_err(&pdev->dev, "Couldn't get clock.\n"); + return -ENOENT; + } + + 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); + + pm_runtime_set_autosuspend_delay(&pdev->dev, EXYNOS_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + return hwrng_register(&exynos_rng->rng); +} + +static int exynos_rng_remove(struct platform_device *pdev) +{ + struct exynos_rng *exynos_rng = platform_get_drvdata(pdev); + + hwrng_unregister(&exynos_rng->rng); + + 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); + struct exynos_rng *exynos_rng = platform_get_drvdata(pdev); + + clk_disable_unprepare(exynos_rng->clk); + + return 0; +} + +static int exynos_rng_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct exynos_rng *exynos_rng = platform_get_drvdata(pdev); + + return clk_prepare_enable(exynos_rng->clk); +} +#endif + +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 = { + .driver = { + .name = "exynos-rng", + .owner = THIS_MODULE, + .pm = &exynos_rng_pm_ops, + }, + .probe = exynos_rng_probe, + .remove = exynos_rng_remove, +}; + +module_platform_driver(exynos_rng_driver); + +MODULE_DESCRIPTION("EXYNOS 4 H/W Random Number Generator driver"); +MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/ixp4xx-rng.c b/drivers/char/hw_random/ixp4xx-rng.c index 263567f5f39..beec1627db3 100644 --- a/drivers/char/hw_random/ixp4xx-rng.c +++ b/drivers/char/hw_random/ixp4xx-rng.c @@ -45,6 +45,9 @@ static int __init ixp4xx_rng_init(void) void __iomem * rng_base; int err; + if (!cpu_is_ixp46x()) /* includes IXP455 */ + return -ENOSYS; + rng_base = ioremap(0x70002100, 4); if (!rng_base) return -ENOMEM; @@ -68,5 +71,5 @@ module_init(ixp4xx_rng_init); module_exit(ixp4xx_rng_exit); MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>"); -MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver for IXP4xx"); +MODULE_DESCRIPTION("H/W Pseudo-Random Number Generator (RNG) driver for IXP45x/46x"); MODULE_LICENSE("GPL"); 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 187c6be80f4..6a86b6f56af 100644 --- a/drivers/char/hw_random/mxc-rnga.c +++ b/drivers/char/hw_random/mxc-rnga.c @@ -24,6 +24,7 @@ #include <linux/ioport.h> #include <linux/platform_device.h> #include <linux/hw_random.h> +#include <linux/delay.h> #include <linux/io.h> /* RNGA Registers */ @@ -58,38 +59,47 @@ #define RNGA_STATUS_LAST_READ_STATUS 0x00000002 #define RNGA_STATUS_SECURITY_VIOLATION 0x00000001 -static struct platform_device *rng_dev; +struct mxc_rng { + struct device *dev; + struct hwrng rng; + void __iomem *mem; + struct clk *clk; +}; -static int mxc_rnga_data_present(struct hwrng *rng) +static int mxc_rnga_data_present(struct hwrng *rng, int wait) { - int level; - void __iomem *rng_base = (void __iomem *)rng->priv; - - /* how many random numbers is in FIFO? [0-16] */ - level = ((__raw_readl(rng_base + RNGA_STATUS) & - RNGA_STATUS_LEVEL_MASK) >> 8); - - return level > 0 ? 1 : 0; + int i; + struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); + + for (i = 0; i < 20; i++) { + /* how many random numbers are in FIFO? [0-16] */ + int level = (__raw_readl(mxc_rng->mem + RNGA_STATUS) & + RNGA_STATUS_LEVEL_MASK) >> 8; + if (level || !wait) + return !!level; + udelay(10); + } + return 0; } static int mxc_rnga_data_read(struct hwrng *rng, u32 * data) { int err; u32 ctrl; - void __iomem *rng_base = (void __iomem *)rng->priv; + struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); /* retrieve a random number from FIFO */ - *data = __raw_readl(rng_base + RNGA_OUTPUT_FIFO); + *data = __raw_readl(mxc_rng->mem + RNGA_OUTPUT_FIFO); /* some error while reading this random number? */ - err = __raw_readl(rng_base + RNGA_STATUS) & RNGA_STATUS_ERROR_INT; + err = __raw_readl(mxc_rng->mem + RNGA_STATUS) & RNGA_STATUS_ERROR_INT; /* if error: clear error interrupt, but doesn't return random number */ if (err) { - dev_dbg(&rng_dev->dev, "Error while reading random number!\n"); - ctrl = __raw_readl(rng_base + RNGA_CONTROL); + dev_dbg(mxc_rng->dev, "Error while reading random number!\n"); + ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL); __raw_writel(ctrl | RNGA_CONTROL_CLEAR_INT, - rng_base + RNGA_CONTROL); + mxc_rng->mem + RNGA_CONTROL); return 0; } else return 4; @@ -98,22 +108,22 @@ static int mxc_rnga_data_read(struct hwrng *rng, u32 * data) static int mxc_rnga_init(struct hwrng *rng) { u32 ctrl, osc; - void __iomem *rng_base = (void __iomem *)rng->priv; + struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); /* wake up */ - ctrl = __raw_readl(rng_base + RNGA_CONTROL); - __raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, rng_base + RNGA_CONTROL); + ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL); + __raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, mxc_rng->mem + RNGA_CONTROL); /* verify if oscillator is working */ - osc = __raw_readl(rng_base + RNGA_STATUS); + osc = __raw_readl(mxc_rng->mem + RNGA_STATUS); if (osc & RNGA_STATUS_OSC_DEAD) { - dev_err(&rng_dev->dev, "RNGA Oscillator is dead!\n"); + dev_err(mxc_rng->dev, "RNGA Oscillator is dead!\n"); return -ENODEV; } /* go running */ - ctrl = __raw_readl(rng_base + RNGA_CONTROL); - __raw_writel(ctrl | RNGA_CONTROL_GO, rng_base + RNGA_CONTROL); + ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL); + __raw_writel(ctrl | RNGA_CONTROL_GO, mxc_rng->mem + RNGA_CONTROL); return 0; } @@ -121,83 +131,62 @@ static int mxc_rnga_init(struct hwrng *rng) static void mxc_rnga_cleanup(struct hwrng *rng) { u32 ctrl; - void __iomem *rng_base = (void __iomem *)rng->priv; + struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); - ctrl = __raw_readl(rng_base + RNGA_CONTROL); + ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL); /* stop rnga */ - __raw_writel(ctrl & ~RNGA_CONTROL_GO, rng_base + RNGA_CONTROL); + __raw_writel(ctrl & ~RNGA_CONTROL_GO, mxc_rng->mem + RNGA_CONTROL); } -static struct hwrng mxc_rnga = { - .name = "mxc-rnga", - .init = mxc_rnga_init, - .cleanup = mxc_rnga_cleanup, - .data_present = mxc_rnga_data_present, - .data_read = mxc_rnga_data_read -}; - static int __init mxc_rnga_probe(struct platform_device *pdev) { int err = -ENODEV; - struct clk *clk; - struct resource *res, *mem; - void __iomem *rng_base = NULL; - - if (rng_dev) - return -EBUSY; - - clk = clk_get(&pdev->dev, "rng"); - if (IS_ERR(clk)) { + struct resource *res; + struct mxc_rng *mxc_rng; + + mxc_rng = devm_kzalloc(&pdev->dev, sizeof(struct mxc_rng), + GFP_KERNEL); + if (!mxc_rng) + return -ENOMEM; + + mxc_rng->dev = &pdev->dev; + mxc_rng->rng.name = "mxc-rnga"; + mxc_rng->rng.init = mxc_rnga_init; + mxc_rng->rng.cleanup = mxc_rnga_cleanup, + mxc_rng->rng.data_present = mxc_rnga_data_present, + mxc_rng->rng.data_read = mxc_rnga_data_read, + + mxc_rng->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mxc_rng->clk)) { dev_err(&pdev->dev, "Could not get rng_clk!\n"); - err = PTR_ERR(clk); + err = PTR_ERR(mxc_rng->clk); goto out; } - clk_enable(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; - } - - rng_base = ioremap(res->start, resource_size(res)); - if (!rng_base) { - 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; } - mxc_rnga.priv = (unsigned long)rng_base; - - err = hwrng_register(&mxc_rnga); + err = hwrng_register(&mxc_rng->rng); if (err) { dev_err(&pdev->dev, "MXC RNGA registering failed (%d)\n", err); - goto err_register; + goto err_ioremap; } - rng_dev = pdev; - dev_info(&pdev->dev, "MXC RNGA Registered.\n"); return 0; -err_register: - iounmap(rng_base); - rng_base = NULL; - err_ioremap: - release_mem_region(res->start, resource_size(res)); - -err_region: - clk_disable(clk); - clk_put(clk); + clk_disable_unprepare(mxc_rng->clk); out: return err; @@ -205,18 +194,11 @@ out: static int __exit mxc_rnga_remove(struct platform_device *pdev) { - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - void __iomem *rng_base = (void __iomem *)mxc_rnga.priv; - struct clk *clk = clk_get(&pdev->dev, "rng"); - - hwrng_unregister(&mxc_rnga); + struct mxc_rng *mxc_rng = platform_get_drvdata(pdev); - iounmap(rng_base); + hwrng_unregister(&mxc_rng->rng); - release_mem_region(res->start, resource_size(res)); - - clk_disable(clk); - clk_put(clk); + clk_disable_unprepare(mxc_rng->clk); return 0; } @@ -229,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 ebd48f0135d..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> @@ -25,7 +24,7 @@ #define DRV_MODULE_VERSION "0.2" #define DRV_MODULE_RELDATE "July 27, 2011" -static char version[] __devinitdata = +static char version[] = DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); @@ -611,7 +610,7 @@ static void n2rng_work(struct work_struct *work) schedule_delayed_work(&np->work, HZ * 2); } -static void __devinit n2rng_driver_version(void) +static void n2rng_driver_version(void) { static int n2rng_version_printed; @@ -620,7 +619,7 @@ static void __devinit n2rng_driver_version(void) } static const struct of_device_id n2rng_match[]; -static int __devinit n2rng_probe(struct platform_device *op) +static int n2rng_probe(struct platform_device *op) { const struct of_device_id *match; int multi_capable; @@ -633,7 +632,7 @@ static int __devinit 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 __devinit 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 __devinit 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 __devinit 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 __devexit n2rng_remove(struct platform_device *op) +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 __devexit 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; } @@ -767,7 +754,7 @@ static struct platform_driver n2rng_driver = { .of_match_table = n2rng_match, }, .probe = n2rng_probe, - .remove = __devexit_p(n2rng_remove), + .remove = n2rng_remove, }; module_platform_driver(n2rng_driver); diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c index 3d3c1e6703b..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; } @@ -107,17 +101,6 @@ static struct amba_driver nmk_rng_driver = { .id_table = nmk_rng_ids, }; -static int __init nmk_rng_init(void) -{ - return amba_driver_register(&nmk_rng_driver); -} - -static void __devexit nmk_rng_exit(void) -{ - amba_driver_unregister(&nmk_rng_driver); -} - -module_init(nmk_rng_init); -module_exit(nmk_rng_exit); +module_amba_driver(nmk_rng_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/octeon-rng.c b/drivers/char/hw_random/octeon-rng.c index 0943edc782a..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> @@ -56,7 +55,7 @@ static int octeon_rng_data_read(struct hwrng *rng, u32 *data) return sizeof(u32); } -static int __devinit octeon_rng_probe(struct platform_device *pdev) +static int octeon_rng_probe(struct platform_device *pdev) { struct resource *res_ports; struct resource *res_result; @@ -75,47 +74,40 @@ static int __devinit octeon_rng_probe(struct platform_device *pdev) res_ports = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res_ports) - goto err_ports; + return -ENOENT; res_result = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res_result) - goto err_ports; + return -ENOENT; rng->control_status = devm_ioremap_nocache(&pdev->dev, res_ports->start, sizeof(u64)); if (!rng->control_status) - goto err_ports; + return -ENOENT; rng->result = devm_ioremap_nocache(&pdev->dev, res_result->start, sizeof(u64)); if (!rng->result) - goto err_r; + return -ENOENT; rng->ops = ops; - dev_set_drvdata(&pdev->dev, &rng->ops); + platform_set_drvdata(pdev, &rng->ops); ret = hwrng_register(&rng->ops); if (ret) - goto err; + return -ENOENT; dev_info(&pdev->dev, "Octeon Random Number Generator\n"); return 0; -err: - devm_iounmap(&pdev->dev, rng->control_status); -err_r: - devm_iounmap(&pdev->dev, rng->result); -err_ports: - devm_kfree(&pdev->dev, rng); - return -ENOENT; } 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 b757fac3cd1..f66ea258382 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -18,52 +18,137 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/random.h> -#include <linux/clk.h> #include <linux/err.h> #include <linux/platform_device.h> #include <linux/hw_random.h> #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 void __iomem *rng_base; -static struct clk *rng_ick; -static struct platform_device *rng_dev; +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_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_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); +}; -static inline u32 omap_rng_read_reg(int reg) +struct omap_rng_dev { + void __iomem *base; + struct device *dev; + const struct omap_rng_pdata *pdata; +}; + +static inline u32 omap_rng_read(struct omap_rng_dev *priv, u16 reg) { - return __raw_readl(rng_base + reg); + return __raw_readl(priv->base + priv->pdata->regs[reg]); } -static inline void omap_rng_write_reg(int reg, u32 val) +static inline void omap_rng_write(struct omap_rng_dev *priv, u16 reg, + u32 val) { - __raw_writel(val, rng_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_dev *priv; int data, i; + priv = (struct omap_rng_dev *)rng->priv; + for (i = 0; i < 20; i++) { - data = omap_rng_read_reg(RNG_STAT_REG) ? 0 : 1; + data = priv->pdata->data_present(priv); if (data || !wait) break; /* RNG produces data fast enough (2+ MBit/sec, even @@ -78,154 +163,308 @@ static int omap_rng_data_present(struct hwrng *rng, int wait) static int omap_rng_data_read(struct hwrng *rng, u32 *data) { - *data = omap_rng_read_reg(RNG_OUT_REG); + 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); + + if (priv->pdata->regs[RNG_INTACK_REG]) + omap_rng_write(priv, RNG_INTACK_REG, RNG_REG_INTACK_RDY_MASK); + return data_size; +} + +static int omap_rng_init(struct hwrng *rng) +{ + struct omap_rng_dev *priv; + + 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; - return 4; + 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 int __devinit omap_rng_probe(struct platform_device *pdev) +static inline u32 omap2_rng_data_present(struct omap_rng_dev *priv) { - struct resource *res; - int ret; + 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; /* - * A bit ugly, and it will never actually happen but there can - * be only one RNG and this catches any bork + * 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. */ - if (rng_dev) - return -EBUSY; - - if (cpu_is_omap24xx()) { - rng_ick = clk_get(&pdev->dev, "ick"); - if (IS_ERR(rng_ick)) { - dev_err(&pdev->dev, "Could not get rng_ick\n"); - ret = PTR_ERR(rng_ick); - return ret; - } else - clk_enable(rng_ick); - } + omap_rng_write(priv, RNG_ALARMMASK_REG, 0x0); + omap_rng_write(priv, RNG_ALARMSTOP_REG, 0x0); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + 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; - if (!res) { - ret = -ENOENT; - goto err_region; - } + 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; +} - if (!request_mem_region(res->start, resource_size(res), pdev->name)) { - ret = -EBUSY; - goto err_region; +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_dev *priv; + struct resource *res; + struct device *dev = &pdev->dev; + int ret; + + priv = devm_kzalloc(dev, sizeof(struct omap_rng_dev), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + omap_rng_ops.priv = (unsigned long)priv; + platform_set_drvdata(pdev, priv); + priv->dev = dev; - dev_set_drvdata(&pdev->dev, res); - rng_base = ioremap(res->start, resource_size(res)); - if (!rng_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; } + 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(RNG_REV_REG)); - omap_rng_write_reg(RNG_MASK_REG, 0x1); - - rng_dev = pdev; + omap_rng_read(priv, RNG_REV_REG)); return 0; err_register: - iounmap(rng_base); - rng_base = NULL; + priv->base = NULL; + pm_runtime_disable(&pdev->dev); err_ioremap: - release_mem_region(res->start, resource_size(res)); -err_region: - if (cpu_is_omap24xx()) { - clk_disable(rng_ick); - clk_put(rng_ick); - } + dev_err(dev, "initialization failed.\n"); return ret; } static int __exit omap_rng_remove(struct platform_device *pdev) { - struct resource *res = dev_get_drvdata(&pdev->dev); + struct omap_rng_dev *priv = platform_get_drvdata(pdev); hwrng_unregister(&omap_rng_ops); - omap_rng_write_reg(RNG_MASK_REG, 0x0); + priv->pdata->cleanup(priv); - iounmap(rng_base); - - if (cpu_is_omap24xx()) { - clk_disable(rng_ick); - clk_put(rng_ick); - } - - release_mem_region(res->start, resource_size(res)); - rng_base = NULL; + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP -static int omap_rng_suspend(struct platform_device *pdev, pm_message_t message) +static int omap_rng_suspend(struct device *dev) { - omap_rng_write_reg(RNG_MASK_REG, 0x0); + struct omap_rng_dev *priv = dev_get_drvdata(dev); + + priv->pdata->cleanup(priv); + pm_runtime_put_sync(dev); + return 0; } -static int omap_rng_resume(struct platform_device *pdev) +static int omap_rng_resume(struct device *dev) { - omap_rng_write_reg(RNG_MASK_REG, 0x1); + struct omap_rng_dev *priv = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + priv->pdata->init(priv); + return 0; } +static SIMPLE_DEV_PM_OPS(omap_rng_pm, omap_rng_suspend, omap_rng_resume); +#define OMAP_RNG_PM (&omap_rng_pm) + #else -#define omap_rng_suspend NULL -#define omap_rng_resume NULL +#define OMAP_RNG_PM NULL #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), - .suspend = omap_rng_suspend, - .resume = omap_rng_resume }; -static int __init omap_rng_init(void) -{ - if (!cpu_is_omap16xx() && !cpu_is_omap24xx()) - return -ENODEV; - - 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 3a632673aed..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> @@ -94,7 +95,7 @@ static struct hwrng pasemi_rng = { .data_read = pasemi_rng_data_read, }; -static int __devinit rng_probe(struct platform_device *ofdev) +static int rng_probe(struct platform_device *ofdev) { void __iomem *rng_regs; struct device_node *rng_np = ofdev->dev.of_node; @@ -122,7 +123,7 @@ static int __devinit rng_probe(struct platform_device *ofdev) return err; } -static int __devexit rng_remove(struct platform_device *dev) +static int rng_remove(struct platform_device *dev) { void __iomem *rng_regs = (void __iomem *)pasemi_rng.priv; diff --git a/drivers/char/hw_random/picoxcell-rng.c b/drivers/char/hw_random/picoxcell-rng.c deleted file mode 100644 index 97bd891422c..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 __devexit 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 = __devexit_p(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 c51762c1303..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> @@ -90,7 +91,7 @@ static struct hwrng ppc4xx_rng = { .data_read = ppc4xx_rng_data_read, }; -static int __devinit ppc4xx_rng_probe(struct platform_device *dev) +static int ppc4xx_rng_probe(struct platform_device *dev) { void __iomem *rng_regs; int err = 0; @@ -111,7 +112,7 @@ static int __devinit ppc4xx_rng_probe(struct platform_device *dev) return err; } -static int __devexit ppc4xx_rng_remove(struct platform_device *dev) +static int ppc4xx_rng_remove(struct platform_device *dev) { void __iomem *rng_regs = (void __iomem *) ppc4xx_rng.priv; diff --git a/drivers/char/hw_random/pseries-rng.c b/drivers/char/hw_random/pseries-rng.c new file mode 100644 index 00000000000..ab7ffdec0ec --- /dev/null +++ b/drivers/char/hw_random/pseries-rng.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010 Michael Neuling IBM Corporation + * + * Driver for the pseries hardware RNG for POWER7+ and above + * + * 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. + * + * 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., 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> + + +static int pseries_rng_data_read(struct hwrng *rng, u32 *data) +{ + 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; +} + +/** + * pseries_rng_get_desired_dma - Return desired DMA allocate for CMO operations + * + * This is a required function for a driver to operate in a CMO environment + * but this device does not make use of DMA allocations, return 0. + * + * Return value: + * Number of bytes of IO data the driver will need to perform well -> 0 + */ +static unsigned long pseries_rng_get_desired_dma(struct vio_dev *vdev) +{ + return 0; +}; + +static struct hwrng pseries_rng = { + .name = KBUILD_MODNAME, + .data_read = pseries_rng_data_read, +}; + +static int __init pseries_rng_probe(struct vio_dev *dev, + const struct vio_device_id *id) +{ + return hwrng_register(&pseries_rng); +} + +static int __exit pseries_rng_remove(struct vio_dev *dev) +{ + hwrng_unregister(&pseries_rng); + return 0; +} + +static struct vio_device_id pseries_rng_driver_ids[] = { + { "ibm,random-v1", "ibm,random"}, + { "", "" } +}; +MODULE_DEVICE_TABLE(vio, pseries_rng_driver_ids); + +static struct vio_driver pseries_rng_driver = { + .name = KBUILD_MODNAME, + .probe = pseries_rng_probe, + .remove = pseries_rng_remove, + .get_desired_dma = pseries_rng_get_desired_dma, + .id_table = pseries_rng_driver_ids +}; + +static int __init rng_init(void) +{ + printk(KERN_INFO "Registering IBM pSeries RNG driver\n"); + return vio_register_driver(&pseries_rng_driver); +} + +module_init(rng_init); + +static void __exit rng_exit(void) +{ + vio_unregister_driver(&pseries_rng_driver); +} +module_exit(rng_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Neuling <mikey@neuling.org>"); +MODULE_DESCRIPTION("H/W RNG driver for IBM pSeries processors"); diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c index f1a1618db1f..b6ab9ac3f34 100644 --- a/drivers/char/hw_random/timeriomem-rng.c +++ b/drivers/char/hw_random/timeriomem-rng.c @@ -23,130 +23,188 @@ #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 __devinit timeriomem_rng_probe(struct platform_device *pdev) +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 __devexit timeriomem_rng_remove(struct platform_device *pdev) +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 = __devexit_p(timeriomem_rng_remove), + .remove = timeriomem_rng_remove, }; module_platform_driver(timeriomem_rng_driver); diff --git a/drivers/char/hw_random/tpm-rng.c b/drivers/char/hw_random/tpm-rng.c new file mode 100644 index 00000000000..d6d448266f0 --- /dev/null +++ b/drivers/char/hw_random/tpm-rng.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 Kent Yoder IBM Corporation + * + * HWRNG interfaces to pull RNG data from a TPM + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/hw_random.h> +#include <linux/tpm.h> + +#define MODULE_NAME "tpm-rng" + +static int tpm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ + return tpm_get_random(TPM_ANY_NUM, data, max); +} + +static struct hwrng tpm_rng = { + .name = MODULE_NAME, + .read = tpm_rng_read, +}; + +static int __init rng_init(void) +{ + return hwrng_register(&tpm_rng); +} +module_init(rng_init); + +static void __exit rng_exit(void) +{ + hwrng_unregister(&tpm_rng); +} +module_exit(rng_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Kent Yoder <key@linux.vnet.ibm.com>"); +MODULE_DESCRIPTION("RNG driver for TPM devices"); diff --git a/drivers/char/hw_random/tx4939-rng.c b/drivers/char/hw_random/tx4939-rng.c index 0bc0cb70210..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,18 +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; - if (!devm_request_mem_region(&dev->dev, r->start, resource_size(r), - dev_name(&dev->dev))) - return -EBUSY; - rngdev->base = devm_ioremap(&dev->dev, r->start, resource_size(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; @@ -156,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; } @@ -168,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 723725bbb96..e9b15bc18b4 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -25,91 +25,165 @@ #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; + + /* + * 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 (!busy) { - busy = true; - init_completion(&have_data); - register_buffer(buf, size); + if (!vi->busy) { + vi->busy = true; + init_completion(&vi->have_data); + register_buffer(vi, buf, size); } if (!wait) return 0; - wait_for_completion(&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; + + if (vi->busy) + wait_for_completion(&vi->have_data); } +static int probe_common(struct virtio_device *vdev) +{ + int err, index; + struct virtrng_info *vi = NULL; -static struct hwrng virtio_hwrng = { - .name = "virtio", - .cleanup = virtio_cleanup, - .read = virtio_read, -}; + vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL); + if (!vi) + return -ENOMEM; -static int virtrng_probe(struct virtio_device *vdev) -{ - int err; + 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 __devexit virtrng_remove(struct virtio_device *vdev) +static void remove_common(struct virtio_device *vdev) { + struct virtrng_info *vi = vdev->priv; vdev->config->reset(vdev); - 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) +{ + return probe_common(vdev); } +static void virtrng_remove(struct virtio_device *vdev) +{ + remove_common(vdev); +} + +#ifdef CONFIG_PM_SLEEP +static int virtrng_freeze(struct virtio_device *vdev) +{ + remove_common(vdev); + return 0; +} + +static int virtrng_restore(struct virtio_device *vdev) +{ + return probe_common(vdev); +} +#endif + static struct virtio_device_id id_table[] = { { VIRTIO_ID_RNG, VIRTIO_DEV_ANY_ID }, { 0 }, @@ -120,21 +194,14 @@ static struct virtio_driver virtio_rng_driver = { .driver.owner = THIS_MODULE, .id_table = id_table, .probe = virtrng_probe, - .remove = __devexit_p(virtrng_remove), + .remove = virtrng_remove, +#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, ¤t->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(®s) ? : 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(®s)) < 0) + rc = i8k_smm(®s); + 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(®s)) < 0) + rc = i8k_smm(®s); + 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(®s) ? : (regs.eax & 0xffff) * fan_mult; + return i8k_smm(®s) ? : (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(®s)) < 0) + rc = i8k_smm(®s); + 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(®s)) < 0) + rc = i8k_smm(®s); + 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 2aa3977aae5..ec318bf434a 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -34,7 +34,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/errno.h> -#include <asm/system.h> #include <linux/poll.h> #include <linux/sched.h> #include <linux/spinlock.h> @@ -811,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; @@ -838,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 cf82fedae09..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 1000000 -#define OBF_RETRY_TIMEOUT 1000000 +#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 58c0e6387cf..e6db9381b2c 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -33,7 +33,6 @@ #include <linux/module.h> #include <linux/errno.h> -#include <asm/system.h> #include <linux/poll.h> #include <linux/sched.h> #include <linux/seq_file.h> @@ -46,6 +45,7 @@ #include <linux/init.h> #include <linux/proc_fs.h> #include <linux/rcupdate.h> +#include <linux/interrupt.h> #define PFX "IPMI message handler: " @@ -53,6 +53,9 @@ 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; @@ -71,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; @@ -90,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 { @@ -355,12 +372,15 @@ struct ipmi_smi { int curr_seq; /* - * Messages that were delayed for some reason (out of memory, - * for instance), will go in here to be processed later in a - * periodic timer interrupt. + * Messages queued for delivery. If delivery fails (out of memory + * for instance), They will stay in here to be processed later in a + * periodic timer interrupt. The tasklet is for handling received + * messages directly from the handler. */ spinlock_t waiting_msgs_lock; struct list_head waiting_msgs; + atomic_t watchdog_pretimeouts_to_deliver; + struct tasklet_struct recv_tasklet; /* * The list of command receivers that are registered for commands @@ -378,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 @@ -390,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... */ @@ -446,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) \ @@ -493,6 +515,8 @@ static void clean_up_interface_data(ipmi_smi_t intf) struct cmd_rcvr *rcvr, *rcvr2; struct list_head list; + tasklet_kill(&intf->recv_tasklet); + free_smi_msg_list(&intf->waiting_msgs); free_recv_msg_list(&intf->waiting_events); @@ -765,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; } @@ -934,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; @@ -955,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; @@ -1012,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); @@ -1148,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); } @@ -1177,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; @@ -1187,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 @@ -1282,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: @@ -1323,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); @@ -1528,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, @@ -1841,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) @@ -1873,7 +1921,7 @@ int ipmi_request_supply_msgs(ipmi_user_t user, struct ipmi_recv_msg *supplied_recv, int priority) { - unsigned char saddr, lun; + unsigned char saddr = 0, lun = 0; int rv; if (!user) @@ -1910,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 = { @@ -1931,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 = { @@ -2006,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 = { @@ -2030,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) { @@ -2786,12 +2833,17 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) return; } -void ipmi_poll_interface(ipmi_user_t user) +static void ipmi_poll(ipmi_smi_t intf) { - ipmi_smi_t intf = user->intf; - if (intf->handlers->poll) intf->handlers->poll(intf->send_info); + /* In case something came in */ + handle_new_recv_msgs(intf); +} + +void ipmi_poll_interface(ipmi_user_t user) +{ + ipmi_poll(user->intf); } EXPORT_SYMBOL(ipmi_poll_interface); @@ -2860,7 +2912,13 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, #endif spin_lock_init(&intf->waiting_msgs_lock); INIT_LIST_HEAD(&intf->waiting_msgs); + tasklet_init(&intf->recv_tasklet, + smi_recv_tasklet, + (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); @@ -3622,11 +3680,11 @@ static int handle_bmc_rsp(ipmi_smi_t intf, } /* - * Handle a new message. Return 1 if the message should be requeued, + * Handle a received message. Return 1 if the message should be requeued, * 0 if the message should be freed, or -1 if the message should not * be freed or requeued. */ -static int handle_new_recv_msg(ipmi_smi_t intf, +static int handle_one_recv_msg(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { int requeue; @@ -3773,7 +3831,7 @@ static int handle_new_recv_msg(ipmi_smi_t intf, } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) { - /* It's an asyncronous event. */ + /* It's an asynchronous event. */ requeue = handle_read_event_rsp(intf, msg); } else { /* It's a response from the local BMC. */ @@ -3784,12 +3842,72 @@ static int handle_new_recv_msg(ipmi_smi_t intf, return requeue; } +/* + * If there are messages in the queue or pretimeouts, handle them. + */ +static void handle_new_recv_msgs(ipmi_smi_t intf) +{ + struct ipmi_smi_msg *smi_msg; + unsigned long flags = 0; + int rv; + int run_to_completion = intf->run_to_completion; + + /* See if any waiting messages need to be processed. */ + if (!run_to_completion) + spin_lock_irqsave(&intf->waiting_msgs_lock, flags); + while (!list_empty(&intf->waiting_msgs)) { + smi_msg = list_entry(intf->waiting_msgs.next, + struct ipmi_smi_msg, link); + list_del(&smi_msg->link); + if (!run_to_completion) + spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); + rv = handle_one_recv_msg(intf, smi_msg); + if (!run_to_completion) + spin_lock_irqsave(&intf->waiting_msgs_lock, flags); + if (rv == 0) { + /* Message handled */ + ipmi_free_smi_msg(smi_msg); + } else if (rv < 0) { + /* Fatal error on the message, del but don't free. */ + } else { + /* + * To preserve message order, quit if we + * can't handle a message. + */ + list_add(&smi_msg->link, &intf->waiting_msgs); + break; + } + } + if (!run_to_completion) + spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); + + /* + * If the pretimout count is non-zero, decrement one from it and + * deliver pretimeouts to all the users. + */ + if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) { + ipmi_user_t user; + + rcu_read_lock(); + list_for_each_entry_rcu(user, &intf->users, link) { + if (user->handler->ipmi_watchdog_pretimeout) + user->handler->ipmi_watchdog_pretimeout( + user->handler_data); + } + rcu_read_unlock(); + } +} + +static void smi_recv_tasklet(unsigned long val) +{ + handle_new_recv_msgs((ipmi_smi_t) val); +} + /* Handle a new message from the lower layer. */ void ipmi_smi_msg_received(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { unsigned long flags = 0; /* keep us warning-free. */ - int rv; int run_to_completion; @@ -3843,31 +3961,11 @@ void ipmi_smi_msg_received(ipmi_smi_t intf, run_to_completion = intf->run_to_completion; if (!run_to_completion) spin_lock_irqsave(&intf->waiting_msgs_lock, flags); - if (!list_empty(&intf->waiting_msgs)) { - list_add_tail(&msg->link, &intf->waiting_msgs); - if (!run_to_completion) - spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); - goto out; - } + list_add_tail(&msg->link, &intf->waiting_msgs); if (!run_to_completion) spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); - rv = handle_new_recv_msg(intf, msg); - if (rv > 0) { - /* - * Could not handle the message now, just add it to a - * list to handle later. - */ - run_to_completion = intf->run_to_completion; - if (!run_to_completion) - spin_lock_irqsave(&intf->waiting_msgs_lock, flags); - list_add_tail(&msg->link, &intf->waiting_msgs); - if (!run_to_completion) - spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); - } else if (rv == 0) { - ipmi_free_smi_msg(msg); - } - + tasklet_schedule(&intf->recv_tasklet); out: return; } @@ -3875,16 +3973,8 @@ EXPORT_SYMBOL(ipmi_smi_msg_received); void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf) { - ipmi_user_t user; - - rcu_read_lock(); - list_for_each_entry_rcu(user, &intf->users, link) { - if (!user->handler->ipmi_watchdog_pretimeout) - continue; - - user->handler->ipmi_watchdog_pretimeout(user->handler_data); - } - rcu_read_unlock(); + atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1); + tasklet_schedule(&intf->recv_tasklet); } EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout); @@ -3918,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; @@ -3930,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. */ @@ -3948,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. @@ -3993,133 +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; - struct ipmi_smi_msg *smi_msg, *smi_msg2; unsigned long flags; int i; + unsigned int waiting_msgs = 0; - rcu_read_lock(); - list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { - /* See if any waiting messages need to be processed. */ - spin_lock_irqsave(&intf->waiting_msgs_lock, flags); - list_for_each_entry_safe(smi_msg, smi_msg2, - &intf->waiting_msgs, link) { - if (!handle_new_recv_msg(intf, smi_msg)) { - list_del(&smi_msg->link); - ipmi_free_smi_msg(smi_msg); - } else { - /* - * To preserve message order, quit if we - * can't handle a message. - */ - break; - } - } - spin_unlock_irqrestore(&intf->waiting_msgs_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); - 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; - ipmi_timeout_handler(IPMI_TIMEOUT_TIME); + 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++; + } - mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); + lnt += ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME); + + 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); @@ -4173,12 +4253,48 @@ EXPORT_SYMBOL(ipmi_free_recv_msg); #ifdef CONFIG_IPMI_PANIC_EVENT +static atomic_t panic_done_count = ATOMIC_INIT(0); + static void dummy_smi_done_handler(struct ipmi_smi_msg *msg) { + atomic_dec(&panic_done_count); } static void dummy_recv_done_handler(struct ipmi_recv_msg *msg) { + atomic_dec(&panic_done_count); +} + +/* + * Inside a panic, send a message and wait for a response. + */ +static void ipmi_panic_request_and_wait(ipmi_smi_t intf, + struct ipmi_addr *addr, + struct kernel_ipmi_msg *msg) +{ + struct ipmi_smi_msg smi_msg; + struct ipmi_recv_msg recv_msg; + int rv; + + smi_msg.done = dummy_smi_done_handler; + recv_msg.done = dummy_recv_done_handler; + atomic_add(2, &panic_done_count); + rv = i_ipmi_request(NULL, + intf, + addr, + 0, + msg, + intf, + &smi_msg, + &recv_msg, + 0, + intf->channels[0].address, + intf->channels[0].lun, + 0, 1); /* Don't retry, and don't wait. */ + if (rv) + atomic_sub(2, &panic_done_count); + while (atomic_read(&panic_done_count) != 0) + ipmi_poll(intf); } #ifdef CONFIG_IPMI_PANIC_STRING @@ -4217,8 +4333,6 @@ static void send_panic_events(char *str) unsigned char data[16]; struct ipmi_system_interface_addr *si; struct ipmi_addr addr; - struct ipmi_smi_msg smi_msg; - struct ipmi_recv_msg recv_msg; si = (struct ipmi_system_interface_addr *) &addr; si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; @@ -4246,9 +4360,6 @@ static void send_panic_events(char *str) data[7] = str[2]; } - smi_msg.done = dummy_smi_done_handler; - recv_msg.done = dummy_recv_done_handler; - /* For every registered interface, send the event. */ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { if (!intf->handlers) @@ -4258,18 +4369,7 @@ static void send_panic_events(char *str) intf->run_to_completion = 1; /* Send the event announcing the panic. */ intf->handlers->set_run_to_completion(intf->send_info, 1); - i_ipmi_request(NULL, - intf, - &addr, - 0, - &msg, - intf, - &smi_msg, - &recv_msg, - 0, - intf->channels[0].address, - intf->channels[0].lun, - 0, 1); /* Don't retry, and don't wait. */ + ipmi_panic_request_and_wait(intf, &addr, &msg); } #ifdef CONFIG_IPMI_PANIC_STRING @@ -4317,18 +4417,7 @@ static void send_panic_events(char *str) msg.data = NULL; msg.data_len = 0; intf->null_user_handler = device_id_fetcher; - i_ipmi_request(NULL, - intf, - &addr, - 0, - &msg, - intf, - &smi_msg, - &recv_msg, - 0, - intf->channels[0].address, - intf->channels[0].lun, - 0, 1); /* Don't retry, and don't wait. */ + ipmi_panic_request_and_wait(intf, &addr, &msg); if (intf->local_event_generator) { /* Request the event receiver from the local MC. */ @@ -4337,18 +4426,7 @@ static void send_panic_events(char *str) msg.data = NULL; msg.data_len = 0; intf->null_user_handler = event_receiver_fetcher; - i_ipmi_request(NULL, - intf, - &addr, - 0, - &msg, - intf, - &smi_msg, - &recv_msg, - 0, - intf->channels[0].address, - intf->channels[0].lun, - 0, 1); /* no retry, and no wait. */ + ipmi_panic_request_and_wait(intf, &addr, &msg); } intf->null_user_handler = NULL; @@ -4405,18 +4483,7 @@ static void send_panic_events(char *str) strncpy(data+5, p, 11); p += size; - i_ipmi_request(NULL, - intf, - &addr, - 0, - &msg, - intf, - &smi_msg, - &recv_msg, - 0, - intf->channels[0].address, - intf->channels[0].lun, - 0, 1); /* no retry, and no wait. */ + ipmi_panic_request_and_wait(intf, &addr, &msg); } } #endif /* CONFIG_IPMI_PANIC_STRING */ @@ -4522,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 50fcf9c0456..5d665680ae3 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -41,7 +41,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <asm/system.h> #include <linux/sched.h> #include <linux/seq_file.h> #include <linux/timer.h> @@ -62,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> @@ -72,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. */ @@ -156,7 +159,7 @@ enum si_stat_indexes { /* Number of watchdog pretimeouts. */ SI_STAT_watchdog_pretimeouts, - /* Number of asyncronous messages received. */ + /* Number of asynchronous messages received. */ SI_STAT_incoming_messages, @@ -171,7 +174,6 @@ struct smi_info { struct si_sm_handlers *handlers; enum si_type si_type; spinlock_t si_lock; - spinlock_t msg_lock; struct list_head xmit_msgs; struct list_head hp_xmit_msgs; struct ipmi_smi_msg *curr_msg; @@ -215,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 @@ -228,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; @@ -246,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; @@ -271,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; @@ -295,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); @@ -320,16 +331,8 @@ static int register_xaction_notifier(struct notifier_block *nb) static void deliver_recv_msg(struct smi_info *smi_info, struct ipmi_smi_msg *msg) { - /* Deliver the message to the upper layer with the lock - released. */ - - if (smi_info->run_to_completion) { - ipmi_smi_msg_received(smi_info->intf, msg); - } else { - spin_unlock(&(smi_info->si_lock)); - ipmi_smi_msg_received(smi_info->intf, msg); - spin_lock(&(smi_info->si_lock)); - } + /* Deliver the message to the upper layer. */ + ipmi_smi_msg_received(smi_info->intf, msg); } static void return_hosed_msg(struct smi_info *smi_info, int cCode) @@ -358,13 +361,6 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info) struct timeval t; #endif - /* - * No need to save flags, we aleady have interrupts off and we - * already hold the SMI lock. - */ - if (!smi_info->run_to_completion) - spin_lock(&(smi_info->msg_lock)); - /* Pick the high priority queue first. */ if (!list_empty(&(smi_info->hp_xmit_msgs))) { entry = smi_info->hp_xmit_msgs.next; @@ -402,9 +398,6 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info) rv = SI_SM_CALL_WITHOUT_DELAY; } out: - if (!smi_info->run_to_completion) - spin_unlock(&(smi_info->msg_lock)); - return rv; } @@ -447,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 @@ -457,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); } } @@ -468,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; } } @@ -481,9 +480,7 @@ static void handle_flags(struct smi_info *smi_info) start_clear_flags(smi_info); smi_info->msg_flags &= ~WDT_PRE_TIMEOUT_INT; - spin_unlock(&(smi_info->si_lock)); ipmi_smi_watchdog_pretimeout(smi_info->intf); - spin_lock(&(smi_info->si_lock)); } else if (smi_info->msg_flags & RECEIVE_MSG_AVAIL) { /* Messages available. */ smi_info->curr_msg = ipmi_alloc_smi_msg(); @@ -685,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); @@ -707,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; } @@ -864,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) @@ -889,19 +903,6 @@ static void sender(void *send_info, printk("**Enqueue: %d.%9.9d\n", t.tv_sec, t.tv_usec); #endif - /* - * 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); - if (smi_info->run_to_completion) { /* * If we are running to completion, then throw it in @@ -924,20 +925,17 @@ static void sender(void *send_info, return; } - spin_lock_irqsave(&smi_info->msg_lock, flags); + spin_lock_irqsave(&smi_info->si_lock, flags); if (priority > 0) list_add_tail(&msg->link, &smi_info->hp_xmit_msgs); else list_add_tail(&msg->link, &smi_info->xmit_msgs); - spin_unlock_irqrestore(&smi_info->msg_lock, flags); - spin_lock_irqsave(&smi_info->si_lock, flags); - if (smi_info->si_state == SI_NORMAL && smi_info->curr_msg == NULL) - start_next_msg(smi_info); + 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; @@ -1009,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); @@ -1022,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; @@ -1034,16 +1049,19 @@ static int ipmi_thread(void *data) static void poll(void *send_info) { struct smi_info *smi_info = send_info; - unsigned long flags; + unsigned long flags = 0; + bool run_to_completion = smi_info->run_to_completion; /* * Make sure there is some delay in the poll loop so we can * drive time forward and timeout things. */ udelay(10); - spin_lock_irqsave(&smi_info->si_lock, flags); + if (!run_to_completion) + spin_lock_irqsave(&smi_info->si_lock, flags); smi_event_handler(smi_info, 10); - spin_unlock_irqrestore(&smi_info->si_lock, flags); + if (!run_to_completion) + spin_unlock_irqrestore(&smi_info->si_lock, flags); } static void request_events(void *send_info) @@ -1057,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) @@ -1081,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; @@ -1106,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) @@ -1154,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. @@ -1196,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; @@ -1210,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, @@ -1227,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]; @@ -1257,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" @@ -1307,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."); @@ -1336,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) @@ -1346,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) { @@ -1680,10 +1737,8 @@ static struct smi_info *smi_info_alloc(void) { struct smi_info *info = kzalloc(sizeof(*info), GFP_KERNEL); - if (info) { + if (info) spin_lock_init(&info->si_lock); - spin_lock_init(&info->msg_lock); - } return info; } @@ -1829,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 */ @@ -1857,7 +1916,7 @@ static int hotmod_handler(const char *val, struct kernel_param *kp) return rv; } -static int __devinit hardcode_find_bmc(void) +static int hardcode_find_bmc(void) { int ret = -ENODEV; int i; @@ -2044,9 +2103,10 @@ struct SPMITable { s8 spmi_id[1]; /* A '\0' terminated array starts here. */ }; -static int __devinit try_init_spmi(struct SPMITable *spmi) +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); @@ -2121,13 +2181,14 @@ static int __devinit 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 __devinit spmi_find_bmc(void) +static void spmi_find_bmc(void) { acpi_status status; struct SPMITable *spmi; @@ -2149,7 +2210,7 @@ static void __devinit spmi_find_bmc(void) } } -static int __devinit ipmi_pnp_probe(struct pnp_dev *dev, +static int ipmi_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { struct acpi_device *acpi_dev; @@ -2158,6 +2219,7 @@ static int __devinit 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) @@ -2239,17 +2301,18 @@ static int __devinit 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); return -EINVAL; } -static void __devexit ipmi_pnp_remove(struct pnp_dev *dev) +static void ipmi_pnp_remove(struct pnp_dev *dev) { struct smi_info *info = pnp_get_drvdata(dev); @@ -2264,9 +2327,11 @@ static const struct pnp_device_id pnp_dev_table[] = { static struct pnp_driver ipmi_pnp_driver = { .name = DEVICE_NAME, .probe = ipmi_pnp_probe, - .remove = __devexit_p(ipmi_pnp_remove), + .remove = ipmi_pnp_remove, .id_table = pnp_dev_table, }; + +MODULE_DEVICE_TABLE(pnp, pnp_dev_table); #endif #ifdef CONFIG_DMI @@ -2279,7 +2344,7 @@ struct dmi_ipmi_data { u8 slave_addr; }; -static int __devinit decode_dmi(const struct dmi_header *dm, +static int decode_dmi(const struct dmi_header *dm, struct dmi_ipmi_data *dmi) { const u8 *data = (const u8 *)dm; @@ -2341,7 +2406,7 @@ static int __devinit decode_dmi(const struct dmi_header *dm, return 0; } -static void __devinit try_init_dmi(struct dmi_ipmi_data *ipmi_data) +static void try_init_dmi(struct dmi_ipmi_data *ipmi_data) { struct smi_info *info; @@ -2409,7 +2474,7 @@ static void __devinit try_init_dmi(struct dmi_ipmi_data *ipmi_data) kfree(info); } -static void __devinit dmi_find_bmc(void) +static void dmi_find_bmc(void) { const struct dmi_device *dev = NULL; struct dmi_ipmi_data data; @@ -2445,7 +2510,39 @@ static void ipmi_pci_cleanup(struct smi_info *info) pci_disable_device(pdev); } -static int __devinit ipmi_pci_probe(struct pci_dev *pdev, +static int ipmi_pci_probe_regspacing(struct smi_info *info) +{ + if (info->si_type == SI_KCS) { + unsigned char status; + int regspacing; + + info->io.regsize = DEFAULT_REGSIZE; + info->io.regshift = 0; + info->io_size = 2; + info->handlers = &kcs_smi_handlers; + + /* detect 1, 4, 16byte spacing */ + for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) { + info->io.regspacing = regspacing; + if (info->io_setup(info)) { + dev_err(info->dev, + "Could not setup I/O space\n"); + return DEFAULT_REGSPACING; + } + /* write invalid cmd */ + info->io.outputb(&info->io, 1, 0x10); + /* read status back */ + status = info->io.inputb(&info->io, 1); + info->io_cleanup(info); + if (status) + return regspacing; + regspacing *= 4; + } + } + return DEFAULT_REGSPACING; +} + +static int ipmi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int rv; @@ -2497,8 +2594,8 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, } info->io.addr_data = pci_resource_start(pdev, 0); - info->io.regspacing = DEFAULT_REGSPACING; - info->io.regsize = DEFAULT_REGSPACING; + info->io.regspacing = ipmi_pci_probe_regspacing(info); + info->io.regsize = DEFAULT_REGSIZE; info->io.regshift = 0; info->irq = pdev->irq; @@ -2512,30 +2609,22 @@ static int __devinit 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 __devexit ipmi_pci_remove(struct pci_dev *pdev) +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); } -#ifdef CONFIG_PM -static int ipmi_pci_suspend(struct pci_dev *pdev, pm_message_t state) -{ - return 0; -} - -static int ipmi_pci_resume(struct pci_dev *pdev) -{ - return 0; -} -#endif - static struct pci_device_id ipmi_pci_devices[] = { { PCI_DEVICE(PCI_HP_VENDOR_ID, PCI_MMC_DEVICE_ID) }, { PCI_DEVICE_CLASS(PCI_ERMC_CLASSCODE, PCI_ERMC_CLASSCODE_MASK) }, @@ -2547,16 +2636,12 @@ static struct pci_driver ipmi_pci_driver = { .name = DEVICE_NAME, .id_table = ipmi_pci_devices, .probe = ipmi_pci_probe, - .remove = __devexit_p(ipmi_pci_remove), -#ifdef CONFIG_PM - .suspend = ipmi_pci_suspend, - .resume = ipmi_pci_resume, -#endif + .remove = ipmi_pci_remove, }; #endif /* CONFIG_PCI */ static struct of_device_id ipmi_match[]; -static int __devinit ipmi_probe(struct platform_device *dev) +static int ipmi_probe(struct platform_device *dev) { #ifdef CONFIG_OF const struct of_device_id *match; @@ -2632,15 +2717,16 @@ static int __devinit 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; } -static int __devexit ipmi_remove(struct platform_device *dev) +static int ipmi_remove(struct platform_device *dev) { #ifdef CONFIG_OF cleanup_one_si(dev_get_drvdata(&dev->dev)); @@ -2666,9 +2752,67 @@ static struct platform_driver ipmi_driver = { .of_match_table = ipmi_match, }, .probe = ipmi_probe, - .remove = __devexit_p(ipmi_remove), + .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; @@ -2679,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); @@ -2815,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 = { @@ -2858,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 = { @@ -2886,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 = { @@ -3052,7 +3196,7 @@ static inline void wait_for_timer_and_thread(struct smi_info *smi_info) } } -static __devinitdata struct ipmi_default_vals +static struct ipmi_default_vals { int type; int port; @@ -3064,7 +3208,7 @@ static __devinitdata struct ipmi_default_vals { .port = 0 } }; -static void __devinit default_find_bmc(void) +static void default_find_bmc(void) { struct smi_info *info; int i; @@ -3228,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 @@ -3273,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, @@ -3322,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); @@ -3358,13 +3503,13 @@ 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; } -static int __devinit init_ipmi_si(void) +static int init_ipmi_si(void) { int i; char *str; @@ -3376,13 +3521,15 @@ static int __devinit 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') { @@ -3405,24 +3552,38 @@ static int __devinit 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 @@ -3571,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/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 34767a6d7f4..37b8be7cba9 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -141,19 +141,8 @@ #define IPMI_WDOG_TIMER_NOT_INIT_RESP 0x80 -/* These are here until the real ones get into the watchdog.h interface. */ -#ifndef WDIOC_GETTIMEOUT -#define WDIOC_GETTIMEOUT _IOW(WATCHDOG_IOCTL_BASE, 20, int) -#endif -#ifndef WDIOC_SET_PRETIMEOUT -#define WDIOC_SET_PRETIMEOUT _IOW(WATCHDOG_IOCTL_BASE, 21, int) -#endif -#ifndef WDIOC_GET_PRETIMEOUT -#define WDIOC_GET_PRETIMEOUT _IOW(WATCHDOG_IOCTL_BASE, 22, int) -#endif - static DEFINE_MUTEX(ipmi_watchdog_mutex); -static int nowayout = WATCHDOG_NOWAYOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; static ipmi_user_t watchdog_user; static int watchdog_ifnum; @@ -320,7 +309,7 @@ module_param(start_now, int, 0444); MODULE_PARM_DESC(start_now, "Set to 1 to start the watchdog as" "soon as the driver is loaded."); -module_param(nowayout, int, 0644); +module_param(nowayout, bool, 0644); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=CONFIG_WATCHDOG_NOWAYOUT)"); @@ -520,6 +509,7 @@ static void panic_halt_ipmi_heartbeat(void) msg.cmd = IPMI_WDOG_RESET_TIMER; msg.data = NULL; msg.data_len = 0; + atomic_add(2, &panic_done_count); rv = ipmi_request_supply_msgs(watchdog_user, (struct ipmi_addr *) &addr, 0, @@ -528,8 +518,8 @@ static void panic_halt_ipmi_heartbeat(void) &panic_halt_heartbeat_smi_msg, &panic_halt_heartbeat_recv_msg, 1); - if (!rv) - atomic_add(2, &panic_done_count); + if (rv) + atomic_sub(2, &panic_done_count); } static struct ipmi_smi_msg panic_halt_smi_msg = { @@ -553,16 +543,18 @@ static void panic_halt_ipmi_set_timeout(void) /* Wait for the messages to be free. */ while (atomic_read(&panic_done_count) != 0) ipmi_poll_interface(watchdog_user); + atomic_add(2, &panic_done_count); rv = i_ipmi_set_timeout(&panic_halt_smi_msg, &panic_halt_recv_msg, &send_heartbeat_now); - if (!rv) { - atomic_add(2, &panic_done_count); - if (send_heartbeat_now) - panic_halt_ipmi_heartbeat(); - } else + if (rv) { + atomic_sub(2, &panic_done_count); printk(KERN_WARNING PFX "Unable to extend the watchdog timeout."); + } else { + if (send_heartbeat_now) + panic_halt_ipmi_heartbeat(); + } while (atomic_read(&panic_done_count) != 0) ipmi_poll_interface(watchdog_user); } @@ -729,7 +721,6 @@ static int ipmi_ioctl(struct file *file, return -EFAULT; return 0; - case WDIOC_SET_PRETIMEOUT: case WDIOC_SETPRETIMEOUT: i = copy_from_user(&val, argp, sizeof(int)); if (i) @@ -737,7 +728,6 @@ static int ipmi_ioctl(struct file *file, pretimeout = val; return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); - case WDIOC_GET_PRETIMEOUT: case WDIOC_GETPRETIMEOUT: i = copy_to_user(argp, &pretimeout, sizeof(pretimeout)); if (i) @@ -1164,7 +1154,7 @@ static int wdog_reboot_handler(struct notifier_block *this, if (code == SYS_POWER_OFF || code == SYS_HALT) { /* Disable the WDT if we are shutting down. */ ipmi_watchdog_state = WDOG_TIMEOUT_NONE; - panic_halt_ipmi_set_timeout(); + ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); } else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) { /* Set a long timer to let the reboot happens, but reboot if it hangs, but only if the watchdog @@ -1172,7 +1162,7 @@ static int wdog_reboot_handler(struct notifier_block *this, timeout = 120; pretimeout = 0; ipmi_watchdog_state = WDOG_TIMEOUT_RESET; - panic_halt_ipmi_set_timeout(); + ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); } } return NOTIFY_OK; diff --git a/drivers/char/lp.c b/drivers/char/lp.c index f4348560706..c4094c4e22c 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -135,7 +135,6 @@ #include <asm/irq.h> #include <asm/uaccess.h> -#include <asm/system.h> /* if you have more than 8 printers, remember to increase LP_NO */ #define LP_NO 8 @@ -295,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; @@ -414,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; @@ -588,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: @@ -623,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; @@ -680,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: @@ -706,16 +710,13 @@ static long lp_compat_ioctl(struct file *file, unsigned int cmd, { unsigned int minor; struct timeval par_timeout; - struct compat_timeval __user *tc; int ret; - minor = iminor(file->f_path.dentry->d_inode); + minor = iminor(file_inode(file)); mutex_lock(&lp_mutex); switch (cmd) { case LPSETTIMEOUT: - tc = compat_ptr(arg); - if (get_user(par_timeout.tv_sec, &tc->tv_sec) || - get_user(par_timeout.tv_usec, &tc->tv_usec)) { + if (compat_get_timeval(&par_timeout, compat_ptr(arg))) { ret = -EFAULT; break; } diff --git a/drivers/char/mbcs.c b/drivers/char/mbcs.c index 1aeaaba680d..e5d3e3f7a49 100644 --- a/drivers/char/mbcs.c +++ b/drivers/char/mbcs.c @@ -28,7 +28,6 @@ #include <linux/slab.h> #include <asm/io.h> #include <asm/uaccess.h> -#include <asm/system.h> #include <asm/pgtable.h> #include <asm/sn/addrs.h> #include <asm/sn/intr.h> @@ -508,7 +507,7 @@ static int mbcs_gscr_mmap(struct file *fp, struct vm_area_struct *vma) vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */ + /* Remap-pfn-range will mark the range VM_IO */ if (remap_pfn_range(vma, vma->vm_start, __pa(soft->gscr_addr) >> PAGE_SHIFT, @@ -800,7 +799,7 @@ static int mbcs_remove(struct cx_dev *dev) return 0; } -static const struct cx_device_id __devinitdata mbcs_id_table[] = { +static const struct cx_device_id mbcs_id_table[] = { { .part_num = MBCS_PART_NUM, .mfg_num = MBCS_MFG_NUM, diff --git a/drivers/char/mem.c b/drivers/char/mem.c index d6e9d081c8b..917403fe10d 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -21,20 +21,21 @@ #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> -#include <asm/io.h> #ifdef CONFIG_IA64 # include <linux/efi.h> #endif +#define DEVPORT_MINOR 4 + static inline unsigned long size_inside_page(unsigned long start, unsigned long size) { @@ -46,7 +47,7 @@ static inline unsigned long size_inside_page(unsigned long start, } #ifndef ARCH_HAS_VALID_PHYS_ADDR_RANGE -static inline int valid_phys_addr_range(unsigned long addr, size_t count) +static inline int valid_phys_addr_range(phys_addr_t addr, size_t count) { return addr + count <= __pa(high_memory); } @@ -94,10 +95,13 @@ void __weak unxlate_dev_mem_ptr(unsigned long phys, void *addr) static ssize_t read_mem(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - unsigned long p = *ppos; + phys_addr_t p = *ppos; ssize_t read, sz; char *ptr; + if (p != *ppos) + return 0; + if (!valid_phys_addr_range(p, count)) return -EFAULT; read = 0; @@ -151,11 +155,14 @@ static ssize_t read_mem(struct file *file, char __user *buf, static ssize_t write_mem(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - unsigned long p = *ppos; + phys_addr_t p = *ppos; ssize_t written, sz; unsigned long copied; void *ptr; + if (p != *ppos) + return -EFBIG; + if (!valid_phys_addr_range(p, count)) return -EFAULT; @@ -224,7 +231,7 @@ int __weak phys_mem_access_prot_allowed(struct file *file, * */ #ifdef pgprot_noncached -static int uncached_access(struct file *file, unsigned long addr) +static int uncached_access(struct file *file, phys_addr_t addr) { #if defined(CONFIG_IA64) /* @@ -256,7 +263,7 @@ static pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, unsigned long size, pgprot_t vma_prot) { #ifdef pgprot_noncached - unsigned long offset = pfn << PAGE_SHIFT; + phys_addr_t offset = pfn << PAGE_SHIFT; if (uncached_access(file, offset)) return pgprot_noncached(vma_prot); @@ -320,7 +327,7 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma) vma->vm_ops = &mmap_mem_ops; - /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */ + /* Remap-pfn-range will mark the range VM_IO */ if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, @@ -354,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. @@ -397,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; @@ -525,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) { @@ -593,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; @@ -625,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) { @@ -668,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 @@ -706,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; } @@ -723,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; } @@ -736,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, @@ -764,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, }; @@ -780,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, }; @@ -799,52 +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 ssize_t kmsg_writev(struct kiocb *iocb, const struct iovec *iv, - unsigned long count, loff_t pos) -{ - char *line, *p; - int i; - ssize_t ret = -EFAULT; - size_t len = iov_length(iv, count); - - line = kmalloc(len + 1, GFP_KERNEL); - if (line == NULL) - return -ENOMEM; - - /* - * copy all vectors into a single string, to ensure we do - * not interleave our log line with other printk calls - */ - p = line; - for (i = 0; i < count; i++) { - if (copy_from_user(p, iv[i].iov_base, iv[i].iov_len)) - goto out; - p += iv[i].iov_len; - } - p[0] = '\0'; - - ret = printk("%s", line); - /* printk can add a prefix */ - if (ret > len) - ret = len; -out: - kfree(line); - return ret; -} - -static const struct file_operations kmsg_fops = { - .aio_write = kmsg_writev, - .llseek = noop_llseek, -}; - static const struct memdev { const char *name; umode_t mode; @@ -863,9 +824,8 @@ static const struct memdev { [7] = { "full", 0666, &full_fops, NULL }, [8] = { "random", 0666, &random_fops, NULL }, [9] = { "urandom", 0666, &urandom_fops, NULL }, - [11] = { "kmsg", 0, &kmsg_fops, NULL }, -#ifdef CONFIG_CRASH_DUMP - [12] = { "oldmem", 0, &oldmem_fops, NULL }, +#ifdef CONFIG_PRINTK + [11] = { "kmsg", 0644, &kmsg_fops, NULL }, #endif }; @@ -930,6 +890,13 @@ static int __init chr_dev_init(void) for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) { if (!devlist[minor].name) continue; + + /* + * Create /dev/port? + */ + if ((minor == DEVPORT_MINOR) && !arch_has_dev_port()) + continue; + device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor), NULL, devlist[minor].name); } 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/mmtimer.c b/drivers/char/mmtimer.c index 33dc2298af7..3d6c0671e99 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c @@ -826,7 +826,7 @@ static int __init mmtimer_init(void) /* Allocate list of node ptrs to mmtimer_t's */ timers = kzalloc(sizeof(struct mmtimer_node)*maxn, GFP_KERNEL); - if (timers == NULL) { + if (!timers) { printk(KERN_ERR "%s: failed to allocate memory for device\n", MMTIMER_NAME); goto out3; @@ -848,7 +848,6 @@ static int __init mmtimer_init(void) return 0; out3: - kfree(timers); misc_deregister(&mmtimer_miscdev); out2: free_irq(SGI_MMTIMER_VECTOR, NULL); 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 5c0d96a820f..f1d7fa45c27 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c @@ -44,7 +44,6 @@ #include <linux/slab.h> #include <linux/numa.h> #include <asm/page.h> -#include <asm/system.h> #include <asm/pgtable.h> #include <linux/atomic.h> #include <asm/tlbflush.h> @@ -268,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); @@ -284,10 +283,10 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma, vdata->flags = flags; vdata->type = type; spin_lock_init(&vdata->lock); - vdata->refcnt = ATOMIC_INIT(1); + atomic_set(&vdata->refcnt, 1); vma->vm_private_data = vdata; - vma->vm_flags |= (VM_IO | VM_RESERVED | VM_PFNMAP | VM_DONTEXPAND); + vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP; if (vdata->type == MSPEC_FETCHOP || vdata->type == MSPEC_UNCACHED) vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); vma->vm_ops = &mspec_vm_ops; diff --git a/drivers/char/mwave/3780i.c b/drivers/char/mwave/3780i.c index 492dbfb2efd..28740046bc8 100644 --- a/drivers/char/mwave/3780i.c +++ b/drivers/char/mwave/3780i.c @@ -50,13 +50,11 @@ #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() */ #include <asm/io.h> #include <asm/uaccess.h> -#include <asm/system.h> #include <asm/irq.h> #include "smapi.h" #include "mwavedd.h" diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c index 1d82d5838f0..164544afd68 100644 --- a/drivers/char/mwave/mwavedd.c +++ b/drivers/char/mwave/mwavedd.c @@ -430,7 +430,7 @@ static ssize_t mwave_write(struct file *file, const char __user *buf, static int register_serial_portandirq(unsigned int port, int irq) { - struct uart_port uart; + struct uart_8250_port uart; switch ( port ) { case 0x3f8: @@ -462,14 +462,14 @@ static int register_serial_portandirq(unsigned int port, int irq) } /* switch */ /* irq is okay */ - memset(&uart, 0, sizeof(struct uart_port)); + memset(&uart, 0, sizeof(uart)); - uart.uartclk = 1843200; - uart.iobase = port; - uart.irq = irq; - uart.iotype = UPIO_PORT; - uart.flags = UPF_SHARE_IRQ; - return serial8250_register_port(&uart); + uart.port.uartclk = 1843200; + uart.port.iobase = port; + uart.port.irq = irq; + uart.port.iotype = UPIO_PORT; + uart.port.flags = UPF_SHARE_IRQ; + return serial8250_register_8250_port(&uart); } 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/nvram.c b/drivers/char/nvram.c index da3cfee782d..9df78e2cc45 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -94,7 +94,7 @@ /* Note that *all* calls to CMOS_READ and CMOS_WRITE must be done with * rtc_lock held. Due to the index-port/data-port design of the RTC, we * don't want two different things trying to get to it at once. (e.g. the - * periodic 11 min sync from time.c vs. this driver.) + * periodic 11 min sync from kernel/time/ntp.c vs. this driver.) */ #include <linux/types.h> @@ -111,7 +111,6 @@ #include <linux/uaccess.h> #include <linux/mutex.h> -#include <asm/system.h> static DEFINE_MUTEX(nvram_mutex); static DEFINE_SPINLOCK(nvram_state_lock); diff --git a/drivers/char/nwbutton.c b/drivers/char/nwbutton.c index 04a480f86c6..76c490fa051 100644 --- a/drivers/char/nwbutton.c +++ b/drivers/char/nwbutton.c @@ -93,9 +93,9 @@ int button_del_callback (void (*callback) (void)) button_callback_list [lp].count = 0; callback_count--; return 0; - }; + } lp--; - }; + } return -EINVAL; } @@ -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/nwflash.c b/drivers/char/nwflash.c index bf586ae1ee8..e371480d363 100644 --- a/drivers/char/nwflash.c +++ b/drivers/char/nwflash.c @@ -30,9 +30,7 @@ #include <asm/hardware/dec21285.h> #include <asm/io.h> -#include <asm/leds.h> #include <asm/mach-types.h> -#include <asm/system.h> #include <asm/uaccess.h> /*****************************************************************************/ @@ -180,9 +178,6 @@ static ssize_t flash_write(struct file *file, const char __user *buf, written = 0; - leds_event(led_claim); - leds_event(led_green_on); - nBlock = (int) p >> 16; //block # of 64K bytes /* @@ -259,11 +254,6 @@ static ssize_t flash_write(struct file *file, const char __user *buf, printk(KERN_DEBUG "flash_write: written 0x%X bytes OK.\n", written); } - /* - * restore reg on exit - */ - leds_event(led_release); - mutex_unlock(&nwflash_mutex); return written; @@ -335,11 +325,6 @@ static int erase_block(int nBlock) int temp, temp1; /* - * orange LED == erase - */ - leds_event(led_amber_on); - - /* * reset footbridge to the correct offset 0 (...0..3) */ *CSR_ROMWRITEREG = 0; @@ -447,12 +432,6 @@ static int write_block(unsigned long p, const char __user *buf, int count) unsigned long timeout; unsigned long timeout1; - /* - * red LED == write - */ - leds_event(led_amber_off); - leds_event(led_red_on); - pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p)); /* @@ -559,17 +538,9 @@ static int write_block(unsigned long p, const char __user *buf, int count) pWritePtr - FLASH_BASE); /* - * no LED == waiting - */ - leds_event(led_amber_off); - /* * wait couple ms */ msleep(10); - /* - * red LED == write - */ - leds_event(led_red_on); goto WriteRetry; } else { @@ -584,12 +555,6 @@ static int write_block(unsigned long p, const char __user *buf, int count) } } - /* - * green LED == read/verify - */ - leds_event(led_amber_off); - leds_event(led_green_on); - msleep(10); pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p)); @@ -618,9 +583,9 @@ static void kick_open(void) * we want to write a bit pattern XXX1 to Xilinx to enable * the write gate, which will be open for about the next 2ms. */ - spin_lock_irqsave(&nw_gpio_lock, flags); + raw_spin_lock_irqsave(&nw_gpio_lock, flags); nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE); - spin_unlock_irqrestore(&nw_gpio_lock, flags); + raw_spin_unlock_irqrestore(&nw_gpio_lock, flags); /* * let the ISA bus to catch on... diff --git a/drivers/char/pc8736x_gpio.c b/drivers/char/pc8736x_gpio.c index b304ec05250..3f79a9fb6b1 100644 --- a/drivers/char/pc8736x_gpio.c +++ b/drivers/char/pc8736x_gpio.c @@ -345,8 +345,7 @@ static void __exit pc8736x_gpio_cleanup(void) unregister_chrdev_region(MKDEV(major,0), PC8736X_GPIO_CT); release_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE); - platform_device_del(pdev); - platform_device_put(pdev); + platform_device_unregister(pdev); } module_init(pc8736x_gpio_init); 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 07f6a5abe37..8320abd1ef1 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -60,7 +60,6 @@ #include <linux/ioctl.h> #include <linux/synclink.h> -#include <asm/system.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/dma.h> @@ -103,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]; @@ -211,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; @@ -327,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 @@ -357,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); @@ -398,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); @@ -515,47 +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) - 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. @@ -568,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. @@ -702,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) @@ -718,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. @@ -734,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; @@ -753,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; } @@ -764,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; @@ -777,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) { @@ -801,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); } @@ -830,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; @@ -846,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; @@ -885,12 +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; + struct mgsl_icount *icount = &info->icount; if (tcd) { /* early termination, get FIFO count from RBCL register */ @@ -904,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); @@ -935,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); @@ -948,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); } @@ -981,7 +987,7 @@ static void tx_done(MGSLPC_INFO *info, struct tty_struct *tty) else #endif { - if (tty->stopped || tty->hw_stopped) { + if (tty && (tty->stopped || tty->hw_stopped)) { tx_stop(info); return; } @@ -995,13 +1001,13 @@ 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) return; } else { - if (tty->stopped || tty->hw_stopped) { + if (tty && (tty->stopped || tty->hw_stopped)) { tx_stop(info); return; } @@ -1051,13 +1057,12 @@ static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty) wake_up_interruptible(&info->status_event_wait_q); wake_up_interruptible(&info->event_wait_q); - if (info->port.flags & ASYNC_CTS_FLOW) { + if (tty && tty_port_cts_enabled(&info->port)) { if (tty->hw_stopped) { if (info->serial_signals & SerialSignal_CTS) { if (debug_level >= DEBUG_LEVEL_ISR) printk("CTS tx start..."); - if (tty) - tty->hw_stopped = 0; + tty->hw_stopped = 0; tx_start(info, tty); info->pending_bh |= BH_TRANSMIT; return; @@ -1066,8 +1071,7 @@ static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty) if (!(info->serial_signals & SerialSignal_CTS)) { if (debug_level >= DEBUG_LEVEL_ISR) printk("CTS tx stop..."); - if (tty) - tty->hw_stopped = 1; + tty->hw_stopped = 1; tx_stop(info); } } @@ -1178,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) { @@ -1206,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 */ @@ -1242,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); @@ -1266,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; @@ -1276,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; } } @@ -1291,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); @@ -1323,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 */ @@ -1337,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); @@ -1345,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); @@ -1364,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); @@ -1386,10 +1390,10 @@ static void mgslpc_program_hw(MGSLPC_INFO *info, struct tty_struct *tty) port_irq_enable(info, (unsigned char) PVR_DSR | PVR_RI); get_signals(info); - if (info->netcount || (tty && (tty->termios->c_cflag & CREAD))) + 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 @@ -1399,21 +1403,21 @@ static void mgslpc_change_params(MGSLPC_INFO *info, struct tty_struct *tty) unsigned cflag; int bits_per_char; - if (!tty || !tty->termios) + if (!tty) return; 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; + 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 */ @@ -1456,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; } @@ -1491,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")) @@ -1501,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) { @@ -1511,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; } @@ -1524,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; @@ -1535,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 @@ -1562,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) @@ -1589,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; } @@ -1636,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; } @@ -1649,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; @@ -1661,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; } @@ -1675,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); @@ -1698,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); } } @@ -1721,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; @@ -1729,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); } } @@ -1746,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; @@ -1758,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); } } @@ -1800,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; } @@ -1844,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; } @@ -1867,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; @@ -1883,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); @@ -1902,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. @@ -1922,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); @@ -1941,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; @@ -1971,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; } @@ -1997,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(;;) { @@ -2008,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 && @@ -2051,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) @@ -2064,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(); @@ -2084,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 && @@ -2118,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) + @@ -2133,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; } @@ -2143,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; @@ -2158,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; } @@ -2177,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; } @@ -2198,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; @@ -2221,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 */ @@ -2234,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; @@ -2281,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) { @@ -2290,12 +2294,12 @@ 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) - && (RELEVANT_IFLAG(tty->termios->c_iflag) + if ((tty->termios.c_cflag == old_termios->c_cflag) + && (RELEVANT_IFLAG(tty->termios.c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) return; @@ -2303,29 +2307,29 @@ 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); + !(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); } /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && - tty->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 */ if (old_termios->c_cflag & CRTSCTS && - !(tty->termios->c_cflag & CRTSCTS)) { + !(tty->termios.c_cflag & CRTSCTS)) { tty->hw_stopped = 0; tx_release(tty); } @@ -2341,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); @@ -2360,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); } @@ -2371,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; @@ -2392,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 @@ -2424,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. @@ -2436,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; @@ -2451,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; @@ -2465,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); } @@ -2479,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 < 0) || (line >= mgslpc_device_count)) { + if (line >= mgslpc_device_count) { printk("%s(%d):mgslpc_open with invalid line #%d.\n", - __FILE__,__LINE__,line); + __FILE__, __LINE__, line); return -ENODEV; } @@ -2503,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) { @@ -2538,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: @@ -2564,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; @@ -2628,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; } @@ -2667,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; } @@ -2675,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; } @@ -2699,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); @@ -2715,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; } @@ -2726,12 +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_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) @@ -2745,10 +2785,12 @@ static void mgslpc_remove_device(MGSLPC_INFO *remove_info) last->next_device = info->next_device; else mgslpc_device_list = info->next_device; + tty_unregister_device(serial_driver, info->line); #if SYNCLINK_GENERIC_HDLC hdlcdev_exit(info); #endif release_resources(info); + tty_port_destroy(&info->port); kfree(info); mgslpc_device_count--; return; @@ -2799,78 +2841,63 @@ static const struct tty_operations mgslpc_ops = { .proc_fops = &mgslpc_proc_fops, }; -static void synclink_cs_cleanup(void) +static int __init synclink_cs_init(void) { int rc; - while(mgslpc_device_list) - mgslpc_remove_device(mgslpc_device_list); + if (break_on_load) { + mgslpc_get_text_ptr(); + BREAKPOINT(); + } - if (serial_driver) { - if ((rc = tty_unregister_driver(serial_driver))) - printk("%s(%d) failed to unregister tty driver err=%d\n", - __FILE__,__LINE__,rc); - put_tty_driver(serial_driver); + serial_driver = tty_alloc_driver(MAX_DEVICE_COUNT, + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(serial_driver)) { + rc = PTR_ERR(serial_driver); + goto err; } - pcmcia_unregister_driver(&mgslpc_driver); -} + /* Initialize the tty_driver structure */ + serial_driver->driver_name = "synclink_cs"; + serial_driver->name = "ttySLP"; + serial_driver->major = ttymajor; + serial_driver->minor_start = 64; + serial_driver->type = TTY_DRIVER_TYPE_SERIAL; + serial_driver->subtype = SERIAL_TYPE_NORMAL; + serial_driver->init_termios = tty_std_termios; + serial_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + tty_set_operations(serial_driver, &mgslpc_ops); + + rc = tty_register_driver(serial_driver); + if (rc < 0) { + printk(KERN_ERR "%s(%d):Couldn't register serial driver\n", + __FILE__, __LINE__); + goto err_put_tty; + } -static int __init synclink_cs_init(void) -{ - int rc; - - if (break_on_load) { - mgslpc_get_text_ptr(); - BREAKPOINT(); - } - - if ((rc = pcmcia_register_driver(&mgslpc_driver)) < 0) - return rc; - - serial_driver = alloc_tty_driver(MAX_DEVICE_COUNT); - if (!serial_driver) { - rc = -ENOMEM; - goto error; - } - - /* Initialize the tty_driver structure */ - - serial_driver->owner = THIS_MODULE; - serial_driver->driver_name = "synclink_cs"; - serial_driver->name = "ttySLP"; - serial_driver->major = ttymajor; - serial_driver->minor_start = 64; - serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - serial_driver->subtype = SERIAL_TYPE_NORMAL; - serial_driver->init_termios = tty_std_termios; - serial_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - serial_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(serial_driver, &mgslpc_ops); - - if ((rc = tty_register_driver(serial_driver)) < 0) { - printk("%s(%d):Couldn't register serial driver\n", - __FILE__,__LINE__); - put_tty_driver(serial_driver); - serial_driver = NULL; - goto error; - } - - printk("%s %s, tty major#%d\n", - driver_name, driver_version, - serial_driver->major); - - return 0; - -error: - synclink_cs_cleanup(); - return rc; + rc = pcmcia_register_driver(&mgslpc_driver); + if (rc < 0) + goto err_unreg_tty; + + printk(KERN_INFO "%s %s, tty major#%d\n", driver_name, driver_version, + serial_driver->major); + + return 0; +err_unreg_tty: + tty_unregister_driver(serial_driver); +err_put_tty: + put_tty_driver(serial_driver); +err: + return rc; } static void __exit synclink_cs_exit(void) { - synclink_cs_cleanup(); + pcmcia_unregister_driver(&mgslpc_driver); + tty_unregister_driver(serial_driver); + put_tty_driver(serial_driver); } module_init(synclink_cs_init); @@ -3004,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 */ @@ -3098,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); @@ -3158,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) @@ -3170,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); @@ -3237,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 */ @@ -3266,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); @@ -3279,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; @@ -3295,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 */ @@ -3333,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); @@ -3555,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 */ @@ -3579,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; @@ -3594,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) @@ -3685,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); @@ -3713,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; } @@ -3733,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++) { @@ -3746,7 +3773,7 @@ static bool register_test(MGSLPC_INFO *info) } } - spin_unlock_irqrestore(&info->lock,flags); + spin_unlock_irqrestore(&info->lock, flags); return rc; } @@ -3755,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; @@ -3769,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) { @@ -3778,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; } @@ -3789,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; } @@ -3812,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) @@ -3823,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("."); } @@ -3847,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) @@ -3940,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); @@ -3961,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; } @@ -3988,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 */ @@ -4006,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); @@ -4048,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); @@ -4082,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) @@ -4183,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); } @@ -4221,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); @@ -4264,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; } @@ -4284,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 3fcf80ff12f..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; @@ -251,12 +251,8 @@ static ssize_t pp_write (struct file * file, const char __user * buf, break; } - if (signal_pending (current)) { - if (!bytes_written) { - bytes_written = -EINTR; - } + if (signal_pending (current)) break; - } cond_resched(); } @@ -328,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; @@ -783,7 +779,8 @@ static int __init ppdev_init (void) err = PTR_ERR(ppdev_class); goto out_chrdev; } - if (parport_register_driver(&pp_driver)) { + err = parport_register_driver(&pp_driver); + if (err < 0) { printk (KERN_WARNING CHRDEV ": unable to register with parport\n"); goto out_class; } diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c index 6abdde4da2b..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); @@ -363,7 +339,7 @@ static struct miscdevice ps3flash_misc = { .fops = &ps3flash_fops, }; -static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev) +static int ps3flash_probe(struct ps3_system_bus_device *_dev) { struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); struct ps3flash_private *priv; diff --git a/drivers/char/ramoops.c b/drivers/char/ramoops.c deleted file mode 100644 index 9fec3232b73..00000000000 --- a/drivers/char/ramoops.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * RAM Oops/Panic logger - * - * Copyright (C) 2010 Marco Stornelli <marco.stornelli@gmail.com> - * - * 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. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/kernel.h> -#include <linux/err.h> -#include <linux/module.h> -#include <linux/kmsg_dump.h> -#include <linux/time.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/ioport.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/ramoops.h> - -#define RAMOOPS_KERNMSG_HDR "====" -#define MIN_MEM_SIZE 4096UL - -static ulong record_size = MIN_MEM_SIZE; -module_param(record_size, ulong, 0400); -MODULE_PARM_DESC(record_size, - "size of each dump done on oops/panic"); - -static ulong mem_address; -module_param(mem_address, ulong, 0400); -MODULE_PARM_DESC(mem_address, - "start of reserved RAM used to store oops/panic logs"); - -static ulong mem_size; -module_param(mem_size, ulong, 0400); -MODULE_PARM_DESC(mem_size, - "size of reserved RAM used to store oops/panic logs"); - -static int dump_oops = 1; -module_param(dump_oops, int, 0600); -MODULE_PARM_DESC(dump_oops, - "set to 1 to dump oopses, 0 to only dump panics (default 1)"); - -static struct ramoops_context { - struct kmsg_dumper dump; - void *virt_addr; - phys_addr_t phys_addr; - unsigned long size; - unsigned long record_size; - int dump_oops; - int count; - int max_count; -} oops_cxt; - -static struct platform_device *dummy; -static struct ramoops_platform_data *dummy_data; - -static void ramoops_do_dump(struct kmsg_dumper *dumper, - enum kmsg_dump_reason reason, const char *s1, unsigned long l1, - const char *s2, unsigned long l2) -{ - struct ramoops_context *cxt = container_of(dumper, - struct ramoops_context, dump); - unsigned long s1_start, s2_start; - unsigned long l1_cpy, l2_cpy; - int res, hdr_size; - char *buf, *buf_orig; - struct timeval timestamp; - - if (reason != KMSG_DUMP_OOPS && - reason != KMSG_DUMP_PANIC) - return; - - /* Only dump oopses if dump_oops is set */ - if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops) - return; - - buf = cxt->virt_addr + (cxt->count * cxt->record_size); - buf_orig = buf; - - memset(buf, '\0', cxt->record_size); - res = sprintf(buf, "%s", RAMOOPS_KERNMSG_HDR); - buf += res; - do_gettimeofday(×tamp); - res = sprintf(buf, "%lu.%lu\n", (long)timestamp.tv_sec, (long)timestamp.tv_usec); - buf += res; - - hdr_size = buf - buf_orig; - l2_cpy = min(l2, cxt->record_size - hdr_size); - l1_cpy = min(l1, cxt->record_size - hdr_size - l2_cpy); - - s2_start = l2 - l2_cpy; - s1_start = l1 - l1_cpy; - - memcpy(buf, s1 + s1_start, l1_cpy); - memcpy(buf + l1_cpy, s2 + s2_start, l2_cpy); - - cxt->count = (cxt->count + 1) % cxt->max_count; -} - -static int __init ramoops_probe(struct platform_device *pdev) -{ - struct ramoops_platform_data *pdata = pdev->dev.platform_data; - struct ramoops_context *cxt = &oops_cxt; - int err = -EINVAL; - - if (!pdata->mem_size || !pdata->record_size) { - pr_err("The memory size and the record size must be " - "non-zero\n"); - goto fail3; - } - - pdata->mem_size = rounddown_pow_of_two(pdata->mem_size); - pdata->record_size = rounddown_pow_of_two(pdata->record_size); - - /* Check for the minimum memory size */ - if (pdata->mem_size < MIN_MEM_SIZE && - pdata->record_size < MIN_MEM_SIZE) { - pr_err("memory size too small, minium is %lu\n", MIN_MEM_SIZE); - goto fail3; - } - - if (pdata->mem_size < pdata->record_size) { - pr_err("The memory size must be larger than the " - "records size\n"); - goto fail3; - } - - cxt->max_count = pdata->mem_size / pdata->record_size; - cxt->count = 0; - cxt->size = pdata->mem_size; - cxt->phys_addr = pdata->mem_address; - cxt->record_size = pdata->record_size; - cxt->dump_oops = pdata->dump_oops; - - if (!request_mem_region(cxt->phys_addr, cxt->size, "ramoops")) { - pr_err("request mem region failed\n"); - err = -EINVAL; - goto fail3; - } - - cxt->virt_addr = ioremap(cxt->phys_addr, cxt->size); - if (!cxt->virt_addr) { - pr_err("ioremap failed\n"); - goto fail2; - } - - cxt->dump.dump = ramoops_do_dump; - err = kmsg_dump_register(&cxt->dump); - if (err) { - pr_err("registering kmsg dumper failed\n"); - goto fail1; - } - - /* - * Update the module parameter variables as well so they are visible - * through /sys/module/ramoops/parameters/ - */ - mem_size = pdata->mem_size; - mem_address = pdata->mem_address; - record_size = pdata->record_size; - dump_oops = pdata->dump_oops; - - return 0; - -fail1: - iounmap(cxt->virt_addr); -fail2: - release_mem_region(cxt->phys_addr, cxt->size); -fail3: - return err; -} - -static int __exit ramoops_remove(struct platform_device *pdev) -{ - struct ramoops_context *cxt = &oops_cxt; - - if (kmsg_dump_unregister(&cxt->dump) < 0) - pr_warn("could not unregister kmsg_dumper\n"); - - iounmap(cxt->virt_addr); - release_mem_region(cxt->phys_addr, cxt->size); - return 0; -} - -static struct platform_driver ramoops_driver = { - .remove = __exit_p(ramoops_remove), - .driver = { - .name = "ramoops", - .owner = THIS_MODULE, - }, -}; - -static int __init ramoops_init(void) -{ - int ret; - ret = platform_driver_probe(&ramoops_driver, ramoops_probe); - if (ret == -ENODEV) { - /* - * If we didn't find a platform device, we use module parameters - * building platform data on the fly. - */ - pr_info("platform device not found, using module parameters\n"); - dummy_data = kzalloc(sizeof(struct ramoops_platform_data), - GFP_KERNEL); - if (!dummy_data) - return -ENOMEM; - dummy_data->mem_size = mem_size; - dummy_data->mem_address = mem_address; - dummy_data->record_size = record_size; - dummy_data->dump_oops = dump_oops; - dummy = platform_create_bundle(&ramoops_driver, ramoops_probe, - NULL, 0, dummy_data, - sizeof(struct ramoops_platform_data)); - - if (IS_ERR(dummy)) - ret = PTR_ERR(dummy); - else - ret = 0; - } - - return ret; -} - -static void __exit ramoops_exit(void) -{ - platform_driver_unregister(&ramoops_driver); - kfree(dummy_data); -} - -module_init(ramoops_init); -module_exit(ramoops_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Marco Stornelli <marco.stornelli@gmail.com>"); -MODULE_DESCRIPTION("RAM Oops/Panic logger/driver"); diff --git a/drivers/char/random.c b/drivers/char/random.c index 54ca8b23cde..71529e196b8 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -125,21 +125,26 @@ * The current exported interfaces for gathering environmental noise * from the devices are: * + * void add_device_randomness(const void *buf, unsigned int size); * void add_input_randomness(unsigned int type, unsigned int code, * unsigned int value); - * void add_interrupt_randomness(int irq); + * void add_interrupt_randomness(int irq, int irq_flags); * void add_disk_randomness(struct gendisk *disk); * + * add_device_randomness() is for adding data to the random pool that + * is likely to differ between two devices (or possibly even per boot). + * This would be things like MAC addresses or serial numbers, or the + * read-out of the RTC. This does *not* add any actual entropy to the + * pool, but it initializes the pool to different values for devices + * that might otherwise be identical and have very little entropy + * available to them (particularly common in the embedded world). + * * add_input_randomness() uses the input layer interrupt timing, as well as * the event type information from the hardware. * - * add_interrupt_randomness() uses the inter-interrupt timing as random - * inputs to the entropy pool. Note that not all interrupts are good - * sources of randomness! For example, the timer interrupts is not a - * good choice, because the periodicity of the interrupts is too - * regular, and hence predictable to an attacker. Network Interface - * Controller interrupts are a better measure, since the timing of the - * NIC interrupts are more unpredictable. + * add_interrupt_randomness() uses the interrupt timing as random + * inputs to the entropy pool. Using the cycle counters and the irq source + * as inputs, it feeds the randomness roughly once a second. * * add_disk_randomness() uses what amounts to the seek time of block * layer request events, on a per-disk_devt basis, as input to the @@ -248,137 +253,149 @@ #include <linux/percpu.h> #include <linux/cryptohash.h> #include <linux/fips.h> - -#ifdef CONFIG_GENERIC_HARDIRQS -# include <linux/irq.h> -#endif +#include <linux/ptrace.h> +#include <linux/kmemcheck.h> +#include <linux/workqueue.h> +#include <linux/irq.h> #include <asm/processor.h> #include <asm/uaccess.h> #include <asm/irq.h> +#include <asm/irq_regs.h> #include <asm/io.h> +#define CREATE_TRACE_POINTS +#include <trace/events/random.h> + /* * 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 */ @@ -386,21 +403,6 @@ static DECLARE_WAIT_QUEUE_HEAD(random_read_wait); static DECLARE_WAIT_QUEUE_HEAD(random_write_wait); static struct fasync_struct *fasync; -#if 0 -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) -#else -#define DEBUG_ENT(fmt, arg...) do {} while (0) -#endif - /********************************************************************** * * OS independent entropy store. Here are the functions which handle @@ -411,20 +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 short add_ptr; + unsigned short input_rotate; int entropy_count; - int input_rotate; + int entropy_total; + unsigned int initialized:1; + 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]; @@ -433,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 }; @@ -442,18 +450,26 @@ 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] = { + 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158, + 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 }; + /* * This function adds bytes into the entropy "pool". It does not * update the entropy estimate. The caller should call @@ -464,33 +480,28 @@ static struct entropy_store nonblocking_pool = { * it's cheap to do so and helps slightly in the expected case where * the entropy is concentrated in the low-order bits. */ -static void mix_pool_bytes_extract(struct entropy_store *r, const void *in, - int nbytes, __u8 out[64]) +static void _mix_pool_bytes(struct entropy_store *r, const void *in, + int nbytes, __u8 out[64]) { - static __u32 const twist_table[8] = { - 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158, - 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 }; unsigned long i, j, tap1, tap2, tap3, tap4, tap5; int input_rotate; int wordmask = r->poolinfo->poolwords - 1; const char *bytes = in; __u32 w; - unsigned long flags; - /* Taps are constant, so we can load them without holding r->lock. */ tap1 = r->poolinfo->tap1; tap2 = r->poolinfo->tap2; tap3 = r->poolinfo->tap3; tap4 = r->poolinfo->tap4; tap5 = r->poolinfo->tap5; - spin_lock_irqsave(&r->lock, flags); - input_rotate = r->input_rotate; - i = r->add_ptr; + smp_rmb(); + input_rotate = ACCESS_ONCE(r->input_rotate); + i = ACCESS_ONCE(r->add_ptr); /* 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 */ @@ -510,53 +521,192 @@ static void mix_pool_bytes_extract(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; } - r->input_rotate = input_rotate; - r->add_ptr = i; + ACCESS_ONCE(r->input_rotate) = input_rotate; + ACCESS_ONCE(r->add_ptr) = i; + smp_wmb(); if (out) for (j = 0; j < 16; j++) ((__u32 *)out)[j] = r->pool[(i - j) & wordmask]; +} + +static void __mix_pool_bytes(struct entropy_store *r, const void *in, + int nbytes, __u8 out[64]) +{ + trace_mix_pool_bytes_nolock(r->name, nbytes, _RET_IP_); + _mix_pool_bytes(r, in, nbytes, out); +} + +static void mix_pool_bytes(struct entropy_store *r, const void *in, + int nbytes, __u8 out[64]) +{ + unsigned long flags; + trace_mix_pool_bytes(r->name, nbytes, _RET_IP_); + spin_lock_irqsave(&r->lock, flags); + _mix_pool_bytes(r, in, nbytes, out); spin_unlock_irqrestore(&r->lock, flags); } -static void mix_pool_bytes(struct entropy_store *r, const void *in, int bytes) +struct fast_pool { + __u32 pool[4]; + unsigned long last; + unsigned short count; + unsigned char rotate; + unsigned char last_timer_intr; +}; + +/* + * This is a fast mixing routine used by the interrupt randomness + * 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, __u32 input[4]) { - mix_pool_bytes_extract(r, in, bytes, NULL); + __u32 w; + unsigned input_rotate = f->rotate; + + 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) { - unsigned long flags; - int entropy_count; + int entropy_count, orig; + const int pool_size = r->poolinfo->poolfracbits; + int nfrac = nbits << ENTROPY_SHIFT; if (!nbits) return; - spin_lock_irqsave(&r->lock, flags); +retry: + entropy_count = orig = ACCESS_ONCE(r->entropy_count); + 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)); + } - DEBUG_ENT("added %d entropy credits to %s\n", nbits, r->name); - entropy_count = r->entropy_count; - entropy_count += nbits; - 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; - r->entropy_count = entropy_count; - - /* 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); + } else if (entropy_count > pool_size) + entropy_count = pool_size; + if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig) + goto retry; + + 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); + } } - spin_unlock_irqrestore(&r->lock, flags); + + trace_credit_entropy_bits(r->name, nbits, + entropy_count >> ENTROPY_SHIFT, + r->entropy_total, _RET_IP_); + + 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); } /********************************************************************* @@ -572,44 +722,35 @@ struct timer_rand_state { unsigned dont_count_entropy:1; }; -#ifndef CONFIG_GENERIC_HARDIRQS - -static struct timer_rand_state *irq_timer_state[NR_IRQS]; - -static struct timer_rand_state *get_timer_rand_state(unsigned int irq) -{ - return irq_timer_state[irq]; -} - -static void set_timer_rand_state(unsigned int irq, - struct timer_rand_state *state) -{ - irq_timer_state[irq] = state; -} +#define INIT_TIMER_RAND_STATE { INITIAL_JIFFIES, }; -#else - -static struct timer_rand_state *get_timer_rand_state(unsigned int irq) -{ - struct irq_desc *desc; - - desc = irq_to_desc(irq); - - return desc->timer_rand_state; -} - -static void set_timer_rand_state(unsigned int irq, - struct timer_rand_state *state) +/* + * Add device- or boot-specific data to the input and nonblocking + * pools to help initialize them to unique values. + * + * None of this adds any entropy, it is meant to avoid the + * problem of the nonblocking pool having similar initial state + * across largely identical devices. + */ +void add_device_randomness(const void *buf, unsigned int size) { - struct irq_desc *desc; + unsigned long time = random_get_entropy() ^ jiffies; + unsigned long flags; - desc = irq_to_desc(irq); + 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); - desc->timer_rand_state = state; + 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); } -#endif +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 @@ -623,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; @@ -631,19 +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; - - /* Use arch random value, fall back to cycles */ - if (!arch_get_random_int(&sample.cycles)) - sample.cycles = get_cycles(); - + sample.cycles = random_get_entropy(); sample.num = num; - mix_pool_bytes(&input_pool, &sample, sizeof(sample)); + 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. @@ -677,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(); } @@ -693,24 +826,71 @@ 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); -void add_interrupt_randomness(int irq) +static DEFINE_PER_CPU(struct fast_pool, irq_randomness); + +void add_interrupt_randomness(int irq, int irq_flags) { - struct timer_rand_state *state; + struct entropy_store *r; + struct fast_pool *fast_pool = &__get_cpu_var(irq_randomness); + struct pt_regs *regs = get_irq_regs(); + unsigned long now = jiffies; + 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; - state = get_timer_rand_state(irq); + fast_pool->last = now; - if (state == NULL) - return; + 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, otherwise credit 1 bit. + */ + credit = 1; + if (cycles == 0) { + if (irq_flags & __IRQF_TIMER) { + if (fast_pool->last_timer_intr) + credit = 0; + fast_pool->last_timer_intr = 1; + } else + fast_pool->last_timer_intr = 0; + } - DEBUG_ENT("irq event %d\n", irq); - add_timer_randomness(state, 0x100 + irq); + /* + * 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 @@ -719,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 /********************************************************************* @@ -740,93 +919,145 @@ 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->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 " - "(%d 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); - credit_entropy_bits(r, bytes*8); + if (r->limit == 0 && random_min_urandom_seed) { + unsigned long now = jiffies; + + 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; + int entropy_count, orig; + size_t ibytes, nfrac; - /* Hold lock while accounting */ - spin_lock_irqsave(&r->lock, flags); - - BUG_ON(r->entropy_count > r->poolinfo->POOLBITS); - DEBUG_ENT("trying to extract %d 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; + + 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; - DEBUG_ENT("debiting %d entropy credits from %s%s\n", - nbytes * 8, r->name, r->limit ? "" : " (unlimited)"); + if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig) + goto retry; - spin_unlock_irqrestore(&r->lock, flags); + 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; - __u32 hash[5], workspace[SHA_WORKSPACE_WORDS]; + union { + __u32 w[5]; + unsigned long l[LONGS(20)]; + } hash; + __u32 workspace[SHA_WORKSPACE_WORDS]; __u8 extract[64]; + unsigned long flags; + + /* + * 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 */ - sha_init(hash); + spin_lock_irqsave(&r->lock, flags); for (i = 0; i < r->poolinfo->poolwords; i += 16) - sha_transform(hash, (__u8 *)(r->pool + i), workspace); + sha_transform(hash.w, (__u8 *)(r->pool + i), workspace); /* * We mix the hash back into the pool to prevent backtracking @@ -837,13 +1068,14 @@ static void extract_buf(struct entropy_store *r, __u8 *out) * brute-forcing the feedback as hard as brute-forcing the * hash. */ - mix_pool_bytes_extract(r, hash, sizeof(hash), extract); + __mix_pool_bytes(r, hash.w, sizeof(hash.w), extract); + spin_unlock_irqrestore(&r->lock, flags); /* * To avoid duplicates, we atomically extract a portion of the * pool while mixing, and hash one final time. */ - sha_transform(hash, extract, workspace); + sha_transform(hash.w, extract, workspace); memset(extract, 0, sizeof(extract)); memset(workspace, 0, sizeof(workspace)); @@ -852,20 +1084,47 @@ static void extract_buf(struct entropy_store *r, __u8 *out) * pattern, we fold it in half. Thus, we always feed back * twice as much data as we output. */ - hash[0] ^= hash[3]; - hash[1] ^= hash[4]; - hash[2] ^= rol32(hash[2], 16); - memcpy(out, hash, EXTRACT_SIZE); - memset(hash, 0, sizeof(hash)); + hash.w[0] ^= hash.w[3]; + hash.w[1] ^= hash.w[4]; + hash.w[2] ^= rol32(hash.w[2], 16); + + 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) + 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) { + 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, ENTROPY_BITS(r), _RET_IP_); xfer_secondary_pool(r, nbytes); nbytes = account(r, nbytes, min, reserved); @@ -892,12 +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, ENTROPY_BITS(r), _RET_IP_); xfer_secondary_pool(r, nbytes); nbytes = account(r, nbytes, 0, 0); @@ -931,17 +1195,44 @@ 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 seeding TCP sequence - * numbers, etc. + * number of good random numbers, suitable for key generation, seeding + * 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); + +/* + * This function will use the architecture-specific hardware random + * number generator if it is available. The arch-specific hw RNG will + * almost certainly be faster than what we can do in software, but it + * is impossible to verify that it is implemented securely (as + * opposed, to, say, the AES encryption of a sequence number using a + * key known by the NSA). So it's useful if we need the speed, but + * only if we're willing to trust the hardware manufacturer not to + * have put in a back door. + */ +void get_random_bytes_arch(void *buf, int nbytes) +{ char *p = buf; + trace_get_random_bytes_arch(nbytes, _RET_IP_); while (nbytes) { unsigned long v; int chunk = min(nbytes, (int)sizeof(unsigned long)); - + if (!arch_get_random_long(&v)) break; @@ -950,9 +1241,11 @@ void get_random_bytes(void *buf, int nbytes) nbytes -= chunk; } - extract_entropy(&nonblocking_pool, p, nbytes, 0, 0); + if (nbytes) + extract_entropy(&nonblocking_pool, p, nbytes, 0, 0); } -EXPORT_SYMBOL(get_random_bytes); +EXPORT_SYMBOL(get_random_bytes_arch); + /* * init_std_data - initialize pool with system data @@ -966,23 +1259,30 @@ EXPORT_SYMBOL(get_random_bytes); static void init_std_data(struct entropy_store *r) { int i; - ktime_t now; - unsigned long flags; - - spin_lock_irqsave(&r->lock, flags); - r->entropy_count = 0; - spin_unlock_irqrestore(&r->lock, flags); - - now = ktime_get_real(); - mix_pool_bytes(r, &now, sizeof(now)); - for (i = r->poolinfo->POOLBYTES; i > 0; i -= sizeof flags) { - if (!arch_get_random_long(&flags)) - break; - mix_pool_bytes(r, &flags, sizeof(flags)); + ktime_t now = ktime_get_real(); + unsigned long rv; + + 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_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()))); + mix_pool_bytes(r, utsname(), sizeof(*(utsname())), NULL); } +/* + * Note that setup_arch() may call add_device_randomness() + * long before we get here. This allows seeding of the pools + * with some platform dependent data very early in the boot + * process. But it limits our options here. We must use + * statically allocated structures that already have all + * initializations complete at compile time. We should also + * take care not to overwrite the precious per platform data + * we were given. + */ static int rand_initialize(void) { init_std_data(&input_pool); @@ -990,25 +1290,7 @@ static int rand_initialize(void) init_std_data(&nonblocking_pool); return 0; } -module_init(rand_initialize); - -void rand_initialize_irq(int irq) -{ - struct timer_rand_state *state; - - state = get_timer_rand_state(irq); - - if (state) - return; - - /* - * If kzalloc returns null, we just won't use that entropy - * source. - */ - state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL); - if (state) - set_timer_rand_state(irq, state); -} +early_initcall(rand_initialize); #ifdef CONFIG_BLOCK void rand_initialize_disk(struct gendisk *disk) @@ -1020,71 +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 %d bits\n", n*8); - - n = extract_entropy_user(&blocking_pool, buf, n); - - DEBUG_ENT("read got %d bits (%d 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; - } - if (n < 0) { - retval = n; - break; - } - 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 @@ -1095,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; } @@ -1117,7 +1424,7 @@ write_pool(struct entropy_store *r, const char __user *buffer, size_t count) count -= bytes; p += bytes; - mix_pool_bytes(r, buf, bytes); + mix_pool_bytes(r, buf, bytes, NULL); cond_resched(); } @@ -1148,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: @@ -1156,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)) @@ -1171,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; @@ -1238,32 +1551,37 @@ 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; if (!uuid) { uuid = tmp_uuid; - uuid[8] = 0; - } - if (uuid[8] == 0) generate_random_uuid(uuid); + } else { + static DEFINE_SPINLOCK(bootid_spinlock); + + spin_lock(&bootid_spinlock); + if (!uuid[8]) + generate_random_uuid(uuid); + spin_unlock(&bootid_spinlock); + } sprintf(buf, "%pU", uuid); @@ -1273,8 +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; -ctl_table random_table[] = { +extern struct ctl_table random_table[]; +struct ctl_table random_table[] = { { .procname = "poolsize", .data = &sysctl_poolsize, @@ -1286,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, @@ -1300,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, @@ -1308,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, @@ -1326,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 @@ -1339,7 +1681,7 @@ late_initcall(random_int_secret_init); * value is not cryptographically secure but for several uses the cost of * depleting entropy is too high */ -DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash); +static DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash); unsigned int get_random_int(void) { __u32 *hash; @@ -1350,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 ccd124ab7ca..35259961cc3 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -57,8 +57,8 @@ * Note that *all* calls to CMOS_READ and CMOS_WRITE are done with * interrupts disabled. Due to the index-port/data-port (0x70/0x71) * design of the RTC, we don't want two different things trying to - * get to it at once. (e.g. the periodic 11 min sync from time.c vs. - * this driver.) + * get to it at once. (e.g. the periodic 11 min sync from + * kernel/time/ntp.c vs. this driver.) */ #include <linux/interrupt.h> @@ -83,7 +83,6 @@ #include <linux/ratelimit.h> #include <asm/current.h> -#include <asm/system.h> #ifdef CONFIG_X86 #include <asm/hpet.h> @@ -228,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 @@ -281,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, @@ -292,7 +291,7 @@ static ctl_table rtc_table[] = { { } }; -static ctl_table rtc_root[] = { +static struct ctl_table rtc_root[] = { { .procname = "rtc", .mode = 0555, @@ -301,7 +300,7 @@ static ctl_table rtc_root[] = { { } }; -static ctl_table dev_root[] = { +static struct ctl_table dev_root[] = { { .procname = "dev", .mode = 0555, @@ -412,7 +411,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) case RTC_IRQP_READ: case RTC_IRQP_SET: return -EINVAL; - }; + } } #endif @@ -1041,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 1ee8ce7d276..7cc1fe2241f 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -54,7 +54,6 @@ #include <asm/uaccess.h> #include <asm/io.h> -#include <asm/system.h> #include <linux/sonypi.h> @@ -877,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); @@ -939,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); } @@ -1143,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; @@ -1165,7 +1159,7 @@ static struct acpi_driver sonypi_acpi_driver = { }; #endif -static int __devinit sonypi_create_input_devices(struct platform_device *pdev) +static int sonypi_create_input_devices(struct platform_device *pdev) { struct input_dev *jog_dev; struct input_dev *key_dev; @@ -1226,7 +1220,7 @@ static int __devinit sonypi_create_input_devices(struct platform_device *pdev) return error; } -static int __devinit sonypi_setup_ioports(struct sonypi_device *dev, +static int sonypi_setup_ioports(struct sonypi_device *dev, const struct sonypi_ioport_list *ioport_list) { /* try to detect if sony-laptop is being used and thus @@ -1266,7 +1260,7 @@ static int __devinit sonypi_setup_ioports(struct sonypi_device *dev, return -EBUSY; } -static int __devinit sonypi_setup_irq(struct sonypi_device *dev, +static int sonypi_setup_irq(struct sonypi_device *dev, const struct sonypi_irq_list *irq_list) { while (irq_list->irq) { @@ -1283,7 +1277,7 @@ static int __devinit sonypi_setup_irq(struct sonypi_device *dev, return -EBUSY; } -static void __devinit sonypi_display_info(void) +static void sonypi_display_info(void) { printk(KERN_INFO "sonypi: detected type%d model, " "verbose = %d, fnkeyinit = %s, camera = %s, " @@ -1305,7 +1299,7 @@ static void __devinit sonypi_display_info(void) sonypi_misc_device.minor); } -static int __devinit sonypi_probe(struct platform_device *dev) +static int sonypi_probe(struct platform_device *dev) { const struct sonypi_ioport_list *ioport_list; const struct sonypi_irq_list *irq_list; @@ -1429,12 +1423,12 @@ static int __devinit sonypi_probe(struct platform_device *dev) return error; } -static int __devexit sonypi_remove(struct platform_device *dev) +static int sonypi_remove(struct platform_device *dev) { sonypi_disable(); synchronize_irq(sonypi_device.irq); - flush_work_sync(&sonypi_device.input_work); + flush_work(&sonypi_device.input_work); if (useinput) { input_unregister_device(sonypi_device.input_key_dev); @@ -1457,10 +1451,10 @@ static int __devexit sonypi_remove(struct platform_device *dev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int old_camera_power; -static int sonypi_suspend(struct platform_device *dev, pm_message_t state) +static int sonypi_suspend(struct device *dev) { old_camera_power = sonypi_device.camera_power; sonypi_disable(); @@ -1468,14 +1462,16 @@ static int sonypi_suspend(struct platform_device *dev, pm_message_t state) return 0; } -static int sonypi_resume(struct platform_device *dev) +static int sonypi_resume(struct device *dev) { sonypi_enable(old_camera_power); return 0; } + +static SIMPLE_DEV_PM_OPS(sonypi_pm, sonypi_suspend, sonypi_resume); +#define SONYPI_PM (&sonypi_pm) #else -#define sonypi_suspend NULL -#define sonypi_resume NULL +#define SONYPI_PM NULL #endif static void sonypi_shutdown(struct platform_device *dev) @@ -1487,12 +1483,11 @@ static struct platform_driver sonypi_driver = { .driver = { .name = "sonypi", .owner = THIS_MODULE, + .pm = SONYPI_PM, }, .probe = sonypi_probe, - .remove = __devexit_p(sonypi_remove), + .remove = sonypi_remove, .shutdown = sonypi_shutdown, - .suspend = sonypi_suspend, - .resume = sonypi_resume, }; static struct platform_device *sonypi_platform_device; diff --git a/drivers/char/tb0219.c b/drivers/char/tb0219.c index ad264185eb1..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; @@ -284,7 +284,7 @@ static void tb0219_pci_irq_init(void) vr41xx_set_irq_level(TB0219_PCI_SLOT3_PIN, IRQ_LEVEL_LOW); } -static int __devinit tb0219_probe(struct platform_device *dev) +static int tb0219_probe(struct platform_device *dev) { int retval; @@ -318,7 +318,7 @@ static int __devinit tb0219_probe(struct platform_device *dev) return 0; } -static int __devexit tb0219_remove(struct platform_device *dev) +static int tb0219_remove(struct platform_device *dev) { _machine_restart = old_machine_restart; @@ -334,7 +334,7 @@ static struct platform_device *tb0219_platform_device; static struct platform_driver tb0219_device_driver = { .probe = tb0219_probe, - .remove = __devexit_p(tb0219_remove), + .remove = tb0219_remove, .driver = { .name = "TB0219", .owner = THIS_MODULE, diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c index 4dc019408fa..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... */ @@ -194,17 +193,17 @@ static ssize_t srom_read(struct file *filp, char __user *buf, hv_retval = _srom_read(srom->hv_devhdl, kernbuf, *f_pos, bytes_this_pass); - if (hv_retval > 0) { - if (copy_to_user(buf, kernbuf, hv_retval) != 0) { - retval = -EFAULT; - break; - } - } else if (hv_retval <= 0) { + if (hv_retval <= 0) { if (retval == 0) retval = hv_retval; break; } + if (copy_to_user(buf, kernbuf, hv_retval) != 0) { + retval = -EFAULT; + break; + } + retval += hv_retval; *f_pos += hv_retval; buf += hv_retval; @@ -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 0c964cdcc22..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 @@ -784,8 +784,10 @@ static int __init tlclk_init(void) } tlclk_major = ret; alarm_events = kzalloc( sizeof(struct tlclk_alarms), GFP_KERNEL); - if (!alarm_events) + if (!alarm_events) { + ret = -ENOMEM; goto out1; + } /* Read telecom clock IRQ number (Set by BIOS) */ if (!request_region(TLCLK_BASE, 8, "telco_clock")) { @@ -797,7 +799,7 @@ static int __init tlclk_init(void) telclk_interrupt = (inb(TLCLK_REG7) & 0x0f); if (0x0F == telclk_interrupt ) { /* not MCPBL0010 ? */ - printk(KERN_ERR "telclk_interrup = 0x%x non-mcpbl0010 hw.\n", + printk(KERN_ERR "telclk_interrupt = 0x%x non-mcpbl0010 hw.\n", telclk_interrupt); ret = -ENXIO; goto out3; diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 7fc75e47e6d..c54cac3f8bc 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -5,7 +5,6 @@ menuconfig TCG_TPM tristate "TPM Hardware Support" depends on HAS_IOMEM - depends on EXPERIMENTAL select SECURITYFS ---help--- If you have a TPM security chip in your system, which @@ -34,6 +33,36 @@ 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 + ---help--- + If you have a TPM security chip that is compliant with the + TCG TIS 1.2 TPM specification and Infineon's I2C Protocol Stack + 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_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" depends on X86 @@ -45,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 @@ -63,4 +92,34 @@ config TCG_INFINEON Further information on this driver and the supported hardware can be found at http://www.trust.rub.de/projects/linux-device-driver-infineon-tpm/ +config TCG_IBMVTPM + tristate "IBM VTPM Interface" + 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 ea3a1e02a82..4d85dd681b8 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -2,10 +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-y += tpm_eventlog.o tpm_acpi.o +else +ifdef CONFIG_TCG_IBMVTPM + 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 32362cf35b8..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. @@ -30,23 +30,12 @@ #include <linux/freezer.h> #include "tpm.h" - -enum tpm_const { - TPM_MINOR = 224, /* officially assigned */ - TPM_BUFSIZE = 4096, - TPM_NUM_DEVICES = 256, -}; - -enum tpm_duration { - TPM_SHORT = 0, - TPM_MEDIUM = 1, - TPM_LONG = 2, - TPM_UNDEFINED, -}; +#include "tpm_eventlog.h" #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 @@ -70,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, @@ -331,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 */ @@ -356,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]; @@ -377,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; @@ -393,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; @@ -410,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; @@ -425,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); @@ -443,45 +398,27 @@ 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) static const struct tpm_input_header tpm_getcap_header = { .tag = TPM_TAG_RQU_COMMAND, @@ -532,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; @@ -545,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) @@ -643,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 */ @@ -736,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; @@ -754,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 @@ -771,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; } @@ -779,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 @@ -827,13 +736,12 @@ EXPORT_SYMBOL_GPL(tpm_pcr_extend); int tpm_do_selftest(struct tpm_chip *chip) { int rc; - u8 digest[TPM_DIGEST_SIZE]; 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,7 +753,23 @@ int tpm_do_selftest(struct tpm_chip *chip) return rc; do { - rc = __tpm_pcr_read(chip, 0, digest); + /* Attempt to read a PCR value */ + 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; + + rc = be32_to_cpu(cmd.header.out.return_code); if (rc == TPM_ERR_DISABLED || rc == TPM_ERR_DEACTIVATED) { dev_info(chip->dev, "TPM is disabled/deactivated (0x%X)\n", rc); @@ -880,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; + u8 status = chip->ops->status(chip); - 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); - -#define READ_PUBEK_RESULT_SIZE 314 -#define TPM_ORD_READPUBEK cpu_to_be32(124) -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"); -} -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; + return false; } -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; @@ -1091,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; @@ -1103,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)); @@ -1111,131 +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_sync(&chip->work); - file->private_data = NULL; - atomic_set(&chip->data_pending, 0); - kfree(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, out_size; - - /* cannot perform a write until the read has cleared - either via tpm_read or a user_read_timer timeout */ - while (atomic_read(&chip->data_pending) != 0) - msleep(TPM_TIMEOUT); - - mutex_lock(&chip->buffer_mutex); - - if (in_size > TPM_BUFSIZE) - in_size = TPM_BUFSIZE; - - 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); - - 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_sync(&chip->work); - ret_size = atomic_read(&chip->data_pending); - atomic_set(&chip->data_pending, 0); - if (ret_size > 0) { /* relay data */ - 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, ret_size); - if (rc) - ret_size = -EFAULT; - - mutex_unlock(&chip->buffer_mutex); - } - - return ret_size; -} -EXPORT_SYMBOL_GPL(tpm_read); void tpm_remove_hardware(struct device *dev) { @@ -1251,8 +878,9 @@ 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); /* write it this way to be explicit (chip->dev == dev) */ @@ -1273,11 +901,11 @@ static struct tpm_input_header savestate_header = { * We are about to suspend. Save the TPM state * so that it can be restored. */ -int tpm_pm_suspend(struct device *dev, pm_message_t pm_state) +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 }; @@ -1295,9 +923,32 @@ int tpm_pm_suspend(struct device *dev, pm_message_t pm_state) } /* 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); @@ -1317,15 +968,66 @@ int tpm_pm_resume(struct device *dev) } EXPORT_SYMBOL_GPL(tpm_pm_resume); +#define TPM_GETRANDOM_RESULT_SIZE 18 +static struct tpm_input_header tpm_getrandom_header = { + .tag = TPM_TAG_RQU_COMMAND, + .length = cpu_to_be32(14), + .ordinal = TPM_ORD_GET_RANDOM +}; + +/** + * tpm_get_random() - Get random bytes from the tpm's RNG + * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @out: destination buffer for the random bytes + * @max: the max number of bytes to write to @out + * + * Returns < 0 on error and the number of bytes read on success + */ +int tpm_get_random(u32 chip_num, u8 *out, size_t max) +{ + struct tpm_chip *chip; + struct tpm_cmd_t tpm_cmd; + u32 recd, num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA); + int err, total = 0, retries = 5; + u8 *dest = out; + + chip = tpm_chip_find_get(chip_num); + if (chip == NULL) + return -ENODEV; + + if (!out || !num_bytes || max > TPM_MAX_RNG_DATA) + return -EINVAL; + + do { + tpm_cmd.header.in = tpm_getrandom_header; + tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes); + + err = transmit_cmd(chip, &tpm_cmd, + TPM_GETRANDOM_RESULT_SIZE + num_bytes, + "attempting get random"); + if (err) + break; + + recd = be32_to_cpu(tpm_cmd.params.getrandom_out.rng_data_len); + memcpy(dest, tpm_cmd.params.getrandom_out.rng_data, recd); + + dest += recd; + total += recd; + num_bytes -= recd; + } while (retries-- && total < max); + + return total ? total : -EIO; +} +EXPORT_SYMBOL_GPL(tpm_get_random); + /* In case vendor provided release function, call it too.*/ void tpm_dev_vendor_release(struct tpm_chip *chip) { - if (chip->vendor.release) - chip->vendor.release(chip->dev); + if (!chip) + return; clear_bit(chip->dev_num, dev_mask); - kfree(chip->vendor.miscdev.name); } EXPORT_SYMBOL_GPL(tpm_dev_vendor_release); @@ -1334,88 +1036,68 @@ EXPORT_SYMBOL_GPL(tpm_dev_vendor_release); * Once all references to platform device are down to 0, * release all allocated structures. */ -void tpm_dev_release(struct device *dev) +static void tpm_dev_release(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); + if (!chip) + return; + tpm_dev_vendor_release(chip); 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); - put_device(chip->dev); - return NULL; - } + if (tpm_dev_add_device(chip)) + goto put_device; - if (sysfs_create_group(&dev->kobj, chip->vendor.attr_group)) { - misc_deregister(&chip->vendor.miscdev); - put_device(chip->dev); + if (tpm_sysfs_add_device(chip)) + goto del_misc; - return NULL; - } + 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); @@ -1424,9 +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 01054713828..e4d0888d2ea 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -28,8 +28,16 @@ #include <linux/io.h> #include <linux/tpm.h> +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 */ @@ -38,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 */ @@ -81,38 +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 *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; @@ -145,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; @@ -160,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; @@ -175,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; @@ -212,7 +204,7 @@ struct permanent_flags_t { u8 tpmEstablished; u8 maintenanceDone; u8 disableFullDALogicInfo; -}__attribute__((packed)); +} __packed; typedef union { struct permanent_flags_t perm_flags; @@ -226,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]; @@ -246,26 +256,44 @@ 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 + * anything much bigger causes users of struct tpm_cmd_t to start getting + * compiler warnings about stack frame size. */ +#define TPM_MAX_RNG_DATA 128 + +struct tpm_getrandom_out { + __be32 rng_data_len; + u8 rng_data[TPM_MAX_RNG_DATA]; +} __packed; + +struct tpm_getrandom_in { + __be32 num_bytes; +} __packed; + +struct tpm_startup_in { + __be16 startup_type; +} __packed; typedef union { struct tpm_getcap_params_out getcap_out; @@ -275,41 +303,50 @@ typedef union { struct tpm_pcrread_in pcrread_in; struct tpm_pcrread_out pcrread_out; 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 *, pm_message_t); +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 struct dentry ** tpm_bios_log_setup(char *); -extern void tpm_bios_log_teardown(struct dentry **); +extern int tpm_add_ppi(struct kobject *); +extern void tpm_remove_ppi(struct kobject *); #else -static inline struct dentry ** tpm_bios_log_setup(char *name) +static inline int tpm_add_ppi(struct kobject *parent) { - return NULL; + return 0; } -static inline void tpm_bios_log_teardown(struct dentry **dir) + +static inline void tpm_remove_ppi(struct kobject *parent) { } #endif diff --git a/drivers/char/tpm/tpm_acpi.c b/drivers/char/tpm/tpm_acpi.c new file mode 100644 index 00000000000..565a9478cb9 --- /dev/null +++ b/drivers/char/tpm/tpm_acpi.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Seiji Munetoh <munetoh@jp.ibm.com> + * Stefan Berger <stefanb@us.ibm.com> + * Reiner Sailer <sailer@watson.ibm.com> + * Kylene Hall <kjhall@us.ibm.com> + * + * Maintained by: <tpmdd-devel@lists.sourceforge.net> + * + * Access to the eventlog extended by the TCG BIOS of PC platform + * + * 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. + * + */ + +#include <linux/seq_file.h> +#include <linux/fs.h> +#include <linux/security.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/acpi.h> + +#include "tpm.h" +#include "tpm_eventlog.h" + +struct acpi_tcpa { + struct acpi_table_header hdr; + u16 platform_class; + union { + struct client_hdr { + u32 log_max_len __packed; + u64 log_start_addr __packed; + } client; + struct server_hdr { + u16 reserved; + u64 log_max_len __packed; + u64 log_start_addr __packed; + } server; + }; +}; + +/* read binary bios log */ +int read_log(struct tpm_bios_log *log) +{ + struct acpi_tcpa *buff; + acpi_status status; + void __iomem *virt; + u64 len, start; + + if (log->bios_event_log != NULL) { + printk(KERN_ERR + "%s: ERROR - Eventlog already initialized\n", + __func__); + return -EFAULT; + } + + /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */ + status = acpi_get_table(ACPI_SIG_TCPA, 1, + (struct acpi_table_header **)&buff); + + if (ACPI_FAILURE(status)) { + printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n", + __func__); + return -EIO; + } + + switch(buff->platform_class) { + case BIOS_SERVER: + len = buff->server.log_max_len; + start = buff->server.log_start_addr; + break; + case BIOS_CLIENT: + default: + len = buff->client.log_max_len; + start = buff->client.log_start_addr; + break; + } + if (!len) { + printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__); + return -EIO; + } + + /* malloc EventLog space */ + log->bios_event_log = kmalloc(len, GFP_KERNEL); + if (!log->bios_event_log) { + printk("%s: ERROR - Not enough Memory for BIOS measurements\n", + __func__); + return -ENOMEM; + } + + log->bios_event_log_end = log->bios_event_log + len; + + virt = acpi_os_map_iomem(start, len); + if (!virt) { + kfree(log->bios_event_log); + printk("%s: ERROR - Unable to map memory\n", __func__); + return -EIO; + } + + memcpy_fromio(log->bios_event_log, 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 c64a1bc6534..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; @@ -168,22 +147,14 @@ static void atml_plat_remove(void) } } -static int tpm_atml_suspend(struct platform_device *dev, pm_message_t msg) -{ - return tpm_pm_suspend(&dev->dev, msg); -} +static SIMPLE_DEV_PM_OPS(tpm_atml_pm, tpm_pm_suspend, tpm_pm_resume); -static int tpm_atml_resume(struct platform_device *dev) -{ - return tpm_pm_resume(&dev->dev); -} static struct platform_driver atml_drv = { .driver = { .name = "tpm_atmel", .owner = THIS_MODULE, + .pm = &tpm_atml_pm, }, - .suspend = tpm_atml_suspend, - .resume = tpm_atml_resume, }; static int __init init_atmel(void) @@ -205,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_bios.c b/drivers/char/tpm/tpm_eventlog.c index 0636520fa9b..59f7cb28260 100644 --- a/drivers/char/tpm/tpm_bios.c +++ b/drivers/char/tpm/tpm_eventlog.c @@ -1,7 +1,8 @@ /* - * Copyright (C) 2005 IBM Corporation + * Copyright (C) 2005, 2012 IBM Corporation * * Authors: + * Kent Yoder <key@linux.vnet.ibm.com> * Seiji Munetoh <munetoh@jp.ibm.com> * Stefan Berger <stefanb@us.ibm.com> * Reiner Sailer <sailer@watson.ibm.com> @@ -9,7 +10,7 @@ * * Maintained by: <tpmdd-devel@lists.sourceforge.net> * - * Access to the eventlog extended by the TCG BIOS of PC platform + * Access to the eventlog created by a system's firmware / BIOS * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -23,67 +24,10 @@ #include <linux/security.h> #include <linux/module.h> #include <linux/slab.h> -#include <acpi/acpi.h> -#include "tpm.h" - -#define TCG_EVENT_NAME_LEN_MAX 255 -#define MAX_TEXT_EVENT 1000 /* Max event string length */ -#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */ - -enum bios_platform_class { - BIOS_CLIENT = 0x00, - BIOS_SERVER = 0x01, -}; - -struct tpm_bios_log { - void *bios_event_log; - void *bios_event_log_end; -}; - -struct acpi_tcpa { - struct acpi_table_header hdr; - u16 platform_class; - union { - struct client_hdr { - u32 log_max_len __attribute__ ((packed)); - u64 log_start_addr __attribute__ ((packed)); - } client; - struct server_hdr { - u16 reserved; - u64 log_max_len __attribute__ ((packed)); - u64 log_start_addr __attribute__ ((packed)); - } server; - }; -}; -struct tcpa_event { - u32 pcr_index; - u32 event_type; - u8 pcr_value[20]; /* SHA1 */ - u32 event_size; - u8 event_data[0]; -}; +#include "tpm.h" +#include "tpm_eventlog.h" -enum tcpa_event_types { - PREBOOT = 0, - POST_CODE, - UNUSED, - NO_ACTION, - SEPARATOR, - ACTION, - EVENT_TAG, - SCRTM_CONTENTS, - SCRTM_VERSION, - CPU_MICROCODE, - PLATFORM_CONFIG_FLAGS, - TABLE_OF_DEVICES, - COMPACT_HASH, - IPL, - IPL_PARTITION_DATA, - NONHOST_CODE, - NONHOST_CONFIG, - NONHOST_INFO, -}; static const char* tcpa_event_type_strings[] = { "PREBOOT", @@ -106,28 +50,6 @@ static const char* tcpa_event_type_strings[] = { "Non-Host Info" }; -struct tcpa_pc_event { - u32 event_id; - u32 event_size; - u8 event_data[0]; -}; - -enum tcpa_pc_event_ids { - SMBIOS = 1, - BIS_CERT, - POST_BIOS_ROM, - ESCD, - CMOS, - NVRAM, - OPTION_ROM_EXEC, - OPTION_ROM_CONFIG, - OPTION_ROM_MICROCODE = 10, - S_CRTM_VERSION, - S_CRTM_CONTENTS, - POST_CONTENTS, - HOST_TABLE_OF_DEVICES, -}; - static const char* tcpa_pc_event_id_strings[] = { "", "SMBIOS", @@ -358,65 +280,6 @@ static const struct seq_operations tpm_binary_b_measurments_seqops = { .show = tpm_binary_bios_measurements_show, }; -/* read binary bios log */ -static int read_log(struct tpm_bios_log *log) -{ - struct acpi_tcpa *buff; - acpi_status status; - struct acpi_table_header *virt; - u64 len, start; - - if (log->bios_event_log != NULL) { - printk(KERN_ERR - "%s: ERROR - Eventlog already initialized\n", - __func__); - return -EFAULT; - } - - /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */ - status = acpi_get_table(ACPI_SIG_TCPA, 1, - (struct acpi_table_header **)&buff); - - if (ACPI_FAILURE(status)) { - printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n", - __func__); - return -EIO; - } - - switch(buff->platform_class) { - case BIOS_SERVER: - len = buff->server.log_max_len; - start = buff->server.log_start_addr; - break; - case BIOS_CLIENT: - default: - len = buff->client.log_max_len; - start = buff->client.log_start_addr; - break; - } - if (!len) { - printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__); - return -EIO; - } - - /* malloc EventLog space */ - log->bios_event_log = kmalloc(len, GFP_KERNEL); - if (!log->bios_event_log) { - printk("%s: ERROR - Not enough Memory for BIOS measurements\n", - __func__); - return -ENOMEM; - } - - log->bios_event_log_end = log->bios_event_log + len; - - virt = acpi_os_map_memory(start, len); - - memcpy(log->bios_event_log, virt, len); - - acpi_os_unmap_memory(virt, len); - return 0; -} - static int tpm_ascii_bios_measurements_open(struct inode *inode, struct file *file) { @@ -543,7 +406,6 @@ out_tpm: out: return NULL; } -EXPORT_SYMBOL_GPL(tpm_bios_log_setup); void tpm_bios_log_teardown(struct dentry **lst) { @@ -552,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_eventlog.h b/drivers/char/tpm/tpm_eventlog.h new file mode 100644 index 00000000000..e7da086d692 --- /dev/null +++ b/drivers/char/tpm/tpm_eventlog.h @@ -0,0 +1,86 @@ + +#ifndef __TPM_EVENTLOG_H__ +#define __TPM_EVENTLOG_H__ + +#define TCG_EVENT_NAME_LEN_MAX 255 +#define MAX_TEXT_EVENT 1000 /* Max event string length */ +#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */ + +enum bios_platform_class { + BIOS_CLIENT = 0x00, + BIOS_SERVER = 0x01, +}; + +struct tpm_bios_log { + void *bios_event_log; + void *bios_event_log_end; +}; + +struct tcpa_event { + u32 pcr_index; + u32 event_type; + u8 pcr_value[20]; /* SHA1 */ + u32 event_size; + u8 event_data[0]; +}; + +enum tcpa_event_types { + PREBOOT = 0, + POST_CODE, + UNUSED, + NO_ACTION, + SEPARATOR, + ACTION, + EVENT_TAG, + SCRTM_CONTENTS, + SCRTM_VERSION, + CPU_MICROCODE, + PLATFORM_CONFIG_FLAGS, + TABLE_OF_DEVICES, + COMPACT_HASH, + IPL, + IPL_PARTITION_DATA, + NONHOST_CODE, + NONHOST_CONFIG, + NONHOST_INFO, +}; + +struct tcpa_pc_event { + u32 event_id; + u32 event_size; + u8 event_data[0]; +}; + +enum tcpa_pc_event_ids { + SMBIOS = 1, + BIS_CERT, + POST_BIOS_ROM, + ESCD, + CMOS, + NVRAM, + OPTION_ROM_EXEC, + OPTION_ROM_CONFIG, + OPTION_ROM_MICROCODE = 10, + S_CRTM_VERSION, + S_CRTM_CONTENTS, + POST_CONTENTS, + HOST_TABLE_OF_DEVICES, +}; + +int read_log(struct tpm_bios_log *log); + +#if defined(CONFIG_TCG_IBMVTPM) || defined(CONFIG_TCG_IBMVTPM_MODULE) || \ + defined(CONFIG_ACPI) +extern struct dentry **tpm_bios_log_setup(char *); +extern void tpm_bios_log_teardown(struct dentry **); +#else +static inline struct dentry **tpm_bios_log_setup(char *name) +{ + return NULL; +} +static inline void tpm_bios_log_teardown(struct dentry **dir) +{ +} +#endif + +#endif 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 new file mode 100644 index 00000000000..472af4bb1b6 --- /dev/null +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -0,0 +1,747 @@ +/* + * Copyright (C) 2012,2013 Infineon Technologies + * + * Authors: + * Peter Huewe <peter.huewe@infineon.com> + * + * Device driver for TCG/TCPA TPM (trusted platform module). + * Specifications at www.trustedcomputinggroup.org + * + * This device driver implements the TPM interface as defined in + * the TCG TPM Interface Spec version 1.2, revision 1.0 and the + * Infineon I2C Protocol Stack Specification v0.20. + * + * It is based on the original tpm_tis device driver from Leendert van + * Dorn and Kyleen Hall. + * + * 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/i2c.h> +#include <linux/module.h> +#include <linux/wait.h> +#include "tpm.h" + +/* max. buffer size supported by our TPM */ +#define TPM_BUFSIZE 1260 + +/* max. number of iterations after I2C NAK */ +#define MAX_COUNT 3 + +#define SLEEP_DURATION_LOW 55 +#define SLEEP_DURATION_HI 65 + +/* max. number of iterations after I2C NAK for 'long' commands + * we need this especially for sending TPM_READY, since the cleanup after the + * transtion to the ready state may take some time, but it is unpredictable + * how long it will take. + */ +#define MAX_COUNT_LONG 50 + +#define SLEEP_DURATION_LONG_LOW 200 +#define SLEEP_DURATION_LONG_HI 220 + +/* After sending TPM_READY to 'reset' the TPM we have to sleep even longer */ +#define SLEEP_DURATION_RESET_LOW 2400 +#define SLEEP_DURATION_RESET_HI 2600 + +/* we want to use usleep_range instead of msleep for the 5ms TPM_TIMEOUT */ +#define TPM_TIMEOUT_US_LOW (TPM_TIMEOUT * 1000) +#define TPM_TIMEOUT_US_HI (TPM_TIMEOUT_US_LOW + 2000) + +/* expected value for DIDVID register */ +#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; + +/* + * iic_tpm_read() - read from TPM register + * @addr: register address to read from + * @buffer: provided by caller + * @len: number of bytes to read + * + * Read len bytes from TPM register and put them into + * buffer (little-endian format, i.e. first byte is put into buffer[0]). + * + * NOTE: TPM is big-endian for multi-byte values. Multi-byte + * values have to be swapped. + * + * NOTE: We can't unfortunately use the combined read/write functions + * provided by the i2c core as the TPM currently does not support the + * repeated start condition and due to it's special requirements. + * The i2c_smbus* functions do not work for this chip. + * + * Return -EIO on error, 0 on success. + */ +static int iic_tpm_read(u8 addr, u8 *buffer, size_t len) +{ + + 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 = 0; + int count; + + /* Lock the adapter for the duration of the whole sequence. */ + if (!tpm_dev.client->adapter->algo->master_xfer) + return -EOPNOTSUPP; + i2c_lock_adapter(tpm_dev.client->adapter); + + 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; + + return 0; +} + +static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, + unsigned int sleep_low, + unsigned int sleep_hi, u8 max_count) +{ + int rc = -EIO; + int count; + + struct i2c_msg msg1 = { + .addr = tpm_dev.client->addr, + .len = len + 1, + .buf = tpm_dev.buf + }; + + if (len > TPM_BUFSIZE) + return -EINVAL; + + if (!tpm_dev.client->adapter->algo->master_xfer) + return -EOPNOTSUPP; + i2c_lock_adapter(tpm_dev.client->adapter); + + /* prepend the 'register address' to the buffer */ + tpm_dev.buf[0] = addr; + memcpy(&(tpm_dev.buf[1]), buffer, 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; + + return 0; +} + +/* + * iic_tpm_write() - write to TPM register + * @addr: register address to write to + * @buffer: containing data to be written + * @len: number of bytes to write + * + * Write len bytes from provided buffer to TPM register (little + * endian format, i.e. buffer[0] is written as first byte). + * + * NOTE: TPM is big-endian for multi-byte values. Multi-byte + * values have to be swapped. + * + * NOTE: use this function instead of the iic_tpm_write_generic function. + * + * Return -EIO on error, 0 on success + */ +static int iic_tpm_write(u8 addr, u8 *buffer, size_t len) +{ + return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LOW, + SLEEP_DURATION_HI, MAX_COUNT); +} + +/* + * This function is needed especially for the cleanup situation after + * sending TPM_READY + * */ +static int iic_tpm_write_long(u8 addr, u8 *buffer, size_t len) +{ + return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG_LOW, + SLEEP_DURATION_LONG_HI, MAX_COUNT_LONG); +} + +enum tis_access { + TPM_ACCESS_VALID = 0x80, + TPM_ACCESS_ACTIVE_LOCALITY = 0x20, + TPM_ACCESS_REQUEST_PENDING = 0x04, + TPM_ACCESS_REQUEST_USE = 0x02, +}; + +enum tis_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 tis_defaults { + TIS_SHORT_TIMEOUT = 750, /* ms */ + TIS_LONG_TIMEOUT = 2000, /* 2 sec */ +}; + +#define TPM_ACCESS(l) (0x0000 | ((l) << 4)) +#define TPM_STS(l) (0x0001 | ((l) << 4)) +#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4)) +#define TPM_DID_VID(l) (0x0006 | ((l) << 4)) + +static int check_locality(struct tpm_chip *chip, int loc) +{ + u8 buf; + int rc; + + rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1); + if (rc < 0) + return rc; + + if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) { + chip->vendor.locality = loc; + return loc; + } + + return -EIO; +} + +/* implementation similar to tpm_tis */ +static void release_locality(struct tpm_chip *chip, int loc, int force) +{ + u8 buf; + if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0) + return; + + if (force || (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == + (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) { + buf = TPM_ACCESS_ACTIVE_LOCALITY; + iic_tpm_write(TPM_ACCESS(loc), &buf, 1); + } +} + +static int request_locality(struct tpm_chip *chip, int loc) +{ + unsigned long stop; + u8 buf = TPM_ACCESS_REQUEST_USE; + + if (check_locality(chip, loc) >= 0) + return loc; + + iic_tpm_write(TPM_ACCESS(loc), &buf, 1); + + /* wait for burstcount */ + stop = jiffies + chip->vendor.timeout_a; + do { + if (check_locality(chip, loc) >= 0) + return loc; + usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI); + } while (time_before(jiffies, stop)); + + return -ETIME; +} + +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 = 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) +{ + /* this causes the current command to be aborted */ + u8 buf = TPM_STS_COMMAND_READY; + iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1); +} + +static ssize_t get_burstcount(struct tpm_chip *chip) +{ + unsigned long stop; + ssize_t burstcnt; + u8 buf[3]; + + /* wait for burstcount */ + /* which timeout value, spec has 2 answers (c & d) */ + stop = jiffies + chip->vendor.timeout_d; + do { + /* Note: STS is little endian */ + if (iic_tpm_read(TPM_STS(chip->vendor.locality)+1, buf, 3) < 0) + burstcnt = 0; + else + burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0]; + + if (burstcnt) + return burstcnt; + + usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI); + } while (time_before(jiffies, stop)); + return -EBUSY; +} + +static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, + int *status) +{ + unsigned long stop; + + /* check current status */ + *status = tpm_tis_i2c_status(chip); + if ((*status != 0xFF) && (*status & mask) == mask) + return 0; + + stop = jiffies + timeout; + do { + /* since we just checked the status, give the TPM some time */ + usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI); + *status = tpm_tis_i2c_status(chip); + if ((*status & mask) == mask) + return 0; + + } while (time_before(jiffies, stop)); + + return -ETIME; +} + +static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) +{ + size_t size = 0; + ssize_t burstcnt; + u8 retries = 0; + int rc; + + while (size < count) { + burstcnt = get_burstcount(chip); + + /* burstcnt < 0 = TPM is busy */ + if (burstcnt < 0) + return burstcnt; + + /* limit received data to max. left */ + if (burstcnt > (count - size)) + burstcnt = count - size; + + rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality), + &(buf[size]), burstcnt); + if (rc == 0) + size += burstcnt; + else if (rc < 0) + retries++; + + /* avoid endless loop in case of broken HW */ + if (retries > MAX_COUNT_LONG) + return -EIO; + } + return size; +} + +static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count) +{ + int size = 0; + int expected, status; + + if (count < TPM_HEADER_SIZE) { + size = -EIO; + goto out; + } + + /* read first 10 bytes, including tag, paramsize, and result */ + 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 ((size_t) 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; + } + + wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status); + if (status & TPM_STS_DATA_AVAIL) { /* retry? */ + dev_err(chip->dev, "Error left over data\n"); + size = -EIO; + goto out; + } + +out: + tpm_tis_i2c_ready(chip); + /* The TPM needs some time to clean up here, + * so we sleep rather than keeping the bus busy + */ + usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI); + release_locality(chip, chip->vendor.locality, 0); + return size; +} + +static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) +{ + int rc, status; + ssize_t burstcnt; + size_t count = 0; + u8 retries = 0; + u8 sts = TPM_STS_GO; + + if (len > TPM_BUFSIZE) + return -E2BIG; /* command is too long for our tpm, sorry */ + + if (request_locality(chip, 0) < 0) + return -EBUSY; + + status = tpm_tis_i2c_status(chip); + if ((status & TPM_STS_COMMAND_READY) == 0) { + tpm_tis_i2c_ready(chip); + if (wait_for_stat + (chip, TPM_STS_COMMAND_READY, + chip->vendor.timeout_b, &status) < 0) { + rc = -ETIME; + goto out_err; + } + } + + while (count < len - 1) { + burstcnt = get_burstcount(chip); + + /* burstcnt < 0 = TPM is busy */ + if (burstcnt < 0) + return burstcnt; + + if (burstcnt > (len - 1 - count)) + burstcnt = len - 1 - count; + + rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), + &(buf[count]), burstcnt); + if (rc == 0) + count += burstcnt; + else if (rc < 0) + retries++; + + /* avoid endless loop in case of broken HW */ + if (retries > MAX_COUNT_LONG) { + rc = -EIO; + goto out_err; + } + + wait_for_stat(chip, TPM_STS_VALID, + chip->vendor.timeout_c, &status); + + if ((status & TPM_STS_DATA_EXPECT) == 0) { + rc = -EIO; + goto out_err; + } + } + + /* write last byte */ + iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1); + wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status); + if ((status & TPM_STS_DATA_EXPECT) != 0) { + rc = -EIO; + goto out_err; + } + + /* go and do it */ + iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1); + + return len; +out_err: + tpm_tis_i2c_ready(chip); + /* The TPM needs some time to clean up here, + * so we sleep rather than keeping the bus busy + */ + usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI); + release_locality(chip, chip->vendor.locality, 0); + return rc; +} + +static bool tpm_tis_i2c_req_canceled(struct tpm_chip *chip, u8 status) +{ + return (status == TPM_STS_COMMAND_READY); +} + +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_tis_i2c_req_canceled, +}; + +static int tpm_tis_i2c_init(struct device *dev) +{ + u32 vendor; + int rc = 0; + struct tpm_chip *chip; + + chip = tpm_register_hardware(dev, &tpm_tis_i2c); + if (!chip) { + dev_err(dev, "could not register hardware\n"); + rc = -ENODEV; + goto out_err; + } + + /* Disable interrupts */ + chip->vendor.irq = 0; + + /* Default timeouts */ + 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); + + 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; + } + + 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; + } + + dev_info(dev, "1.2 TPM (device-id 0x%X)\n", vendor >> 16); + + INIT_LIST_HEAD(&chip->vendor.list); + tpm_dev.chip = chip; + + tpm_get_timeouts(chip); + tpm_do_selftest(chip); + + return 0; + +out_release: + release_locality(chip, chip->vendor.locality, 1); + +out_vendor: + /* close file handles */ + tpm_dev_vendor_release(chip); + + /* remove hardware */ + tpm_remove_hardware(chip->dev); + + /* reset these pointers, otherwise we oops */ + chip->dev->release = NULL; + chip->release = NULL; + tpm_dev.client = NULL; +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; + 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(dev, "no algorithms associated to the i2c bus\n"); + return -ENODEV; + } + + tpm_dev.client = client; + rc = tpm_tis_i2c_init(&client->dev); + if (rc != 0) { + tpm_dev.client = NULL; + rc = -ENODEV; + } + return rc; +} + +static int tpm_tis_i2c_remove(struct i2c_client *client) +{ + struct tpm_chip *chip = tpm_dev.chip; + release_locality(chip, chip->vendor.locality, 1); + + /* close file handles */ + tpm_dev_vendor_release(chip); + + /* remove hardware */ + tpm_remove_hardware(chip->dev); + + /* reset these pointers, otherwise we oops */ + chip->dev->release = NULL; + chip->release = NULL; + tpm_dev.client = NULL; + + 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, + .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.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 new file mode 100644 index 00000000000..af74c57e509 --- /dev/null +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -0,0 +1,690 @@ +/* + * Copyright (C) 2012 IBM Corporation + * + * Author: Ashley Lai <adlai@us.ibm.com> + * + * Maintained by: <tpmdd-devel@lists.sourceforge.net> + * + * Device driver for TCG/TCPA TPM (trusted platform module). + * 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. + * + */ + +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/slab.h> +#include <asm/vio.h> +#include <asm/irq.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <asm/prom.h> + +#include "tpm.h" +#include "tpm_ibmvtpm.h" + +static const char tpm_ibmvtpm_driver_name[] = "tpm_ibmvtpm"; + +static struct vio_device_id tpm_ibmvtpm_device_table[] = { + { "IBM,vtpm", "IBM,vtpm"}, + { "", "" } +}; +MODULE_DEVICE_TABLE(vio, tpm_ibmvtpm_device_table); + +/** + * ibmvtpm_send_crq - Send a CRQ request + * @vdev: vio device struct + * @w1: first word + * @w2: second word + * + * Return value: + * 0 -Sucess + * Non-zero - Failure + */ +static int ibmvtpm_send_crq(struct vio_dev *vdev, u64 w1, u64 w2) +{ + return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, w1, w2); +} + +/** + * ibmvtpm_get_data - Retrieve ibm vtpm data + * @dev: device struct + * + * Return value: + * vtpm device struct + */ +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 *)TPM_VPRIV(chip); + return NULL; +} + +/** + * tpm_ibmvtpm_recv - Receive data after send + * @chip: tpm chip struct + * @buf: buffer to read + * count: size of buffer + * + * Return value: + * Number of bytes read + */ +static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct ibmvtpm_dev *ibmvtpm; + u16 len; + int sig; + + ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip); + + if (!ibmvtpm->rtce_buf) { + dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n"); + return 0; + } + + sig = wait_event_interruptible(ibmvtpm->wq, ibmvtpm->res_len != 0); + if (sig) + return -EINTR; + + len = ibmvtpm->res_len; + + if (count < len) { + dev_err(ibmvtpm->dev, + "Invalid size in recv: count=%zd, crq_size=%d\n", + count, len); + return -EIO; + } + + spin_lock(&ibmvtpm->rtce_lock); + memcpy((void *)buf, (void *)ibmvtpm->rtce_buf, len); + memset(ibmvtpm->rtce_buf, 0, len); + ibmvtpm->res_len = 0; + spin_unlock(&ibmvtpm->rtce_lock); + return len; +} + +/** + * tpm_ibmvtpm_send - Send tpm request + * @chip: tpm chip struct + * @buf: buffer contains data to send + * count: size of buffer + * + * Return value: + * Number of bytes sent + */ +static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct ibmvtpm_dev *ibmvtpm; + struct ibmvtpm_crq crq; + u64 *word = (u64 *) &crq; + int rc; + + ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip); + + if (!ibmvtpm->rtce_buf) { + dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n"); + return 0; + } + + if (count > ibmvtpm->rtce_size) { + dev_err(ibmvtpm->dev, + "Invalid size in send: count=%zd, rtce_size=%d\n", + count, ibmvtpm->rtce_size); + return -EIO; + } + + spin_lock(&ibmvtpm->rtce_lock); + memcpy((void *)ibmvtpm->rtce_buf, (void *)buf, count); + crq.valid = (u8)IBMVTPM_VALID_CMD; + crq.msg = (u8)VTPM_TPM_COMMAND; + crq.len = (u16)count; + crq.data = ibmvtpm->rtce_dma_handle; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, word[0], word[1]); + if (rc != H_SUCCESS) { + dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc); + rc = 0; + } else + rc = count; + + spin_unlock(&ibmvtpm->rtce_lock); + return rc; +} + +static void tpm_ibmvtpm_cancel(struct tpm_chip *chip) +{ + return; +} + +static u8 tpm_ibmvtpm_status(struct tpm_chip *chip) +{ + return 0; +} + +/** + * ibmvtpm_crq_get_rtce_size - Send a CRQ request to get rtce size + * @ibmvtpm: vtpm device struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int ibmvtpm_crq_get_rtce_size(struct ibmvtpm_dev *ibmvtpm) +{ + struct ibmvtpm_crq crq; + u64 *buf = (u64 *) &crq; + int rc; + + crq.valid = (u8)IBMVTPM_VALID_CMD; + crq.msg = (u8)VTPM_GET_RTCE_BUFFER_SIZE; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "ibmvtpm_crq_get_rtce_size failed rc=%d\n", rc); + + return rc; +} + +/** + * ibmvtpm_crq_get_version - Send a CRQ request to get vtpm version + * - Note that this is vtpm version and not tpm version + * @ibmvtpm: vtpm device struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int ibmvtpm_crq_get_version(struct ibmvtpm_dev *ibmvtpm) +{ + struct ibmvtpm_crq crq; + u64 *buf = (u64 *) &crq; + int rc; + + crq.valid = (u8)IBMVTPM_VALID_CMD; + crq.msg = (u8)VTPM_GET_VERSION; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "ibmvtpm_crq_get_version failed rc=%d\n", rc); + + return rc; +} + +/** + * ibmvtpm_crq_send_init_complete - Send a CRQ initialize complete message + * @ibmvtpm: vtpm device struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int ibmvtpm_crq_send_init_complete(struct ibmvtpm_dev *ibmvtpm) +{ + int rc; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, INIT_CRQ_COMP_CMD, 0); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "ibmvtpm_crq_send_init_complete failed rc=%d\n", rc); + + return rc; +} + +/** + * ibmvtpm_crq_send_init - Send a CRQ initialize message + * @ibmvtpm: vtpm device struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm) +{ + int rc; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, INIT_CRQ_CMD, 0); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "ibmvtpm_crq_send_init failed rc=%d\n", rc); + + return rc; +} + +/** + * tpm_ibmvtpm_remove - ibm vtpm remove entry point + * @vdev: vio device struct + * + * Return value: + * 0 + */ +static int tpm_ibmvtpm_remove(struct vio_dev *vdev) +{ + struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev); + int rc = 0; + + free_irq(vdev->irq, ibmvtpm); + + do { + if (rc) + msleep(100); + rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); + } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); + + dma_unmap_single(ibmvtpm->dev, ibmvtpm->crq_dma_handle, + CRQ_RES_BUF_SIZE, DMA_BIDIRECTIONAL); + free_page((unsigned long)ibmvtpm->crq_queue.crq_addr); + + if (ibmvtpm->rtce_buf) { + dma_unmap_single(ibmvtpm->dev, ibmvtpm->rtce_dma_handle, + ibmvtpm->rtce_size, DMA_BIDIRECTIONAL); + kfree(ibmvtpm->rtce_buf); + } + + tpm_remove_hardware(ibmvtpm->dev); + + kfree(ibmvtpm); + + return 0; +} + +/** + * tpm_ibmvtpm_get_desired_dma - Get DMA size needed by this driver + * @vdev: vio device struct + * + * Return value: + * Number of bytes the driver needs to DMA map + */ +static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev) +{ + struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev); + return CRQ_RES_BUF_SIZE + ibmvtpm->rtce_size; +} + +/** + * tpm_ibmvtpm_suspend - Suspend + * @dev: device struct + * + * Return value: + * 0 + */ +static int tpm_ibmvtpm_suspend(struct device *dev) +{ + struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev); + struct ibmvtpm_crq crq; + u64 *buf = (u64 *) &crq; + int rc = 0; + + crq.valid = (u8)IBMVTPM_VALID_CMD; + crq.msg = (u8)VTPM_PREPARE_TO_SUSPEND; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "tpm_ibmvtpm_suspend failed rc=%d\n", rc); + + return rc; +} + +/** + * ibmvtpm_reset_crq - Reset CRQ + * @ibmvtpm: ibm vtpm struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm) +{ + int rc = 0; + + do { + if (rc) + msleep(100); + rc = plpar_hcall_norets(H_FREE_CRQ, + ibmvtpm->vdev->unit_address); + } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); + + memset(ibmvtpm->crq_queue.crq_addr, 0, CRQ_RES_BUF_SIZE); + ibmvtpm->crq_queue.index = 0; + + return plpar_hcall_norets(H_REG_CRQ, ibmvtpm->vdev->unit_address, + ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE); +} + +/** + * tpm_ibmvtpm_resume - Resume from suspend + * @dev: device struct + * + * Return value: + * 0 + */ +static int tpm_ibmvtpm_resume(struct device *dev) +{ + struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev); + int rc = 0; + + do { + if (rc) + msleep(100); + rc = plpar_hcall_norets(H_ENABLE_CRQ, + ibmvtpm->vdev->unit_address); + } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc)); + + if (rc) { + dev_err(dev, "Error enabling ibmvtpm rc=%d\n", rc); + return rc; + } + + rc = vio_enable_interrupts(ibmvtpm->vdev); + if (rc) { + dev_err(dev, "Error vio_enable_interrupts rc=%d\n", rc); + return rc; + } + + rc = ibmvtpm_crq_send_init(ibmvtpm); + if (rc) + dev_err(dev, "Error send_init rc=%d\n", rc); + + return rc; +} + +static bool tpm_ibmvtpm_req_canceled(struct tpm_chip *chip, u8 status) +{ + return (status == 0); +} + +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 = tpm_ibmvtpm_req_canceled, +}; + +static const struct dev_pm_ops tpm_ibmvtpm_pm_ops = { + .suspend = tpm_ibmvtpm_suspend, + .resume = tpm_ibmvtpm_resume, +}; + +/** + * ibmvtpm_crq_get_next - Get next responded crq + * @ibmvtpm vtpm device struct + * + * Return value: + * vtpm crq pointer + */ +static struct ibmvtpm_crq *ibmvtpm_crq_get_next(struct ibmvtpm_dev *ibmvtpm) +{ + struct ibmvtpm_crq_queue *crq_q = &ibmvtpm->crq_queue; + struct ibmvtpm_crq *crq = &crq_q->crq_addr[crq_q->index]; + + if (crq->valid & VTPM_MSG_RES) { + if (++crq_q->index == crq_q->num_entry) + crq_q->index = 0; + smp_rmb(); + } else + crq = NULL; + return crq; +} + +/** + * ibmvtpm_crq_process - Process responded crq + * @crq crq to be processed + * @ibmvtpm vtpm device struct + * + * Return value: + * Nothing + */ +static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq, + struct ibmvtpm_dev *ibmvtpm) +{ + int rc = 0; + + switch (crq->valid) { + case VALID_INIT_CRQ: + switch (crq->msg) { + case INIT_CRQ_RES: + dev_info(ibmvtpm->dev, "CRQ initialized\n"); + rc = ibmvtpm_crq_send_init_complete(ibmvtpm); + if (rc) + dev_err(ibmvtpm->dev, "Unable to send CRQ init complete rc=%d\n", rc); + return; + case INIT_CRQ_COMP_RES: + dev_info(ibmvtpm->dev, + "CRQ initialization completed\n"); + return; + default: + dev_err(ibmvtpm->dev, "Unknown crq message type: %d\n", crq->msg); + return; + } + case IBMVTPM_VALID_CMD: + switch (crq->msg) { + case VTPM_GET_RTCE_BUFFER_SIZE_RES: + if (crq->len <= 0) { + dev_err(ibmvtpm->dev, "Invalid rtce size\n"); + return; + } + ibmvtpm->rtce_size = crq->len; + ibmvtpm->rtce_buf = kmalloc(ibmvtpm->rtce_size, + GFP_KERNEL); + if (!ibmvtpm->rtce_buf) { + dev_err(ibmvtpm->dev, "Failed to allocate memory for rtce buffer\n"); + return; + } + + ibmvtpm->rtce_dma_handle = dma_map_single(ibmvtpm->dev, + ibmvtpm->rtce_buf, ibmvtpm->rtce_size, + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(ibmvtpm->dev, + ibmvtpm->rtce_dma_handle)) { + kfree(ibmvtpm->rtce_buf); + ibmvtpm->rtce_buf = NULL; + dev_err(ibmvtpm->dev, "Failed to dma map rtce buffer\n"); + } + + return; + case VTPM_GET_VERSION_RES: + ibmvtpm->vtpm_version = crq->data; + return; + case VTPM_TPM_COMMAND_RES: + /* len of the data in rtce buffer */ + ibmvtpm->res_len = crq->len; + wake_up_interruptible(&ibmvtpm->wq); + return; + default: + return; + } + } + return; +} + +/** + * ibmvtpm_interrupt - Interrupt handler + * @irq: irq number to handle + * @vtpm_instance: vtpm that received interrupt + * + * Returns: + * IRQ_HANDLED + **/ +static irqreturn_t ibmvtpm_interrupt(int irq, void *vtpm_instance) +{ + struct ibmvtpm_dev *ibmvtpm = (struct ibmvtpm_dev *) vtpm_instance; + struct ibmvtpm_crq *crq; + + /* while loop is needed for initial setup (get version and + * get rtce_size). There should be only one tpm request at any + * given time. + */ + while ((crq = ibmvtpm_crq_get_next(ibmvtpm)) != NULL) { + ibmvtpm_crq_process(crq, ibmvtpm); + crq->valid = 0; + smp_wmb(); + } + + return IRQ_HANDLED; +} + +/** + * tpm_ibmvtpm_probe - ibm vtpm initialize entry point + * @vio_dev: vio device struct + * @id: vio device id struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev, + const struct vio_device_id *id) +{ + struct ibmvtpm_dev *ibmvtpm; + struct device *dev = &vio_dev->dev; + struct ibmvtpm_crq_queue *crq_q; + struct tpm_chip *chip; + int rc = -ENOMEM, rc1; + + chip = tpm_register_hardware(dev, &tpm_ibmvtpm); + if (!chip) { + dev_err(dev, "tpm_register_hardware failed\n"); + return -ENODEV; + } + + ibmvtpm = kzalloc(sizeof(struct ibmvtpm_dev), GFP_KERNEL); + if (!ibmvtpm) { + dev_err(dev, "kzalloc for ibmvtpm failed\n"); + goto cleanup; + } + + crq_q = &ibmvtpm->crq_queue; + crq_q->crq_addr = (struct ibmvtpm_crq *)get_zeroed_page(GFP_KERNEL); + if (!crq_q->crq_addr) { + dev_err(dev, "Unable to allocate memory for crq_addr\n"); + goto cleanup; + } + + crq_q->num_entry = CRQ_RES_BUF_SIZE / sizeof(*crq_q->crq_addr); + ibmvtpm->crq_dma_handle = dma_map_single(dev, crq_q->crq_addr, + CRQ_RES_BUF_SIZE, + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(dev, ibmvtpm->crq_dma_handle)) { + dev_err(dev, "dma mapping failed\n"); + goto cleanup; + } + + rc = plpar_hcall_norets(H_REG_CRQ, vio_dev->unit_address, + ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE); + if (rc == H_RESOURCE) + rc = ibmvtpm_reset_crq(ibmvtpm); + + if (rc) { + dev_err(dev, "Unable to register CRQ rc=%d\n", rc); + goto reg_crq_cleanup; + } + + rc = request_irq(vio_dev->irq, ibmvtpm_interrupt, 0, + tpm_ibmvtpm_driver_name, ibmvtpm); + if (rc) { + dev_err(dev, "Error %d register irq 0x%x\n", rc, vio_dev->irq); + goto init_irq_cleanup; + } + + rc = vio_enable_interrupts(vio_dev); + if (rc) { + dev_err(dev, "Error %d enabling interrupts\n", rc); + goto init_irq_cleanup; + } + + init_waitqueue_head(&ibmvtpm->wq); + + crq_q->index = 0; + + ibmvtpm->dev = dev; + ibmvtpm->vdev = vio_dev; + TPM_VPRIV(chip) = (void *)ibmvtpm; + + spin_lock_init(&ibmvtpm->rtce_lock); + + rc = ibmvtpm_crq_send_init(ibmvtpm); + if (rc) + goto init_irq_cleanup; + + rc = ibmvtpm_crq_get_version(ibmvtpm); + if (rc) + goto init_irq_cleanup; + + rc = ibmvtpm_crq_get_rtce_size(ibmvtpm); + if (rc) + goto init_irq_cleanup; + + return rc; +init_irq_cleanup: + do { + rc1 = plpar_hcall_norets(H_FREE_CRQ, vio_dev->unit_address); + } while (rc1 == H_BUSY || H_IS_LONG_BUSY(rc1)); +reg_crq_cleanup: + dma_unmap_single(dev, ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE, + DMA_BIDIRECTIONAL); +cleanup: + if (ibmvtpm) { + if (crq_q->crq_addr) + free_page((unsigned long)crq_q->crq_addr); + kfree(ibmvtpm); + } + + tpm_remove_hardware(dev); + + return rc; +} + +static struct vio_driver ibmvtpm_driver = { + .id_table = tpm_ibmvtpm_device_table, + .probe = tpm_ibmvtpm_probe, + .remove = tpm_ibmvtpm_remove, + .get_desired_dma = tpm_ibmvtpm_get_desired_dma, + .name = tpm_ibmvtpm_driver_name, + .pm = &tpm_ibmvtpm_pm_ops, +}; + +/** + * ibmvtpm_module_init - Initialize ibm vtpm module + * + * Return value: + * 0 -Success + * Non-zero - Failure + */ +static int __init ibmvtpm_module_init(void) +{ + return vio_register_driver(&ibmvtpm_driver); +} + +/** + * ibmvtpm_module_exit - Teardown ibm vtpm module + * + * Return value: + * Nothing + */ +static void __exit ibmvtpm_module_exit(void) +{ + vio_unregister_driver(&ibmvtpm_driver); +} + +module_init(ibmvtpm_module_init); +module_exit(ibmvtpm_module_exit); + +MODULE_AUTHOR("adlai@us.ibm.com"); +MODULE_DESCRIPTION("IBM vTPM Driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/tpm_ibmvtpm.h b/drivers/char/tpm/tpm_ibmvtpm.h new file mode 100644 index 00000000000..bd82a791f99 --- /dev/null +++ b/drivers/char/tpm/tpm_ibmvtpm.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2012 IBM Corporation + * + * Author: Ashley Lai <adlai@us.ibm.com> + * + * Maintained by: <tpmdd-devel@lists.sourceforge.net> + * + * Device driver for TCG/TCPA TPM (trusted platform module). + * 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. + * + */ + +#ifndef __TPM_IBMVTPM_H__ +#define __TPM_IBMVTPM_H__ + +/* vTPM Message Format 1 */ +struct ibmvtpm_crq { + u8 valid; + u8 msg; + u16 len; + u32 data; + u64 reserved; +} __attribute__((packed, aligned(8))); + +struct ibmvtpm_crq_queue { + struct ibmvtpm_crq *crq_addr; + u32 index; + u32 num_entry; +}; + +struct ibmvtpm_dev { + struct device *dev; + struct vio_dev *vdev; + struct ibmvtpm_crq_queue crq_queue; + dma_addr_t crq_dma_handle; + u32 rtce_size; + void __iomem *rtce_buf; + dma_addr_t rtce_dma_handle; + spinlock_t rtce_lock; + wait_queue_head_t wq; + u16 res_len; + u32 vtpm_version; +}; + +#define CRQ_RES_BUF_SIZE PAGE_SIZE + +/* Initialize CRQ */ +#define INIT_CRQ_CMD 0xC001000000000000LL /* Init cmd */ +#define INIT_CRQ_COMP_CMD 0xC002000000000000LL /* Init complete cmd */ +#define INIT_CRQ_RES 0x01 /* Init respond */ +#define INIT_CRQ_COMP_RES 0x02 /* Init complete respond */ +#define VALID_INIT_CRQ 0xC0 /* Valid command for init crq */ + +/* vTPM CRQ response is the message type | 0x80 */ +#define VTPM_MSG_RES 0x80 +#define IBMVTPM_VALID_CMD 0x80 + +/* vTPM CRQ message types */ +#define VTPM_GET_VERSION 0x01 +#define VTPM_GET_VERSION_RES (0x01 | VTPM_MSG_RES) + +#define VTPM_TPM_COMMAND 0x02 +#define VTPM_TPM_COMMAND_RES (0x02 | VTPM_MSG_RES) + +#define VTPM_GET_RTCE_BUFFER_SIZE 0x03 +#define VTPM_GET_RTCE_BUFFER_SIZE_RES (0x03 | VTPM_MSG_RES) + +#define VTPM_PREPARE_TO_SUSPEND 0x04 +#define VTPM_PREPARE_TO_SUSPEND_RES (0x04 | VTPM_MSG_RES) + +#endif diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index 76da32e11f1..dc0a2554034 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -4,8 +4,8 @@ * SLD 9630 TT 1.1 and SLB 9635 TT 1.2 Trusted Platform Module * Specifications at www.trustedcomputinggroup.org * - * Copyright (C) 2005, Marcel Selhorst <m.selhorst@sirrix.com> - * Sirrix AG - security technologies, http://www.sirrix.com and + * Copyright (C) 2005, Marcel Selhorst <tpmdd@selhorst.net> + * Sirrix AG - security technologies <tpmdd@sirrix.com> and * Applied Data Security Group, Ruhr-University Bochum, Germany * Project-Homepage: http://www.trust.rub.de/projects/linux-device-driver-infineon-tpm/ * @@ -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[] = { @@ -415,7 +389,7 @@ static const struct pnp_device_id tpm_inf_pnp_tbl[] = { MODULE_DEVICE_TABLE(pnp, tpm_inf_pnp_tbl); -static int __devinit tpm_inf_pnp_probe(struct pnp_dev *dev, +static int tpm_inf_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { int rc = 0; @@ -594,7 +568,7 @@ err_last: return rc; } -static __devexit void tpm_inf_pnp_remove(struct pnp_dev *dev) +static void tpm_inf_pnp_remove(struct pnp_dev *dev) { struct tpm_chip *chip = pnp_get_drvdata(dev); @@ -655,7 +629,7 @@ static struct pnp_driver tpm_inf_pnp_driver = { .probe = tpm_inf_pnp_probe, .suspend = tpm_inf_pnp_suspend, .resume = tpm_inf_pnp_resume, - .remove = __devexit_p(tpm_inf_pnp_remove) + .remove = tpm_inf_pnp_remove }; static int __init init_inf(void) @@ -671,7 +645,7 @@ static void __exit cleanup_inf(void) module_init(init_inf); module_exit(cleanup_inf); -MODULE_AUTHOR("Marcel Selhorst <m.selhorst@sirrix.com>"); +MODULE_AUTHOR("Marcel Selhorst <tpmdd@sirrix.com>"); MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2"); MODULE_VERSION("1.9.2"); MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 4d2464871ad..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; @@ -274,22 +253,13 @@ static void tpm_nsc_remove(struct device *dev) } } -static int tpm_nsc_suspend(struct platform_device *dev, pm_message_t msg) -{ - return tpm_pm_suspend(&dev->dev, msg); -} - -static int tpm_nsc_resume(struct platform_device *dev) -{ - return tpm_pm_resume(&dev->dev); -} +static SIMPLE_DEV_PM_OPS(tpm_nsc_pm, tpm_pm_suspend, tpm_pm_resume); static struct platform_driver nsc_drv = { - .suspend = tpm_nsc_suspend, - .resume = tpm_nsc_resume, .driver = { .name = "tpm_nsc", .owner = THIS_MODULE, + .pm = &tpm_nsc_pm, }, }; diff --git a/drivers/char/tpm/tpm_of.c b/drivers/char/tpm/tpm_of.c new file mode 100644 index 00000000000..98ba2bd1a35 --- /dev/null +++ b/drivers/char/tpm/tpm_of.c @@ -0,0 +1,73 @@ +/* + * Copyright 2012 IBM Corporation + * + * Author: Ashley Lai <adlai@us.ibm.com> + * + * Maintained by: <tpmdd-devel@lists.sourceforge.net> + * + * Read the event log created by the firmware on PPC64 + * + * 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. + * + */ + +#include <linux/slab.h> +#include <linux/of.h> + +#include "tpm.h" +#include "tpm_eventlog.h" + +int read_log(struct tpm_bios_log *log) +{ + struct device_node *np; + const u32 *sizep; + const __be64 *basep; + + if (log->bios_event_log != NULL) { + pr_err("%s: ERROR - Eventlog already initialized\n", __func__); + return -EFAULT; + } + + np = of_find_node_by_name(NULL, "ibm,vtpm"); + if (!np) { + pr_err("%s: ERROR - IBMVTPM not supported\n", __func__); + return -ENODEV; + } + + sizep = of_get_property(np, "linux,sml-size", NULL); + if (sizep == NULL) { + pr_err("%s: ERROR - SML size not found\n", __func__); + goto cleanup_eio; + } + if (*sizep == 0) { + pr_err("%s: ERROR - event log area empty\n", __func__); + goto cleanup_eio; + } + + basep = of_get_property(np, "linux,sml-base", NULL); + if (basep == NULL) { + pr_err(KERN_ERR "%s: ERROR - SML not found\n", __func__); + goto cleanup_eio; + } + + of_node_put(np); + log->bios_event_log = kmalloc(*sizep, GFP_KERNEL); + if (!log->bios_event_log) { + pr_err("%s: ERROR - Not enough memory for BIOS measurements\n", + __func__); + return -ENOMEM; + } + + log->bios_event_log_end = log->bios_event_log + *sizep; + + memcpy(log->bios_event_log, __va(be64_to_cpup(basep)), *sizep); + + return 0; + +cleanup_eio: + of_node_put(np); + return -EIO; +} diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c new file mode 100644 index 00000000000..61dcc8011ec --- /dev/null +++ b/drivers/char/tpm/tpm_ppi.c @@ -0,0 +1,338 @@ +#include <linux/acpi.h> +#include "tpm.h" + +#define TPM_PPI_REVISION_ID 1 +#define TPM_PPI_FN_VERSION 1 +#define TPM_PPI_FN_SUBREQ 2 +#define TPM_PPI_FN_GETREQ 3 +#define TPM_PPI_FN_GETACT 4 +#define TPM_PPI_FN_GETRSP 5 +#define TPM_PPI_FN_SUBREQ2 7 +#define TPM_PPI_FN_GETOPR 8 +#define PPI_TPM_REQ_MAX 22 +#define PPI_VS_REQ_START 128 +#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) +{ + 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_value = handle; + + return AE_CTRL_TERMINATE; +} + +static inline union acpi_object * +tpm_eval_dsm(int func, acpi_object_type type, union acpi_object *argv4) +{ + 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) +{ + 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) +{ + ssize_t size = -EINVAL; + union acpi_object *obj; + + obj = tpm_eval_dsm(TPM_PPI_FN_GETREQ, ACPI_TYPE_PACKAGE, NULL); + if (!obj) + return -ENXIO; + + /* + * 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. + */ + 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 + size = scnprintf(buf, PAGE_SIZE, "%llu\n", + obj->package.elements[1].integer.value); + } + + 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) +{ + 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); + + /* + * 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 (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(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 { + 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); + } + + if (ret == 0) + 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) +{ + u32 ret; + 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", + }; + + /* + * 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(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); + } + + 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]); + return status; +} + +static ssize_t tpm_show_ppi_response(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + 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; + + /* + * parameter output.pointer should be of package type, including + * 3 integers. The first means function return code, the second means + * most recent TPM operation request, and the last means response to + * 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 = 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[0].integer.value) { + status = -EFAULT; + goto cleanup; + } + + 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 (res == 0xFFFFFFF0) + status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, + "0xFFFFFFF0: User Abort"); + else if (res == 0xFFFFFFF1) + status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, + "0xFFFFFFF1: BIOS Failure"); + else if (res >= 1 && res <= 0x00000FFF) + status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", + req, res, "Corresponding TPM error"); + else + status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", + req, res, "Error"); + } else { + status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n", + req, "No Recent Request"); + } + +cleanup: + ACPI_FREE(obj); + return status; +} + +static ssize_t show_ppi_operations(char *buf, u32 start, u32 end) +{ + int i; + u32 ret; + 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", + }; + + if (!acpi_check_dsm(tpm_ppi_handle, tpm_ppi_uuid, TPM_PPI_REVISION_ID, + 1 << TPM_PPI_FN_GETOPR)) + return -EPERM; + + tmp.integer.type = ACPI_TYPE_INTEGER; + for (i = start; i <= end; i++) { + 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); + } + + if (ret > 0 && ret < ARRAY_SIZE(info)) + str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n", + i, ret, info[ret]); + } + + return str - buf; +} + +static ssize_t tpm_show_ppi_tcg_operations(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return show_ppi_operations(buf, 0, PPI_TPM_REQ_MAX); +} + +static ssize_t tpm_show_ppi_vs_operations(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return show_ppi_operations(buf, PPI_VS_REQ_START, PPI_VS_REQ_END); +} + +static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL); +static DEVICE_ATTR(request, S_IRUGO | S_IWUSR | S_IWGRP, + tpm_show_ppi_request, tpm_store_ppi_request); +static DEVICE_ATTR(transition_action, S_IRUGO, + tpm_show_ppi_transition_action, NULL); +static DEVICE_ATTR(response, S_IRUGO, tpm_show_ppi_response, NULL); +static DEVICE_ATTR(tcg_operations, S_IRUGO, tpm_show_ppi_tcg_operations, NULL); +static DEVICE_ATTR(vs_operations, S_IRUGO, tpm_show_ppi_vs_operations, NULL); + +static struct attribute *ppi_attrs[] = { + &dev_attr_version.attr, + &dev_attr_request.attr, + &dev_attr_transition_action.attr, + &dev_attr_response.attr, + &dev_attr_tcg_operations.attr, + &dev_attr_vs_operations.attr, NULL, +}; +static struct attribute_group ppi_attr_grp = { + .name = "ppi", + .attrs = ppi_attrs +}; + +int tpm_add_ppi(struct kobject *parent) +{ + /* 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; +} + +void tpm_remove_ppi(struct kobject *parent) +{ + if (tpm_ppi_handle) + sysfs_remove_group(parent, &ppi_attr_grp); +} diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index a1748621111..a9ed2270c25 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -76,7 +76,7 @@ enum tis_defaults { #define TPM_RID(l) (0x0F04 | ((l) << 12)) static LIST_HEAD(tis_chips); -static DEFINE_SPINLOCK(tis_lock); +static DEFINE_MUTEX(tis_lock); #if defined(CONFIG_PNP) && defined(CONFIG_ACPI) static int is_itpm(struct pnp_dev *dev) @@ -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; } @@ -367,9 +386,14 @@ static int probe_itpm(struct tpm_chip *chip) 0x00, 0x00, 0x00, 0xf1 }; size_t len = sizeof(cmd_getticks); - int rem_itpm = itpm; + bool rem_itpm = itpm; + u16 vendor = ioread16(chip->vendor.iobase + TPM_DID_VID(0)); - itpm = 0; + /* probe only iTPMS */ + if (vendor != TPM_VID_INTEL) + return 0; + + itpm = false; rc = tpm_tis_send_data(chip, cmd_getticks, len); if (rc == 0) @@ -378,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) { @@ -390,63 +414,32 @@ static int probe_itpm(struct tpm_chip *chip) out: itpm = rem_itpm; tpm_tis_ready(chip); - /* some TPMs need a break here otherwise they will not work - * correctly on the immediately subsequent command */ - msleep(chip->vendor.timeout_b); release_locality(chip, chip->vendor.locality, 0); 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) @@ -500,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"); @@ -508,7 +501,7 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, resource_size_t len, unsigned int irq) { u32 vendor, intfcaps, intmask; - int rc, i, irq_s, irq_e; + int rc, i, irq_s, irq_e, probe; struct tpm_chip *chip; if (!(chip = tpm_register_hardware(dev, &tpm_tis))) @@ -526,23 +519,30 @@ 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", vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0))); if (!itpm) { - itpm = probe_itpm(chip); - if (itpm < 0) { + probe = probe_itpm(chip); + if (probe < 0) { rc = -ENODEV; goto out_err; } + itpm = !!probe; } if (itpm) @@ -689,9 +689,9 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, } INIT_LIST_HEAD(&chip->vendor.list); - spin_lock(&tis_lock); + mutex_lock(&tis_lock); list_add(&chip->vendor.list, &tis_chips); - spin_unlock(&tis_lock); + mutex_unlock(&tis_lock); return 0; @@ -702,6 +702,7 @@ out_err: return rc; } +#ifdef CONFIG_PM_SLEEP static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) { u32 intmask; @@ -723,9 +724,26 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) 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 __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, +static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev, const struct pnp_device_id *pnp_id) { resource_size_t start, len; @@ -737,35 +755,15 @@ static int __devinit 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, msg); -} - -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[] __devinitdata = { +static struct pnp_device_id tpm_pnp_tbl[] = { {"PNP0C31", 0}, /* TPM */ {"ATM1200", 0}, /* Atmel */ {"IFX0102", 0}, /* Infineon */ @@ -779,7 +777,7 @@ static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = { }; MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl); -static __devexit void tpm_tis_pnp_remove(struct pnp_dev *dev) +static void tpm_tis_pnp_remove(struct pnp_dev *dev) { struct tpm_chip *chip = pnp_get_drvdata(dev); @@ -793,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 @@ -803,27 +802,13 @@ module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id, sizeof(tpm_pnp_tbl[TIS_HID_USR_IDX].id), 0444); MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe"); #endif -static int tpm_tis_suspend(struct platform_device *dev, pm_message_t msg) -{ - return tpm_pm_suspend(&dev->dev, msg); -} - -static int tpm_tis_resume(struct platform_device *dev) -{ - struct tpm_chip *chip = dev_get_drvdata(&dev->dev); - if (chip->vendor.irq) - tpm_tis_reenable_interrupts(chip); - - return tpm_pm_resume(&dev->dev); -} static struct platform_driver tis_drv = { .driver = { .name = "tpm_tis", .owner = THIS_MODULE, + .pm = &tpm_tis_pm, }, - .suspend = tpm_tis_suspend, - .resume = tpm_tis_resume, }; static struct platform_device *pdev; @@ -842,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; } @@ -855,7 +847,7 @@ static void __exit cleanup_tis(void) { struct tpm_vendor_specific *i, *j; struct tpm_chip *chip; - spin_lock(&tis_lock); + mutex_lock(&tis_lock); list_for_each_entry_safe(i, j, &tis_chips, list) { chip = to_tpm_chip(i); tpm_remove_hardware(chip->dev); @@ -871,7 +863,7 @@ static void __exit cleanup_tis(void) iounmap(i->iobase); list_del(&i->list); } - spin_unlock(&tis_lock); + mutex_unlock(&tis_lock); #ifdef CONFIG_PNP if (!force) { pnp_unregister_driver(&tis_pnp_driver); 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 eedd5474850..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; @@ -67,7 +67,7 @@ static int tpk_printk(const unsigned char *buf, int count) tmp[tpk_curr + 1] = '\0'; printk(KERN_INFO "%s%s", tpk_tag, tmp); tpk_curr = 0; - if (buf[i + 1] == '\n') + if ((i + 1) < count && buf[i + 1] == '\n') i++; break; case '\n': @@ -178,24 +178,28 @@ static struct tty_driver *ttyprintk_driver; static int __init ttyprintk_init(void) { int ret = -ENOMEM; - void *rp; - ttyprintk_driver = alloc_tty_driver(1); - if (!ttyprintk_driver) - return ret; + mutex_init(&tpk_port.port_write_mutex); + + ttyprintk_driver = tty_alloc_driver(1, + TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_UNNUMBERED_NODE); + if (IS_ERR(ttyprintk_driver)) + return PTR_ERR(ttyprintk_driver); + + tty_port_init(&tpk_port.port); + tpk_port.port.ops = &null_ops; - ttyprintk_driver->owner = THIS_MODULE; ttyprintk_driver->driver_name = "ttyprintk"; ttyprintk_driver->name = "ttyprintk"; ttyprintk_driver->major = TTYAUX_MAJOR; ttyprintk_driver->minor_start = 3; - ttyprintk_driver->num = 1; ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE; ttyprintk_driver->init_termios = tty_std_termios; ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET; - ttyprintk_driver->flags = TTY_DRIVER_RESET_TERMIOS | - TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; tty_set_operations(ttyprintk_driver, &ttyprintk_ops); + tty_port_link_device(&tpk_port.port, ttyprintk_driver, 0); ret = tty_register_driver(ttyprintk_driver); if (ret < 0) { @@ -203,24 +207,22 @@ static int __init ttyprintk_init(void) goto error; } - /* create our unnumbered device */ - rp = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 3), NULL, - ttyprintk_driver->name); - if (IS_ERR(rp)) { - printk(KERN_ERR "Couldn't create ttyprintk device\n"); - ret = PTR_ERR(rp); - goto error; - } - - tty_port_init(&tpk_port.port); - tpk_port.port.ops = &null_ops; - mutex_init(&tpk_port.port_write_mutex); - return 0; error: put_tty_driver(ttyprintk_driver); - ttyprintk_driver = NULL; + tty_port_destroy(&tpk_port.port); 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/viotape.c b/drivers/char/viotape.c deleted file mode 100644 index ad6e64a2912..00000000000 --- a/drivers/char/viotape.c +++ /dev/null @@ -1,1041 +0,0 @@ -/* -*- linux-c -*- - * drivers/char/viotape.c - * - * iSeries Virtual Tape - * - * Authors: Dave Boutcher <boutcher@us.ibm.com> - * Ryan Arnold <ryanarn@us.ibm.com> - * Colin Devilbiss <devilbis@us.ibm.com> - * Stephen Rothwell - * - * (C) Copyright 2000-2004 IBM Corporation - * - * 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) anyu 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * This routine provides access to tape drives owned and managed by an OS/400 - * partition running on the same box as this Linux partition. - * - * All tape operations are performed by sending messages back and forth to - * the OS/400 partition. The format of the messages is defined in - * iseries/vio.h - */ -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/wait.h> -#include <linux/spinlock.h> -#include <linux/mtio.h> -#include <linux/device.h> -#include <linux/dma-mapping.h> -#include <linux/fs.h> -#include <linux/cdev.h> -#include <linux/major.h> -#include <linux/completion.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/mutex.h> -#include <linux/slab.h> - -#include <asm/uaccess.h> -#include <asm/ioctls.h> -#include <asm/firmware.h> -#include <asm/vio.h> -#include <asm/iseries/vio.h> -#include <asm/iseries/hv_lp_event.h> -#include <asm/iseries/hv_call_event.h> -#include <asm/iseries/hv_lp_config.h> - -#define VIOTAPE_VERSION "1.2" -#define VIOTAPE_MAXREQ 1 - -#define VIOTAPE_KERN_WARN KERN_WARNING "viotape: " -#define VIOTAPE_KERN_INFO KERN_INFO "viotape: " - -static DEFINE_MUTEX(proc_viotape_mutex); -static int viotape_numdev; - -/* - * The minor number follows the conventions of the SCSI tape drives. The - * rewind and mode are encoded in the minor #. We use this struct to break - * them out - */ -struct viot_devinfo_struct { - int devno; - int mode; - int rewind; -}; - -#define VIOTAPOP_RESET 0 -#define VIOTAPOP_FSF 1 -#define VIOTAPOP_BSF 2 -#define VIOTAPOP_FSR 3 -#define VIOTAPOP_BSR 4 -#define VIOTAPOP_WEOF 5 -#define VIOTAPOP_REW 6 -#define VIOTAPOP_NOP 7 -#define VIOTAPOP_EOM 8 -#define VIOTAPOP_ERASE 9 -#define VIOTAPOP_SETBLK 10 -#define VIOTAPOP_SETDENSITY 11 -#define VIOTAPOP_SETPOS 12 -#define VIOTAPOP_GETPOS 13 -#define VIOTAPOP_SETPART 14 -#define VIOTAPOP_UNLOAD 15 - -enum viotaperc { - viotape_InvalidRange = 0x0601, - viotape_InvalidToken = 0x0602, - viotape_DMAError = 0x0603, - viotape_UseError = 0x0604, - viotape_ReleaseError = 0x0605, - viotape_InvalidTape = 0x0606, - viotape_InvalidOp = 0x0607, - viotape_TapeErr = 0x0608, - - viotape_AllocTimedOut = 0x0640, - viotape_BOTEnc = 0x0641, - viotape_BlankTape = 0x0642, - viotape_BufferEmpty = 0x0643, - viotape_CleanCartFound = 0x0644, - viotape_CmdNotAllowed = 0x0645, - viotape_CmdNotSupported = 0x0646, - viotape_DataCheck = 0x0647, - viotape_DecompressErr = 0x0648, - viotape_DeviceTimeout = 0x0649, - viotape_DeviceUnavail = 0x064a, - viotape_DeviceBusy = 0x064b, - viotape_EndOfMedia = 0x064c, - viotape_EndOfTape = 0x064d, - viotape_EquipCheck = 0x064e, - viotape_InsufficientRs = 0x064f, - viotape_InvalidLogBlk = 0x0650, - viotape_LengthError = 0x0651, - viotape_LibDoorOpen = 0x0652, - viotape_LoadFailure = 0x0653, - viotape_NotCapable = 0x0654, - viotape_NotOperational = 0x0655, - viotape_NotReady = 0x0656, - viotape_OpCancelled = 0x0657, - viotape_PhyLinkErr = 0x0658, - viotape_RdyNotBOT = 0x0659, - viotape_TapeMark = 0x065a, - viotape_WriteProt = 0x065b -}; - -static const struct vio_error_entry viotape_err_table[] = { - { viotape_InvalidRange, EIO, "Internal error" }, - { viotape_InvalidToken, EIO, "Internal error" }, - { viotape_DMAError, EIO, "DMA error" }, - { viotape_UseError, EIO, "Internal error" }, - { viotape_ReleaseError, EIO, "Internal error" }, - { viotape_InvalidTape, EIO, "Invalid tape device" }, - { viotape_InvalidOp, EIO, "Invalid operation" }, - { viotape_TapeErr, EIO, "Tape error" }, - { viotape_AllocTimedOut, EBUSY, "Allocate timed out" }, - { viotape_BOTEnc, EIO, "Beginning of tape encountered" }, - { viotape_BlankTape, EIO, "Blank tape" }, - { viotape_BufferEmpty, EIO, "Buffer empty" }, - { viotape_CleanCartFound, ENOMEDIUM, "Cleaning cartridge found" }, - { viotape_CmdNotAllowed, EIO, "Command not allowed" }, - { viotape_CmdNotSupported, EIO, "Command not supported" }, - { viotape_DataCheck, EIO, "Data check" }, - { viotape_DecompressErr, EIO, "Decompression error" }, - { viotape_DeviceTimeout, EBUSY, "Device timeout" }, - { viotape_DeviceUnavail, EIO, "Device unavailable" }, - { viotape_DeviceBusy, EBUSY, "Device busy" }, - { viotape_EndOfMedia, ENOSPC, "End of media" }, - { viotape_EndOfTape, ENOSPC, "End of tape" }, - { viotape_EquipCheck, EIO, "Equipment check" }, - { viotape_InsufficientRs, EOVERFLOW, "Insufficient tape resources" }, - { viotape_InvalidLogBlk, EIO, "Invalid logical block location" }, - { viotape_LengthError, EOVERFLOW, "Length error" }, - { viotape_LibDoorOpen, EBUSY, "Door open" }, - { viotape_LoadFailure, ENOMEDIUM, "Load failure" }, - { viotape_NotCapable, EIO, "Not capable" }, - { viotape_NotOperational, EIO, "Not operational" }, - { viotape_NotReady, EIO, "Not ready" }, - { viotape_OpCancelled, EIO, "Operation cancelled" }, - { viotape_PhyLinkErr, EIO, "Physical link error" }, - { viotape_RdyNotBOT, EIO, "Ready but not beginning of tape" }, - { viotape_TapeMark, EIO, "Tape mark" }, - { viotape_WriteProt, EROFS, "Write protection error" }, - { 0, 0, NULL }, -}; - -/* Maximum number of tapes we support */ -#define VIOTAPE_MAX_TAPE HVMAXARCHITECTEDVIRTUALTAPES -#define MAX_PARTITIONS 4 - -/* defines for current tape state */ -#define VIOT_IDLE 0 -#define VIOT_READING 1 -#define VIOT_WRITING 2 - -/* Our info on the tapes */ -static struct { - const char *rsrcname; - const char *type; - const char *model; -} viotape_unitinfo[VIOTAPE_MAX_TAPE]; - -static struct mtget viomtget[VIOTAPE_MAX_TAPE]; - -static struct class *tape_class; - -static struct device *tape_device[VIOTAPE_MAX_TAPE]; - -/* - * maintain the current state of each tape (and partition) - * so that we know when to write EOF marks. - */ -static struct { - unsigned char cur_part; - unsigned char part_stat_rwi[MAX_PARTITIONS]; -} state[VIOTAPE_MAX_TAPE]; - -/* We single-thread */ -static struct semaphore reqSem; - -/* - * When we send a request, we use this struct to get the response back - * from the interrupt handler - */ -struct op_struct { - void *buffer; - dma_addr_t dmaaddr; - size_t count; - int rc; - int non_blocking; - struct completion com; - struct device *dev; - struct op_struct *next; -}; - -static spinlock_t op_struct_list_lock; -static struct op_struct *op_struct_list; - -/* forward declaration to resolve interdependence */ -static int chg_state(int index, unsigned char new_state, struct file *file); - -/* procfs support */ -static int proc_viotape_show(struct seq_file *m, void *v) -{ - int i; - - seq_printf(m, "viotape driver version " VIOTAPE_VERSION "\n"); - for (i = 0; i < viotape_numdev; i++) { - seq_printf(m, "viotape device %d is iSeries resource %10.10s" - "type %4.4s, model %3.3s\n", - i, viotape_unitinfo[i].rsrcname, - viotape_unitinfo[i].type, - viotape_unitinfo[i].model); - } - return 0; -} - -static int proc_viotape_open(struct inode *inode, struct file *file) -{ - return single_open(file, proc_viotape_show, NULL); -} - -static const struct file_operations proc_viotape_operations = { - .owner = THIS_MODULE, - .open = proc_viotape_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/* Decode the device minor number into its parts */ -void get_dev_info(struct inode *ino, struct viot_devinfo_struct *devi) -{ - devi->devno = iminor(ino) & 0x1F; - devi->mode = (iminor(ino) & 0x60) >> 5; - /* if bit is set in the minor, do _not_ rewind automatically */ - devi->rewind = (iminor(ino) & 0x80) == 0; -} - -/* This is called only from the exit and init paths, so no need for locking */ -static void clear_op_struct_pool(void) -{ - while (op_struct_list) { - struct op_struct *toFree = op_struct_list; - op_struct_list = op_struct_list->next; - kfree(toFree); - } -} - -/* Likewise, this is only called from the init path */ -static int add_op_structs(int structs) -{ - int i; - - for (i = 0; i < structs; ++i) { - struct op_struct *new_struct = - kmalloc(sizeof(*new_struct), GFP_KERNEL); - if (!new_struct) { - clear_op_struct_pool(); - return -ENOMEM; - } - new_struct->next = op_struct_list; - op_struct_list = new_struct; - } - return 0; -} - -/* Allocate an op structure from our pool */ -static struct op_struct *get_op_struct(void) -{ - struct op_struct *retval; - unsigned long flags; - - spin_lock_irqsave(&op_struct_list_lock, flags); - retval = op_struct_list; - if (retval) - op_struct_list = retval->next; - spin_unlock_irqrestore(&op_struct_list_lock, flags); - if (retval) { - memset(retval, 0, sizeof(*retval)); - init_completion(&retval->com); - } - - return retval; -} - -/* Return an op structure to our pool */ -static void free_op_struct(struct op_struct *op_struct) -{ - unsigned long flags; - - spin_lock_irqsave(&op_struct_list_lock, flags); - op_struct->next = op_struct_list; - op_struct_list = op_struct; - spin_unlock_irqrestore(&op_struct_list_lock, flags); -} - -/* Map our tape return codes to errno values */ -int tape_rc_to_errno(int tape_rc, char *operation, int tapeno) -{ - const struct vio_error_entry *err; - - if (tape_rc == 0) - return 0; - - err = vio_lookup_rc(viotape_err_table, tape_rc); - printk(VIOTAPE_KERN_WARN "error(%s) 0x%04x on Device %d (%-10s): %s\n", - operation, tape_rc, tapeno, - viotape_unitinfo[tapeno].rsrcname, err->msg); - return -err->errno; -} - -/* Write */ -static ssize_t viotap_write(struct file *file, const char *buf, - size_t count, loff_t * ppos) -{ - HvLpEvent_Rc hvrc; - unsigned short flags = file->f_flags; - int noblock = ((flags & O_NONBLOCK) != 0); - ssize_t ret; - struct viot_devinfo_struct devi; - struct op_struct *op = get_op_struct(); - - if (op == NULL) - return -ENOMEM; - - get_dev_info(file->f_path.dentry->d_inode, &devi); - - /* - * We need to make sure we can send a request. We use - * a semaphore to keep track of # requests in use. If - * we are non-blocking, make sure we don't block on the - * semaphore - */ - if (noblock) { - if (down_trylock(&reqSem)) { - ret = -EWOULDBLOCK; - goto free_op; - } - } else - down(&reqSem); - - /* Allocate a DMA buffer */ - op->dev = tape_device[devi.devno]; - op->buffer = dma_alloc_coherent(op->dev, count, &op->dmaaddr, - GFP_ATOMIC); - - if (op->buffer == NULL) { - printk(VIOTAPE_KERN_WARN - "error allocating dma buffer for len %ld\n", - count); - ret = -EFAULT; - goto up_sem; - } - - /* Copy the data into the buffer */ - if (copy_from_user(op->buffer, buf, count)) { - printk(VIOTAPE_KERN_WARN "tape: error on copy from user\n"); - ret = -EFAULT; - goto free_dma; - } - - op->non_blocking = noblock; - init_completion(&op->com); - op->count = count; - - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotapewrite, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)op, VIOVERSION << 16, - ((u64)devi.devno << 48) | op->dmaaddr, count, 0, 0); - if (hvrc != HvLpEvent_Rc_Good) { - printk(VIOTAPE_KERN_WARN "hv error on op %d\n", - (int)hvrc); - ret = -EIO; - goto free_dma; - } - - if (noblock) - return count; - - wait_for_completion(&op->com); - - if (op->rc) - ret = tape_rc_to_errno(op->rc, "write", devi.devno); - else { - chg_state(devi.devno, VIOT_WRITING, file); - ret = op->count; - } - -free_dma: - dma_free_coherent(op->dev, count, op->buffer, op->dmaaddr); -up_sem: - up(&reqSem); -free_op: - free_op_struct(op); - return ret; -} - -/* read */ -static ssize_t viotap_read(struct file *file, char *buf, size_t count, - loff_t *ptr) -{ - HvLpEvent_Rc hvrc; - unsigned short flags = file->f_flags; - struct op_struct *op = get_op_struct(); - int noblock = ((flags & O_NONBLOCK) != 0); - ssize_t ret; - struct viot_devinfo_struct devi; - - if (op == NULL) - return -ENOMEM; - - get_dev_info(file->f_path.dentry->d_inode, &devi); - - /* - * We need to make sure we can send a request. We use - * a semaphore to keep track of # requests in use. If - * we are non-blocking, make sure we don't block on the - * semaphore - */ - if (noblock) { - if (down_trylock(&reqSem)) { - ret = -EWOULDBLOCK; - goto free_op; - } - } else - down(&reqSem); - - chg_state(devi.devno, VIOT_READING, file); - - /* Allocate a DMA buffer */ - op->dev = tape_device[devi.devno]; - op->buffer = dma_alloc_coherent(op->dev, count, &op->dmaaddr, - GFP_ATOMIC); - if (op->buffer == NULL) { - ret = -EFAULT; - goto up_sem; - } - - op->count = count; - init_completion(&op->com); - - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotaperead, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)op, VIOVERSION << 16, - ((u64)devi.devno << 48) | op->dmaaddr, count, 0, 0); - if (hvrc != HvLpEvent_Rc_Good) { - printk(VIOTAPE_KERN_WARN "tape hv error on op %d\n", - (int)hvrc); - ret = -EIO; - goto free_dma; - } - - wait_for_completion(&op->com); - - if (op->rc) - ret = tape_rc_to_errno(op->rc, "read", devi.devno); - else { - ret = op->count; - if (ret && copy_to_user(buf, op->buffer, ret)) { - printk(VIOTAPE_KERN_WARN "error on copy_to_user\n"); - ret = -EFAULT; - } - } - -free_dma: - dma_free_coherent(op->dev, count, op->buffer, op->dmaaddr); -up_sem: - up(&reqSem); -free_op: - free_op_struct(op); - return ret; -} - -/* ioctl */ -static int viotap_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - HvLpEvent_Rc hvrc; - int ret; - struct viot_devinfo_struct devi; - struct mtop mtc; - u32 myOp; - struct op_struct *op = get_op_struct(); - - if (op == NULL) - return -ENOMEM; - - get_dev_info(file->f_path.dentry->d_inode, &devi); - - down(&reqSem); - - ret = -EINVAL; - - switch (cmd) { - case MTIOCTOP: - ret = -EFAULT; - /* - * inode is null if and only if we (the kernel) - * made the request - */ - if (inode == NULL) - memcpy(&mtc, (void *) arg, sizeof(struct mtop)); - else if (copy_from_user((char *)&mtc, (char *)arg, - sizeof(struct mtop))) - goto free_op; - - ret = -EIO; - switch (mtc.mt_op) { - case MTRESET: - myOp = VIOTAPOP_RESET; - break; - case MTFSF: - myOp = VIOTAPOP_FSF; - break; - case MTBSF: - myOp = VIOTAPOP_BSF; - break; - case MTFSR: - myOp = VIOTAPOP_FSR; - break; - case MTBSR: - myOp = VIOTAPOP_BSR; - break; - case MTWEOF: - myOp = VIOTAPOP_WEOF; - break; - case MTREW: - myOp = VIOTAPOP_REW; - break; - case MTNOP: - myOp = VIOTAPOP_NOP; - break; - case MTEOM: - myOp = VIOTAPOP_EOM; - break; - case MTERASE: - myOp = VIOTAPOP_ERASE; - break; - case MTSETBLK: - myOp = VIOTAPOP_SETBLK; - break; - case MTSETDENSITY: - myOp = VIOTAPOP_SETDENSITY; - break; - case MTTELL: - myOp = VIOTAPOP_GETPOS; - break; - case MTSEEK: - myOp = VIOTAPOP_SETPOS; - break; - case MTSETPART: - myOp = VIOTAPOP_SETPART; - break; - case MTOFFL: - myOp = VIOTAPOP_UNLOAD; - break; - default: - printk(VIOTAPE_KERN_WARN "MTIOCTOP called " - "with invalid op 0x%x\n", mtc.mt_op); - goto free_op; - } - - /* - * if we moved the head, we are no longer - * reading or writing - */ - switch (mtc.mt_op) { - case MTFSF: - case MTBSF: - case MTFSR: - case MTBSR: - case MTTELL: - case MTSEEK: - case MTREW: - chg_state(devi.devno, VIOT_IDLE, file); - } - - init_completion(&op->com); - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotapeop, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)op, - VIOVERSION << 16, - ((u64)devi.devno << 48), 0, - (((u64)myOp) << 32) | mtc.mt_count, 0); - if (hvrc != HvLpEvent_Rc_Good) { - printk(VIOTAPE_KERN_WARN "hv error on op %d\n", - (int)hvrc); - goto free_op; - } - wait_for_completion(&op->com); - ret = tape_rc_to_errno(op->rc, "tape operation", devi.devno); - goto free_op; - - case MTIOCGET: - ret = -EIO; - init_completion(&op->com); - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotapegetstatus, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)op, VIOVERSION << 16, - ((u64)devi.devno << 48), 0, 0, 0); - if (hvrc != HvLpEvent_Rc_Good) { - printk(VIOTAPE_KERN_WARN "hv error on op %d\n", - (int)hvrc); - goto free_op; - } - wait_for_completion(&op->com); - - /* Operation is complete - grab the error code */ - ret = tape_rc_to_errno(op->rc, "get status", devi.devno); - free_op_struct(op); - up(&reqSem); - - if ((ret == 0) && copy_to_user((void *)arg, - &viomtget[devi.devno], - sizeof(viomtget[0]))) - ret = -EFAULT; - return ret; - case MTIOCPOS: - printk(VIOTAPE_KERN_WARN "Got an (unsupported) MTIOCPOS\n"); - break; - default: - printk(VIOTAPE_KERN_WARN "got an unsupported ioctl 0x%0x\n", - cmd); - break; - } - -free_op: - free_op_struct(op); - up(&reqSem); - return ret; -} - -static long viotap_unlocked_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - long rc; - - mutex_lock(&proc_viotape_mutex); - rc = viotap_ioctl(file->f_path.dentry->d_inode, file, cmd, arg); - mutex_unlock(&proc_viotape_mutex); - return rc; -} - -static int viotap_open(struct inode *inode, struct file *file) -{ - HvLpEvent_Rc hvrc; - struct viot_devinfo_struct devi; - int ret; - struct op_struct *op = get_op_struct(); - - if (op == NULL) - return -ENOMEM; - - mutex_lock(&proc_viotape_mutex); - get_dev_info(file->f_path.dentry->d_inode, &devi); - - /* Note: We currently only support one mode! */ - if ((devi.devno >= viotape_numdev) || (devi.mode)) { - ret = -ENODEV; - goto free_op; - } - - init_completion(&op->com); - - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotapeopen, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)op, VIOVERSION << 16, - ((u64)devi.devno << 48), 0, 0, 0); - if (hvrc != 0) { - printk(VIOTAPE_KERN_WARN "bad rc on signalLpEvent %d\n", - (int) hvrc); - ret = -EIO; - goto free_op; - } - - wait_for_completion(&op->com); - ret = tape_rc_to_errno(op->rc, "open", devi.devno); - -free_op: - free_op_struct(op); - mutex_unlock(&proc_viotape_mutex); - return ret; -} - - -static int viotap_release(struct inode *inode, struct file *file) -{ - HvLpEvent_Rc hvrc; - struct viot_devinfo_struct devi; - int ret = 0; - struct op_struct *op = get_op_struct(); - - if (op == NULL) - return -ENOMEM; - init_completion(&op->com); - - get_dev_info(file->f_path.dentry->d_inode, &devi); - - if (devi.devno >= viotape_numdev) { - ret = -ENODEV; - goto free_op; - } - - chg_state(devi.devno, VIOT_IDLE, file); - - if (devi.rewind) { - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotapeop, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)op, VIOVERSION << 16, - ((u64)devi.devno << 48), 0, - ((u64)VIOTAPOP_REW) << 32, 0); - wait_for_completion(&op->com); - - tape_rc_to_errno(op->rc, "rewind", devi.devno); - } - - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotapeclose, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)op, VIOVERSION << 16, - ((u64)devi.devno << 48), 0, 0, 0); - if (hvrc != 0) { - printk(VIOTAPE_KERN_WARN "bad rc on signalLpEvent %d\n", - (int) hvrc); - ret = -EIO; - goto free_op; - } - - wait_for_completion(&op->com); - - if (op->rc) - printk(VIOTAPE_KERN_WARN "close failed\n"); - -free_op: - free_op_struct(op); - return ret; -} - -const struct file_operations viotap_fops = { - .owner = THIS_MODULE, - .read = viotap_read, - .write = viotap_write, - .unlocked_ioctl = viotap_unlocked_ioctl, - .open = viotap_open, - .release = viotap_release, - .llseek = noop_llseek, -}; - -/* Handle interrupt events for tape */ -static void vioHandleTapeEvent(struct HvLpEvent *event) -{ - int tapeminor; - struct op_struct *op; - struct viotapelpevent *tevent = (struct viotapelpevent *)event; - - if (event == NULL) { - /* Notification that a partition went away! */ - if (!viopath_isactive(viopath_hostLp)) { - /* TODO! Clean up */ - } - return; - } - - tapeminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; - op = (struct op_struct *)event->xCorrelationToken; - switch (tapeminor) { - case viotapeopen: - case viotapeclose: - op->rc = tevent->sub_type_result; - complete(&op->com); - break; - case viotaperead: - op->rc = tevent->sub_type_result; - op->count = tevent->len; - complete(&op->com); - break; - case viotapewrite: - if (op->non_blocking) { - dma_free_coherent(op->dev, op->count, - op->buffer, op->dmaaddr); - free_op_struct(op); - up(&reqSem); - } else { - op->rc = tevent->sub_type_result; - op->count = tevent->len; - complete(&op->com); - } - break; - case viotapeop: - case viotapegetpos: - case viotapesetpos: - case viotapegetstatus: - if (op) { - op->count = tevent->u.op.count; - op->rc = tevent->sub_type_result; - if (!op->non_blocking) - complete(&op->com); - } - break; - default: - printk(VIOTAPE_KERN_WARN "weird ack\n"); - } -} - -static int viotape_probe(struct vio_dev *vdev, const struct vio_device_id *id) -{ - int i = vdev->unit_address; - int j; - struct device_node *node = vdev->dev.of_node; - - if (i >= VIOTAPE_MAX_TAPE) - return -ENODEV; - if (!node) - return -ENODEV; - - if (i >= viotape_numdev) - viotape_numdev = i + 1; - - tape_device[i] = &vdev->dev; - viotape_unitinfo[i].rsrcname = of_get_property(node, - "linux,vio_rsrcname", NULL); - viotape_unitinfo[i].type = of_get_property(node, "linux,vio_type", - NULL); - viotape_unitinfo[i].model = of_get_property(node, "linux,vio_model", - NULL); - - state[i].cur_part = 0; - for (j = 0; j < MAX_PARTITIONS; ++j) - state[i].part_stat_rwi[j] = VIOT_IDLE; - device_create(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i), NULL, - "iseries!vt%d", i); - device_create(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i | 0x80), NULL, - "iseries!nvt%d", i); - printk(VIOTAPE_KERN_INFO "tape iseries/vt%d is iSeries " - "resource %10.10s type %4.4s, model %3.3s\n", - i, viotape_unitinfo[i].rsrcname, - viotape_unitinfo[i].type, viotape_unitinfo[i].model); - return 0; -} - -static int viotape_remove(struct vio_dev *vdev) -{ - int i = vdev->unit_address; - - device_destroy(tape_class, MKDEV(VIOTAPE_MAJOR, i | 0x80)); - device_destroy(tape_class, MKDEV(VIOTAPE_MAJOR, i)); - return 0; -} - -/** - * viotape_device_table: Used by vio.c to match devices that we - * support. - */ -static struct vio_device_id viotape_device_table[] __devinitdata = { - { "byte", "IBM,iSeries-viotape" }, - { "", "" } -}; -MODULE_DEVICE_TABLE(vio, viotape_device_table); - -static struct vio_driver viotape_driver = { - .id_table = viotape_device_table, - .probe = viotape_probe, - .remove = viotape_remove, - .driver = { - .name = "viotape", - .owner = THIS_MODULE, - } -}; - - -int __init viotap_init(void) -{ - int ret; - - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return -ENODEV; - - op_struct_list = NULL; - if ((ret = add_op_structs(VIOTAPE_MAXREQ)) < 0) { - printk(VIOTAPE_KERN_WARN "couldn't allocate op structs\n"); - return ret; - } - spin_lock_init(&op_struct_list_lock); - - sema_init(&reqSem, VIOTAPE_MAXREQ); - - if (viopath_hostLp == HvLpIndexInvalid) { - vio_set_hostlp(); - if (viopath_hostLp == HvLpIndexInvalid) { - ret = -ENODEV; - goto clear_op; - } - } - - ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, - VIOTAPE_MAXREQ + 2); - if (ret) { - printk(VIOTAPE_KERN_WARN - "error on viopath_open to hostlp %d\n", ret); - ret = -EIO; - goto clear_op; - } - - printk(VIOTAPE_KERN_INFO "vers " VIOTAPE_VERSION - ", hosting partition %d\n", viopath_hostLp); - - vio_setHandler(viomajorsubtype_tape, vioHandleTapeEvent); - - ret = register_chrdev(VIOTAPE_MAJOR, "viotape", &viotap_fops); - if (ret < 0) { - printk(VIOTAPE_KERN_WARN "Error registering viotape device\n"); - goto clear_handler; - } - - tape_class = class_create(THIS_MODULE, "tape"); - if (IS_ERR(tape_class)) { - printk(VIOTAPE_KERN_WARN "Unable to allocat class\n"); - ret = PTR_ERR(tape_class); - goto unreg_chrdev; - } - - ret = vio_register_driver(&viotape_driver); - if (ret) - goto unreg_class; - - proc_create("iSeries/viotape", S_IFREG|S_IRUGO, NULL, - &proc_viotape_operations); - - return 0; - -unreg_class: - class_destroy(tape_class); -unreg_chrdev: - unregister_chrdev(VIOTAPE_MAJOR, "viotape"); -clear_handler: - vio_clearHandler(viomajorsubtype_tape); - viopath_close(viopath_hostLp, viomajorsubtype_tape, VIOTAPE_MAXREQ + 2); -clear_op: - clear_op_struct_pool(); - return ret; -} - -/* Give a new state to the tape object */ -static int chg_state(int index, unsigned char new_state, struct file *file) -{ - unsigned char *cur_state = - &state[index].part_stat_rwi[state[index].cur_part]; - int rc = 0; - - /* if the same state, don't bother */ - if (*cur_state == new_state) - return 0; - - /* write an EOF if changing from writing to some other state */ - if (*cur_state == VIOT_WRITING) { - struct mtop write_eof = { MTWEOF, 1 }; - - rc = viotap_ioctl(NULL, file, MTIOCTOP, - (unsigned long)&write_eof); - } - *cur_state = new_state; - return rc; -} - -/* Cleanup */ -static void __exit viotap_exit(void) -{ - remove_proc_entry("iSeries/viotape", NULL); - vio_unregister_driver(&viotape_driver); - class_destroy(tape_class); - unregister_chrdev(VIOTAPE_MAJOR, "viotape"); - viopath_close(viopath_hostLp, viomajorsubtype_tape, VIOTAPE_MAXREQ + 2); - vio_clearHandler(viomajorsubtype_tape); - clear_op_struct_pool(); -} - -MODULE_LICENSE("GPL"); -module_init(viotap_init); -module_exit(viotap_exit); diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index b58b5618706..60aafb8a1f2 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -24,6 +24,8 @@ #include <linux/err.h> #include <linux/freezer.h> #include <linux/fs.h> +#include <linux/splice.h> +#include <linux/pagemap.h> #include <linux/init.h> #include <linux/list.h> #include <linux/poll.h> @@ -35,8 +37,12 @@ #include <linux/wait.h> #include <linux/workqueue.h> #include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/kconfig.h> #include "../tty/hvc/hvc_console.h" +#define is_rproc_enabled IS_ENABLED(CONFIG_REMOTEPROC) + /* * This is a global struct for storing common data for all the devices * this driver handles. @@ -55,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 @@ -75,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 { @@ -109,6 +112,21 @@ struct port_buffer { size_t len; /* offset in the buf from which to consume data */ size_t offset; + + /* DMA address of buffer */ + dma_addr_t dma; + + /* Device we got DMA memory from */ + struct device *dev; + + /* List of pending dma buffers to free */ + struct list_head list; + + /* If sgpages == 0 then buf is used */ + unsigned int sgpages; + + /* sg is used if spages > 0. sg must be the last in is struct */ + struct scatterlist sg[0]; }; /* @@ -131,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; @@ -148,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; }; @@ -256,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); @@ -323,6 +342,11 @@ static bool is_console_port(struct port *port) return false; } +static bool is_rproc_serial(const struct virtio_device *vdev) +{ + return is_rproc_enabled && vdev->id.device == VIRTIO_ID_RPROC_SERIAL; +} + static inline bool use_multiport(struct ports_device *portdev) { /* @@ -334,20 +358,110 @@ static inline bool use_multiport(struct ports_device *portdev) return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT); } -static void free_buf(struct port_buffer *buf) +static DEFINE_SPINLOCK(dma_bufs_lock); +static LIST_HEAD(pending_free_dma_bufs); + +static void free_buf(struct port_buffer *buf, bool can_sleep) { - kfree(buf->buf); + unsigned int i; + + for (i = 0; i < buf->sgpages; i++) { + struct page *page = sg_page(&buf->sg[i]); + if (!page) + break; + put_page(page); + } + + if (!buf->dev) { + kfree(buf->buf); + } else if (is_rproc_enabled) { + unsigned long flags; + + /* dma_free_coherent requires interrupts to be enabled. */ + if (!can_sleep) { + /* queue up dma-buffers to be freed later */ + spin_lock_irqsave(&dma_bufs_lock, flags); + list_add_tail(&buf->list, &pending_free_dma_bufs); + spin_unlock_irqrestore(&dma_bufs_lock, flags); + return; + } + dma_free_coherent(buf->dev, buf->size, buf->buf, buf->dma); + + /* Release device refcnt and allow it to be freed */ + put_device(buf->dev); + } + kfree(buf); } -static struct port_buffer *alloc_buf(size_t buf_size) +static void reclaim_dma_bufs(void) +{ + unsigned long flags; + struct port_buffer *buf, *tmp; + LIST_HEAD(tmp_list); + + if (list_empty(&pending_free_dma_bufs)) + return; + + /* Create a copy of the pending_free_dma_bufs while holding the lock */ + spin_lock_irqsave(&dma_bufs_lock, flags); + list_cut_position(&tmp_list, &pending_free_dma_bufs, + pending_free_dma_bufs.prev); + spin_unlock_irqrestore(&dma_bufs_lock, flags); + + /* Release the dma buffers, without irqs enabled */ + list_for_each_entry_safe(buf, tmp, &tmp_list, list) { + list_del(&buf->list); + free_buf(buf, true); + } +} + +static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size, + int pages) { struct port_buffer *buf; - buf = kmalloc(sizeof(*buf), GFP_KERNEL); + reclaim_dma_bufs(); + + /* + * Allocate buffer and the sg list. The sg list array is allocated + * directly after the port_buffer struct. + */ + buf = kmalloc(sizeof(*buf) + sizeof(struct scatterlist) * pages, + GFP_KERNEL); if (!buf) goto fail; - buf->buf = kzalloc(buf_size, GFP_KERNEL); + + buf->sgpages = pages; + if (pages > 0) { + buf->dev = NULL; + buf->buf = NULL; + return buf; + } + + if (is_rproc_serial(vq->vdev)) { + /* + * Allocate DMA memory from ancestor. When a virtio + * device is created by remoteproc, the DMA memory is + * associated with the grandparent device: + * vdev => rproc => platform-dev. + * The code here would have been less quirky if + * DMA_MEMORY_INCLUDES_CHILDREN had been supported + * in dma-coherent.c + */ + if (!vq->vdev->dev.parent || !vq->vdev->dev.parent->parent) + goto free_buf; + buf->dev = vq->vdev->dev.parent->parent; + + /* Increase device refcnt to avoid freeing it */ + get_device(buf->dev); + buf->buf = dma_alloc_coherent(buf->dev, buf_size, &buf->dma, + GFP_KERNEL); + } else { + buf->dev = NULL; + buf->buf = kmalloc(buf_size, GFP_KERNEL); + } + if (!buf->buf) goto free_buf; buf->len = 0; @@ -392,8 +506,10 @@ 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; return ret; } @@ -414,7 +530,7 @@ static void discard_port_data(struct port *port) port->stats.bytes_discarded += buf->len - buf->offset; if (add_inbuf(port->in_vq, buf) < 0) { err++; - free_buf(buf); + free_buf(buf, false); } port->inbuf = NULL; buf = get_inbuf(port); @@ -457,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; } @@ -474,10 +594,11 @@ static ssize_t send_control_msg(struct port *port, unsigned int event, return 0; } + /* Callers must take the port->outvq_lock */ static void reclaim_consumed_buffers(struct port *port) { - void *buf; + struct port_buffer *buf; unsigned int len; if (!port->portdev) { @@ -485,17 +606,17 @@ static void reclaim_consumed_buffers(struct port *port) return; } while ((buf = virtqueue_get_buf(port->out_vq, &len))) { - kfree(buf); + free_buf(buf, false); port->outvq_full = false; } } -static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, - bool nonblock) +static ssize_t __send_to_port(struct port *port, struct scatterlist *sg, + int nents, size_t in_count, + void *data, bool nonblock) { - struct scatterlist sg[1]; struct virtqueue *out_vq; - ssize_t ret; + int err; unsigned long flags; unsigned int len; @@ -505,18 +626,17 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, reclaim_consumed_buffers(port); - sg_init_one(sg, in_buf, in_count); - ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf, GFP_ATOMIC); + err = virtqueue_add_outbuf(out_vq, sg, nents, data, GFP_ATOMIC); /* Tell Host to go! */ virtqueue_kick(out_vq); - if (ret < 0) { + if (err) { in_count = 0; goto done; } - if (ret == 0) + if (out_vq->num_free == 0) port->outvq_full = true; if (nonblock) @@ -531,7 +651,8 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, * 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); @@ -630,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 @@ -646,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; /* @@ -665,13 +790,34 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf, return fill_readbuf(port, ubuf, count, true); } +static int wait_port_writable(struct port *port, bool nonblock) +{ + int ret; + + if (will_write_block(port)) { + if (nonblock) + return -EAGAIN; + + ret = wait_event_freezable(port->waitqueue, + !will_write_block(port)); + if (ret < 0) + return ret; + } + /* Port got hot-unplugged. */ + if (!port->guest_connected) + return -ENODEV; + + return 0; +} + static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, size_t count, loff_t *offp) { struct port *port; - char *buf; + struct port_buffer *buf; ssize_t ret; bool nonblock; + struct scatterlist sg[1]; /* Userspace could be out to fool us */ if (!count) @@ -681,26 +827,17 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, nonblock = filp->f_flags & O_NONBLOCK; - if (will_write_block(port)) { - if (nonblock) - return -EAGAIN; - - ret = wait_event_freezable(port->waitqueue, - !will_write_block(port)); - if (ret < 0) - return ret; - } - /* Port got hot-unplugged. */ - if (!port->guest_connected) - return -ENODEV; + ret = wait_port_writable(port, nonblock); + if (ret < 0) + return ret; count = min((size_t)(32 * 1024), count); - buf = kmalloc(count, GFP_KERNEL); + buf = alloc_buf(port->out_vq, count, 0); if (!buf) return -ENOMEM; - ret = copy_from_user(buf, ubuf, count); + ret = copy_from_user(buf->buf, ubuf, count); if (ret) { ret = -EFAULT; goto free_buf; @@ -714,17 +851,132 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, * through to the host. */ nonblock = true; - ret = send_buf(port, buf, count, nonblock); + sg_init_one(sg, buf->buf, count); + ret = __send_to_port(port, sg, 1, count, buf, nonblock); if (nonblock && ret > 0) goto out; free_buf: - kfree(buf); + free_buf(buf, true); out: return ret; } +struct sg_list { + unsigned int n; + unsigned int size; + size_t len; + struct scatterlist *sg; +}; + +static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf, + struct splice_desc *sd) +{ + struct sg_list *sgl = sd->u.data; + unsigned int offset, len; + + if (sgl->n == sgl->size) + return 0; + + /* Try lock this page */ + if (buf->ops->steal(pipe, buf) == 0) { + /* Get reference and unlock page for moving */ + get_page(buf->page); + unlock_page(buf->page); + + len = min(buf->len, sd->len); + sg_set_page(&(sgl->sg[sgl->n]), buf->page, len, buf->offset); + } else { + /* Failback to copying a page */ + struct page *page = alloc_page(GFP_KERNEL); + char *src; + + if (!page) + return -ENOMEM; + + offset = sd->pos & ~PAGE_MASK; + + len = sd->len; + if (len + offset > PAGE_SIZE) + len = PAGE_SIZE - offset; + + 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); + } + sgl->n++; + sgl->len += len; + + return len; +} + +/* Faster zero-copy write by splicing */ +static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, + struct file *filp, loff_t *ppos, + size_t len, unsigned int flags) +{ + struct port *port = filp->private_data; + struct sg_list sgl; + ssize_t ret; + struct port_buffer *buf; + struct splice_desc sd = { + .total_len = len, + .flags = flags, + .pos = *ppos, + .u.data = &sgl, + }; + + /* + * Rproc_serial does not yet support splice. To support splice + * pipe_to_sg() must allocate dma-buffers and copy content from + * regular pages to dma pages. And alloc_buf and free_buf must + * support allocating and freeing such a list of dma-buffers. + */ + 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) + goto error_out; + + buf = alloc_buf(port->out_vq, 0, pipe->nrbufs); + if (!buf) { + ret = -ENOMEM; + goto error_out; + } + + sgl.n = 0; + sgl.len = 0; + sgl.size = pipe->nrbufs; + 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) { struct port *port; @@ -770,6 +1022,7 @@ static int port_fops_release(struct inode *inode, struct file *filp) reclaim_consumed_buffers(port); spin_unlock_irq(&port->outvq_lock); + reclaim_dma_bufs(); /* * Locks aren't necessary here as a port can't be opened after * unplug, and if a port isn't unplugged, a kref would already @@ -789,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 @@ -810,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; } @@ -856,6 +1109,7 @@ static const struct file_operations port_fops = { .open = port_fops_open, .read = port_fops_read, .write = port_fops_write, + .splice_write = port_fops_splice_write, .poll = port_fops_poll, .release = port_fops_release, .fasync = port_fops_fasync, @@ -873,6 +1127,7 @@ static const struct file_operations port_fops = { static int put_chars(u32 vtermno, const char *buf, int count) { struct port *port; + struct scatterlist sg[1]; if (unlikely(early_put_chars)) return early_put_chars(vtermno, buf, count); @@ -881,7 +1136,8 @@ static int put_chars(u32 vtermno, const char *buf, int count) if (!port) return -EPIPE; - return send_buf(port, (void *)buf, count, false); + sg_init_one(sg, buf, count); + return __send_to_port(port, sg, 1, count, (void *)buf, false); } /* @@ -918,7 +1174,10 @@ static void resize_console(struct port *port) return; vdev = port->portdev->vdev; - if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) + + /* Don't test F_SIZE at all if we're rproc: not a valid feature! */ + if (!is_rproc_serial(vdev) && + virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) hvc_resize(port->cons.hvc, port->cons.ws); } @@ -966,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; @@ -1038,12 +1297,6 @@ static struct attribute_group port_attribute_group = { .attrs = port_sysfs_entries, }; -static int debugfs_open(struct inode *inode, struct file *filp) -{ - filp->private_data = inode->i_private; - return 0; -} - static ssize_t debugfs_read(struct file *filp, char __user *ubuf, size_t count, loff_t *offp) { @@ -1087,7 +1340,7 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf, static const struct file_operations port_debugfs_ops = { .owner = THIS_MODULE, - .open = debugfs_open, + .open = simple_open, .read = debugfs_read, }; @@ -1108,7 +1361,7 @@ static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) nr_added_bufs = 0; do { - buf = alloc_buf(PAGE_SIZE); + buf = alloc_buf(vq, PAGE_SIZE, 0); if (!buf) break; @@ -1116,7 +1369,7 @@ static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) ret = add_inbuf(vq, buf); if (ret < 0) { spin_unlock_irq(lock); - free_buf(buf); + free_buf(buf, true); break; } nr_added_bufs++; @@ -1183,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, @@ -1204,10 +1457,18 @@ static int add_port(struct ports_device *portdev, u32 id) goto free_device; } - /* - * If we're not using multiport support, this has to be a console port - */ - if (!use_multiport(port->portdev)) { + if (is_rproc_serial(port->portdev->vdev)) + /* + * For rproc_serial assume remote processor is connected. + * rproc_serial does not want the console port, only + * the generic port implementation. + */ + port->host_connected = true; + else if (!use_multiport(port->portdev)) { + /* + * If we're not using multiport support, + * this has to be a console port. + */ err = init_port_console(port); if (err) goto free_inbufs; @@ -1230,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, @@ -1240,7 +1501,7 @@ static int add_port(struct ports_device *portdev, u32 id) free_inbufs: while ((buf = virtqueue_detach_unused_buf(port->in_vq))) - free_buf(buf); + free_buf(buf, true); free_device: device_destroy(pdrvdata.class, port->dev->devt); free_cdev: @@ -1260,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); } @@ -1275,14 +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); + 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); } /* @@ -1296,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); @@ -1321,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 @@ -1413,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: /* @@ -1471,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); + free_buf(buf, false); } } - spin_unlock(&portdev->cvq_lock); + spin_unlock(&portdev->c_ivq_lock); } static void out_intr(struct virtqueue *vq) @@ -1514,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(); } @@ -1552,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); @@ -1680,10 +1960,10 @@ static void remove_controlq_data(struct ports_device *portdev) return; while ((buf = virtqueue_get_buf(portdev->c_ivq, &len))) - free_buf(buf); + free_buf(buf, true); while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq))) - free_buf(buf); + free_buf(buf, true); } /* @@ -1694,7 +1974,7 @@ static void remove_controlq_data(struct ports_device *portdev) * config space to see how many ports the host has spawned. We * initialize each port found. */ -static int __devinit virtcons_probe(struct virtio_device *vdev) +static int virtcons_probe(struct virtio_device *vdev) { struct ports_device *portdev; int err; @@ -1714,27 +1994,26 @@ static int __devinit 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; } multiport = false; portdev->config.max_nr_ports = 1; - if (virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT, - offsetof(struct virtio_console_config, - max_nr_ports), - &portdev->config.max_nr_ports) == 0) + + /* Don't test MULTIPORT at all if we're rproc: not a valid feature! */ + if (!is_rproc_serial(vdev) && + virtio_cread_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT, + struct virtio_console_config, max_nr_ports, + &portdev->config.max_nr_ports) == 0) { multiport = true; + } err = init_vqs(portdev); if (err < 0) { @@ -1748,10 +2027,12 @@ static int __devinit 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"); @@ -1814,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); @@ -1844,7 +2126,17 @@ static unsigned int features[] = { VIRTIO_CONSOLE_F_MULTIPORT, }; -#ifdef CONFIG_PM +static struct virtio_device_id rproc_serial_id_table[] = { +#if IS_ENABLED(CONFIG_REMOTEPROC) + { VIRTIO_ID_RPROC_SERIAL, VIRTIO_DEV_ANY_ID }, +#endif + { 0 }, +}; + +static unsigned int rproc_serial_features[] = { +}; + +#ifdef CONFIG_PM_SLEEP static int virtcons_freeze(struct virtio_device *vdev) { struct ports_device *portdev; @@ -1891,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]; @@ -1901,6 +2193,13 @@ static int virtcons_restore(struct virtio_device *vdev) /* Get port open/close status on the host */ send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); + + /* + * If a port was open at the time of suspending, we + * have to let the host know that it's still open. + */ + if (port->guest_connected) + send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1); } return 0; } @@ -1915,12 +2214,22 @@ 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 }; +static struct virtio_driver virtio_rproc_serial = { + .feature_table = rproc_serial_features, + .feature_table_size = ARRAY_SIZE(rproc_serial_features), + .driver.name = "virtio_rproc_serial", + .driver.owner = THIS_MODULE, + .id_table = rproc_serial_id_table, + .probe = virtcons_probe, + .remove = virtcons_remove, +}; + static int __init init(void) { int err; @@ -1933,19 +2242,38 @@ 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); - return register_virtio_driver(&virtio_console); + err = register_virtio_driver(&virtio_console); + if (err < 0) { + pr_err("Error %d registering virtio driver\n", err); + goto free; + } + err = register_virtio_driver(&virtio_rproc_serial); + if (err < 0) { + pr_err("Error %d registering virtio rproc serial driver\n", + err); + goto unregister; + } + return 0; +unregister: + unregister_virtio_driver(&virtio_console); +free: + if (pdrvdata.debugfs_dir) + debugfs_remove_recursive(pdrvdata.debugfs_dir); + class_destroy(pdrvdata.class); + return err; } static void __exit fini(void) { + reclaim_dma_bufs(); + unregister_virtio_driver(&virtio_console); + unregister_virtio_driver(&virtio_rproc_serial); class_destroy(pdrvdata.class); if (pdrvdata.debugfs_dir) diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index e90e1c74fd4..f6345f932e4 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -89,7 +89,6 @@ #include <asm/io.h> #include <asm/uaccess.h> -#include <asm/system.h> #ifdef CONFIG_OF /* For open firmware. */ @@ -168,6 +167,7 @@ static const struct config_registers v4_config_registers = { .BOOTSTS = UNIMPLEMENTED, .CTL_1 = UNIMPLEMENTED, }; + static const struct config_registers v5_config_registers = { .CRC = 0, .FAR = 1, @@ -193,6 +193,31 @@ static const struct config_registers v5_config_registers = { .CTL_1 = 19, }; +static const struct config_registers v6_config_registers = { + .CRC = 0, + .FAR = 1, + .FDRI = 2, + .FDRO = 3, + .CMD = 4, + .CTL = 5, + .MASK = 6, + .STAT = 7, + .LOUT = 8, + .COR = 9, + .MFWR = 10, + .FLR = UNIMPLEMENTED, + .KEY = UNIMPLEMENTED, + .CBC = 11, + .IDCODE = 12, + .AXSS = 13, + .C0R_1 = 14, + .CSOB = 15, + .WBSTAR = 16, + .TIMER = 17, + .BOOTSTS = 22, + .CTL_1 = 24, +}; + /** * hwicap_command_desync - Send a DESYNC command to the ICAP port. * @drvdata: a pointer to the drvdata. @@ -570,7 +595,7 @@ static const struct file_operations hwicap_fops = { .llseek = noop_llseek, }; -static int __devinit hwicap_setup(struct device *dev, int id, +static int hwicap_setup(struct device *dev, int id, const struct resource *regs_res, const struct hwicap_driver_config *config, const struct config_registers *config_regs) @@ -692,11 +717,11 @@ static struct hwicap_driver_config fifo_icap_config = { .reset = fifo_icap_reset, }; -static int __devexit hwicap_remove(struct device *dev) +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; @@ -706,7 +731,6 @@ static int __devexit 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; @@ -715,7 +739,7 @@ static int __devexit hwicap_remove(struct device *dev) } #ifdef CONFIG_OF -static int __devinit hwicap_of_probe(struct platform_device *op, +static int hwicap_of_probe(struct platform_device *op, const struct hwicap_driver_config *config) { struct resource res; @@ -745,6 +769,8 @@ static int __devinit hwicap_of_probe(struct platform_device *op, regs = &v4_config_registers; } else if (!strcmp(family, "virtex5")) { regs = &v5_config_registers; + } else if (!strcmp(family, "virtex6")) { + regs = &v6_config_registers; } } return hwicap_setup(&op->dev, id ? *id : -1, &res, config, @@ -758,8 +784,8 @@ static inline int hwicap_of_probe(struct platform_device *op, } #endif /* CONFIG_OF */ -static const struct of_device_id __devinitconst hwicap_of_match[]; -static int __devinit hwicap_drv_probe(struct platform_device *pdev) +static const struct of_device_id hwicap_of_match[]; +static int hwicap_drv_probe(struct platform_device *pdev) { const struct of_device_id *match; struct resource *res; @@ -786,6 +812,8 @@ static int __devinit hwicap_drv_probe(struct platform_device *pdev) regs = &v4_config_registers; } else if (!strcmp(family, "virtex5")) { regs = &v5_config_registers; + } else if (!strcmp(family, "virtex6")) { + regs = &v6_config_registers; } } @@ -793,14 +821,14 @@ static int __devinit hwicap_drv_probe(struct platform_device *pdev) &buffer_icap_config, regs); } -static int __devexit hwicap_drv_remove(struct platform_device *pdev) +static int hwicap_drv_remove(struct platform_device *pdev) { return hwicap_remove(&pdev->dev); } #ifdef CONFIG_OF /* Match table for device tree binding */ -static const struct of_device_id __devinitconst hwicap_of_match[] = { +static const struct of_device_id hwicap_of_match[] = { { .compatible = "xlnx,opb-hwicap-1.00.b", .data = &buffer_icap_config}, { .compatible = "xlnx,xps-hwicap-1.00.a", .data = &fifo_icap_config}, {}, diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.h b/drivers/char/xilinx_hwicap/xilinx_hwicap.h index 8cca11981c5..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,8 +85,14 @@ struct hwicap_driver_config { void (*reset)(struct hwicap_drvdata *drvdata); }; -/* Number of times to poll the done regsiter */ -#define XHI_MAX_RETRIES 10 +/* 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 *************/ |
