diff options
Diffstat (limited to 'drivers/char')
34 files changed, 1087 insertions, 1091 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 49502bc5360a..423fd56bf612 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -616,5 +616,16 @@ config MSM_SMD_PKT Enables userspace clients to read and write to some packet SMD ports via device interface for MSM chipset. +config TILE_SROM + bool "Character-device access via hypervisor to the Tilera SPI ROM" + depends on TILE + default y + ---help--- + This device provides character-level read-write access + to the SROM, typically via the "0", "1", and "2" devices + in /dev/srom/. The Tilera hypervisor makes the flash + device appear much like a simple EEPROM, and knows + how to partition a single ROM for multiple purposes. + endmenu diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 7a00672bd85d..32762ba769c2 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -63,3 +63,5 @@ obj-$(CONFIG_RAMOOPS) += ramoops.o obj-$(CONFIG_JS_RTC) += js-rtc.o js-rtc-y = rtc.o + +obj-$(CONFIG_TILE_SROM) += tile-srom.o diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index f27d0d0816d3..4b71647782d0 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -171,7 +171,7 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) } got_gatt = 1; - bridge->key_list = vmalloc(PAGE_SIZE * 4); + bridge->key_list = vzalloc(PAGE_SIZE * 4); if (bridge->key_list == NULL) { dev_err(&bridge->dev->dev, "can't allocate memory for key lists\n"); @@ -181,7 +181,6 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) got_keylist = 1; /* FIXME vmalloc'd memory not guaranteed contiguous */ - memset(bridge->key_list, 0, PAGE_SIZE * 4); if (bridge->driver->configure()) { dev_err(&bridge->dev->dev, "error configuring host chipset\n"); diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 85151019dde1..2774ac1086d3 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -30,10 +30,10 @@ /* * If we have Intel graphics, we're not going to have anything other than * an Intel IOMMU. So make the correct use of the PCI DMA API contingent - * on the Intel IOMMU support (CONFIG_DMAR). + * on the Intel IOMMU support (CONFIG_INTEL_IOMMU). * Only newer chipsets need to bother with this, of course. */ -#ifdef CONFIG_DMAR +#ifdef CONFIG_INTEL_IOMMU #define USE_PCI_DMA_API 1 #else #define USE_PCI_DMA_API 0 diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c index 548708c4b2b8..f4837a893dfa 100644 --- a/drivers/char/apm-emulation.c +++ b/drivers/char/apm-emulation.c @@ -40,10 +40,7 @@ #define APM_MINOR_DEV 134 /* - * See Documentation/Config.help for the configuration options. - * - * Various options can be changed at boot time as follows: - * (We allow underscores for compatibility with the modules code) + * One option can be changed at boot time as follows: * apm=on/off enable/disable APM */ @@ -300,17 +297,13 @@ apm_ioctl(struct file *filp, u_int cmd, u_long arg) /* * Wait for the suspend/resume to complete. If there * are pending acknowledges, we wait here for them. + * wait_event_freezable() is interruptible and pending + * signal can cause busy looping. We aren't doing + * anything critical, chill a bit on each iteration. */ - freezer_do_not_count(); - - wait_event(apm_suspend_waitqueue, - as->suspend_state == SUSPEND_DONE); - - /* - * Since we are waiting until the suspend is done, the - * try_to_freeze() in freezer_count() will not trigger - */ - freezer_count(); + while (wait_event_freezable(apm_suspend_waitqueue, + as->suspend_state == SUSPEND_DONE)) + msleep(10); break; case SUSPEND_ACKTO: as->suspend_result = -ETIMEDOUT; @@ -606,7 +599,7 @@ static int apm_suspend_notifier(struct notifier_block *nb, return NOTIFY_OK; /* interrupted by signal */ - return NOTIFY_BAD; + return notifier_from_errno(err); case PM_POST_SUSPEND: /* diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c index cf39bc08ce08..0c688232aab3 100644 --- a/drivers/char/bsr.c +++ b/drivers/char/bsr.c @@ -212,7 +212,7 @@ static int bsr_add_node(struct device_node *bn) cur->bsr_minor = i + total_bsr_devs; cur->bsr_addr = res.start; - cur->bsr_len = res.end - res.start + 1; + cur->bsr_len = resource_size(&res); cur->bsr_bytes = bsr_bytes[i]; cur->bsr_stride = bsr_stride[i]; cur->bsr_dev = MKDEV(bsr_major, i + total_bsr_devs); diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c index 0e941b57482e..6c4f4b5a9dd3 100644 --- a/drivers/char/generic_nvram.c +++ b/drivers/char/generic_nvram.c @@ -34,12 +34,16 @@ static ssize_t nvram_len; static loff_t nvram_llseek(struct file *file, loff_t offset, int origin) { switch (origin) { + case 0: + break; case 1: offset += file->f_pos; break; case 2: offset += nvram_len; break; + default: + offset = -1; } if (offset < 0) return -EINVAL; diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 34d6a1cab8de..0833896cf6f2 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -952,7 +952,7 @@ int hpet_alloc(struct hpet_data *hdp) #ifdef CONFIG_IA64 if (!hpet_clocksource) { hpet_mctr = (void __iomem *)&hpetp->hp_hpet->hpet_mc; - CLKSRC_FSYS_MMIO_SET(clocksource_hpet.fsys_mmio, hpet_mctr); + clocksource_hpet.archdata.fsys_mmio = hpet_mctr; clocksource_register_hz(&clocksource_hpet, hpetp->hp_tick_freq); hpetp->hp_clocksource = &clocksource_hpet; hpet_clocksource = &clocksource_hpet; diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index a60043b3e409..1d2ebc7a4947 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -210,3 +210,15 @@ config HW_RANDOM_PICOXCELL 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 + ---help--- + This driver provides the kernel-side support for the TRNG hardware + found in the security function of some PowerPC 4xx SoCs. + + To compile this driver as a module, choose M here: the + module will be called ppc4xx-rng. + + If unsure, say N. diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 3db4eb8b19c0..c88f244c8a71 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -20,3 +20,4 @@ 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 diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 2016aad85203..1bafb40ec8a2 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -19,7 +19,7 @@ Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com> Added generic RNG API - Copyright 2006 Michael Buesch <mbuesch@freenet.de> + Copyright 2006 Michael Buesch <m@bues.ch> Copyright 2005 (c) MontaVista Software, Inc. Please read Documentation/hw_random.txt for details on use. diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c index ac6739e085e3..c3de70de00d4 100644 --- a/drivers/char/hw_random/n2-drv.c +++ b/drivers/char/hw_random/n2-drv.c @@ -1,6 +1,6 @@ /* n2-drv.c: Niagara-2 RNG driver. * - * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + * Copyright (C) 2008, 2011 David S. Miller <davem@davemloft.net> */ #include <linux/kernel.h> @@ -22,8 +22,8 @@ #define DRV_MODULE_NAME "n2rng" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "0.1" -#define DRV_MODULE_RELDATE "May 15, 2008" +#define DRV_MODULE_VERSION "0.2" +#define DRV_MODULE_RELDATE "July 27, 2011" static char version[] __devinitdata = DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; @@ -623,14 +623,14 @@ static const struct of_device_id n2rng_match[]; static int __devinit n2rng_probe(struct platform_device *op) { const struct of_device_id *match; - int victoria_falls; + int multi_capable; int err = -ENOMEM; struct n2rng *np; match = of_match_device(n2rng_match, &op->dev); if (!match) return -EINVAL; - victoria_falls = (match->data != NULL); + multi_capable = (match->data != NULL); n2rng_driver_version(); np = kzalloc(sizeof(*np), GFP_KERNEL); @@ -640,8 +640,8 @@ static int __devinit n2rng_probe(struct platform_device *op) INIT_DELAYED_WORK(&np->work, n2rng_work); - if (victoria_falls) - np->flags |= N2RNG_FLAG_VF; + if (multi_capable) + np->flags |= N2RNG_FLAG_MULTI; err = -ENODEV; np->hvapi_major = 2; @@ -658,10 +658,10 @@ static int __devinit n2rng_probe(struct platform_device *op) } } - if (np->flags & N2RNG_FLAG_VF) { + if (np->flags & N2RNG_FLAG_MULTI) { if (np->hvapi_major < 2) { - dev_err(&op->dev, "VF RNG requires HVAPI major " - "version 2 or later, got %lu\n", + dev_err(&op->dev, "multi-unit-capable RNG requires " + "HVAPI major version 2 or later, got %lu\n", np->hvapi_major); goto out_hvapi_unregister; } @@ -688,8 +688,8 @@ static int __devinit n2rng_probe(struct platform_device *op) goto out_free_units; dev_info(&op->dev, "Found %s RNG, units: %d\n", - ((np->flags & N2RNG_FLAG_VF) ? - "Victoria Falls" : "Niagara2"), + ((np->flags & N2RNG_FLAG_MULTI) ? + "multi-unit-capable" : "single-unit"), np->num_units); np->hwrng.name = "n2rng"; @@ -751,6 +751,11 @@ static const struct of_device_id n2rng_match[] = { .compatible = "SUNW,vf-rng", .data = (void *) 1, }, + { + .name = "random-number-generator", + .compatible = "SUNW,kt-rng", + .data = (void *) 1, + }, {}, }; MODULE_DEVICE_TABLE(of, n2rng_match); diff --git a/drivers/char/hw_random/n2rng.h b/drivers/char/hw_random/n2rng.h index 4bea07f30978..f244ac89087f 100644 --- a/drivers/char/hw_random/n2rng.h +++ b/drivers/char/hw_random/n2rng.h @@ -68,7 +68,7 @@ struct n2rng { struct platform_device *op; unsigned long flags; -#define N2RNG_FLAG_VF 0x00000001 /* Victoria Falls RNG, else N2 */ +#define N2RNG_FLAG_MULTI 0x00000001 /* Multi-unit capable RNG */ #define N2RNG_FLAG_CONTROL 0x00000002 /* Operating in control domain */ #define N2RNG_FLAG_READY 0x00000008 /* Ready for hw-rng layer */ #define N2RNG_FLAG_SHUTDOWN 0x00000010 /* Driver unregistering */ diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c index dd1d143eb8ea..52e08ca3ccd7 100644 --- a/drivers/char/hw_random/nomadik-rng.c +++ b/drivers/char/hw_random/nomadik-rng.c @@ -55,7 +55,7 @@ static int nmk_rng_probe(struct amba_device *dev, const struct amba_id *id) ret = amba_request_regions(dev, dev->dev.init_name); if (ret) - return ret; + goto out_clk; ret = -ENOMEM; base = ioremap(dev->res.start, resource_size(&dev->res)); if (!base) @@ -70,6 +70,7 @@ out_unmap: iounmap(base); out_release: amba_release_regions(dev); +out_clk: clk_disable(rng_clk); clk_put(rng_clk); return ret; diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index 2cc755a64302..b757fac3cd1f 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -113,8 +113,10 @@ static int __devinit omap_rng_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENOENT; + if (!res) { + ret = -ENOENT; + goto err_region; + } if (!request_mem_region(res->start, resource_size(res), pdev->name)) { ret = -EBUSY; diff --git a/drivers/char/hw_random/ppc4xx-rng.c b/drivers/char/hw_random/ppc4xx-rng.c new file mode 100644 index 000000000000..b8afa6a4ff67 --- /dev/null +++ b/drivers/char/hw_random/ppc4xx-rng.c @@ -0,0 +1,156 @@ +/* + * Generic PowerPC 44x RNG driver + * + * Copyright 2011 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; version 2 of the License. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/hw_random.h> +#include <linux/delay.h> +#include <linux/of_platform.h> +#include <asm/io.h> + +#define PPC4XX_TRNG_DEV_CTRL 0x60080 + +#define PPC4XX_TRNGE 0x00020000 +#define PPC4XX_TRNG_CTRL 0x0008 +#define PPC4XX_TRNG_CTRL_DALM 0x20 +#define PPC4XX_TRNG_STAT 0x0004 +#define PPC4XX_TRNG_STAT_B 0x1 +#define PPC4XX_TRNG_DATA 0x0000 + +#define MODULE_NAME "ppc4xx_rng" + +static int ppc4xx_rng_data_present(struct hwrng *rng, int wait) +{ + void __iomem *rng_regs = (void __iomem *) rng->priv; + int busy, i, present = 0; + + for (i = 0; i < 20; i++) { + busy = (in_le32(rng_regs + PPC4XX_TRNG_STAT) & PPC4XX_TRNG_STAT_B); + if (!busy || !wait) { + present = 1; + break; + } + udelay(10); + } + return present; +} + +static int ppc4xx_rng_data_read(struct hwrng *rng, u32 *data) +{ + void __iomem *rng_regs = (void __iomem *) rng->priv; + *data = in_le32(rng_regs + PPC4XX_TRNG_DATA); + return 4; +} + +static int ppc4xx_rng_enable(int enable) +{ + struct device_node *ctrl; + void __iomem *ctrl_reg; + int err = 0; + u32 val; + + /* Find the main crypto device node and map it to turn the TRNG on */ + ctrl = of_find_compatible_node(NULL, NULL, "amcc,ppc4xx-crypto"); + if (!ctrl) + return -ENODEV; + + ctrl_reg = of_iomap(ctrl, 0); + if (!ctrl_reg) { + err = -ENODEV; + goto out; + } + + val = in_le32(ctrl_reg + PPC4XX_TRNG_DEV_CTRL); + + if (enable) + val |= PPC4XX_TRNGE; + else + val = val & ~PPC4XX_TRNGE; + + out_le32(ctrl_reg + PPC4XX_TRNG_DEV_CTRL, val); + iounmap(ctrl_reg); + +out: + of_node_put(ctrl); + + return err; +} + +static struct hwrng ppc4xx_rng = { + .name = MODULE_NAME, + .data_present = ppc4xx_rng_data_present, + .data_read = ppc4xx_rng_data_read, +}; + +static int __devinit ppc4xx_rng_probe(struct platform_device *dev) +{ + void __iomem *rng_regs; + int err = 0; + + rng_regs = of_iomap(dev->dev.of_node, 0); + if (!rng_regs) + return -ENODEV; + + err = ppc4xx_rng_enable(1); + if (err) + return err; + + out_le32(rng_regs + PPC4XX_TRNG_CTRL, PPC4XX_TRNG_CTRL_DALM); + ppc4xx_rng.priv = (unsigned long) rng_regs; + + err = hwrng_register(&ppc4xx_rng); + + return err; +} + +static int __devexit ppc4xx_rng_remove(struct platform_device *dev) +{ + void __iomem *rng_regs = (void __iomem *) ppc4xx_rng.priv; + + hwrng_unregister(&ppc4xx_rng); + ppc4xx_rng_enable(0); + iounmap(rng_regs); + + return 0; +} + +static struct of_device_id ppc4xx_rng_match[] = { + { .compatible = "ppc4xx-rng", }, + { .compatible = "amcc,ppc460ex-rng", }, + { .compatible = "amcc,ppc440epx-rng", }, + {}, +}; + +static struct platform_driver ppc4xx_rng_driver = { + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = ppc4xx_rng_match, + }, + .probe = ppc4xx_rng_probe, + .remove = ppc4xx_rng_remove, +}; + +static int __init ppc4xx_rng_init(void) +{ + return platform_driver_register(&ppc4xx_rng_driver); +} +module_init(ppc4xx_rng_init); + +static void __exit ppc4xx_rng_exit(void) +{ + platform_driver_unregister(&ppc4xx_rng_driver); +} +module_exit(ppc4xx_rng_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Josh Boyer <jwboyer@linux.vnet.ibm.com>"); +MODULE_DESCRIPTION("HW RNG driver for PPC 4xx processors"); diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c index a94e930575f2..a8428e6f64a9 100644 --- a/drivers/char/hw_random/timeriomem-rng.c +++ b/drivers/char/hw_random/timeriomem-rng.c @@ -100,8 +100,7 @@ static int __devinit timeriomem_rng_probe(struct platform_device *pdev) timeriomem_rng_data = pdev->dev.platform_data; - timeriomem_rng_data->address = ioremap(res->start, - res->end - res->start + 1); + timeriomem_rng_data->address = ioremap(res->start, resource_size(res)); if (!timeriomem_rng_data->address) return -EIO; diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 320668f4c3aa..c2917ffad2c2 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -52,7 +52,7 @@ #include <linux/string.h> #include <linux/ctype.h> #include <linux/delay.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #ifdef CONFIG_X86 /* @@ -65,6 +65,7 @@ * mechanism for it at that time. */ #include <asm/kdebug.h> +#include <asm/nmi.h> #define HAVE_DIE_NMI #endif @@ -1077,17 +1078,8 @@ static void ipmi_unregister_watchdog(int ipmi_intf) #ifdef HAVE_DIE_NMI static int -ipmi_nmi(struct notifier_block *self, unsigned long val, void *data) +ipmi_nmi(unsigned int val, struct pt_regs *regs) { - struct die_args *args = data; - - if (val != DIE_NMIUNKNOWN) - return NOTIFY_OK; - - /* Hack, if it's a memory or I/O error, ignore it. */ - if (args->err & 0xc0) - return NOTIFY_OK; - /* * If we get here, it's an NMI that's not a memory or I/O * error. We can't truly tell if it's from IPMI or not @@ -1097,15 +1089,15 @@ ipmi_nmi(struct notifier_block *self, unsigned long val, void *data) if (testing_nmi) { testing_nmi = 2; - return NOTIFY_STOP; + return NMI_HANDLED; } /* If we are not expecting a timeout, ignore it. */ if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) - return NOTIFY_OK; + return NMI_DONE; if (preaction_val != WDOG_PRETIMEOUT_NMI) - return NOTIFY_OK; + return NMI_DONE; /* * If no one else handled the NMI, we assume it was the IPMI @@ -1120,12 +1112,8 @@ ipmi_nmi(struct notifier_block *self, unsigned long val, void *data) panic(PFX "pre-timeout"); } - return NOTIFY_STOP; + return NMI_HANDLED; } - -static struct notifier_block ipmi_nmi_handler = { - .notifier_call = ipmi_nmi -}; #endif static int wdog_reboot_handler(struct notifier_block *this, @@ -1290,7 +1278,8 @@ static void check_parms(void) } } if (do_nmi && !nmi_handler_registered) { - rv = register_die_notifier(&ipmi_nmi_handler); + rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0, + "ipmi"); if (rv) { printk(KERN_WARNING PFX "Can't register nmi handler\n"); @@ -1298,7 +1287,7 @@ static void check_parms(void) } else nmi_handler_registered = 1; } else if (!do_nmi && nmi_handler_registered) { - unregister_die_notifier(&ipmi_nmi_handler); + unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); nmi_handler_registered = 0; } #endif @@ -1336,7 +1325,7 @@ static int __init ipmi_wdog_init(void) if (rv) { #ifdef HAVE_DIE_NMI if (nmi_handler_registered) - unregister_die_notifier(&ipmi_nmi_handler); + unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); #endif atomic_notifier_chain_unregister(&panic_notifier_list, &wdog_panic_notifier); @@ -1357,7 +1346,7 @@ static void __exit ipmi_wdog_exit(void) #ifdef HAVE_DIE_NMI if (nmi_handler_registered) - unregister_die_notifier(&ipmi_nmi_handler); + unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); #endif atomic_notifier_chain_unregister(&panic_notifier_list, diff --git a/drivers/char/msm_smd_pkt.c b/drivers/char/msm_smd_pkt.c index b6f8a65c9960..8eca55deb3a3 100644 --- a/drivers/char/msm_smd_pkt.c +++ b/drivers/char/msm_smd_pkt.c @@ -379,9 +379,8 @@ static int __init smd_pkt_init(void) for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) { smd_pkt_devp[i] = kzalloc(sizeof(struct smd_pkt_dev), GFP_KERNEL); - if (IS_ERR(smd_pkt_devp[i])) { - r = PTR_ERR(smd_pkt_devp[i]); - pr_err("kmalloc() failed %d\n", r); + if (!smd_pkt_devp[i]) { + pr_err("kmalloc() failed\n"); goto clean_cdevs; } diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index 25d139c9dbed..5c0d96a820fa 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c @@ -46,7 +46,7 @@ #include <asm/page.h> #include <asm/system.h> #include <asm/pgtable.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/tlbflush.h> #include <asm/uncached.h> #include <asm/sn/addrs.h> diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c index 166f1e7aaa7e..da3cfee782dc 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -224,6 +224,8 @@ static loff_t nvram_llseek(struct file *file, loff_t offset, int origin) case 2: offset += NVRAM_BYTES; break; + default: + return -EINVAL; } return (offset >= 0) ? (file->f_pos = offset) : -EINVAL; diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c index 85c004a518ee..d0c57c2e2909 100644 --- a/drivers/char/ps3flash.c +++ b/drivers/char/ps3flash.c @@ -101,12 +101,16 @@ static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin) 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; @@ -305,9 +309,14 @@ static int ps3flash_flush(struct file *file, fl_owner_t id) return ps3flash_writeback(ps3flash_dev); } -static int ps3flash_fsync(struct file *file, int datasync) +static int ps3flash_fsync(struct file *file, loff_t start, loff_t end, int datasync) { - return ps3flash_writeback(ps3flash_dev); + struct inode *inode = file->f_path.dentry->d_inode; + int err; + mutex_lock(&inode->i_mutex); + err = ps3flash_writeback(ps3flash_dev); + mutex_unlock(&inode->i_mutex); + return err; } static irqreturn_t ps3flash_interrupt(int irq, void *data) diff --git a/drivers/char/ramoops.c b/drivers/char/ramoops.c index 1a9f5f6d6ac5..810aff9e750f 100644 --- a/drivers/char/ramoops.c +++ b/drivers/char/ramoops.c @@ -19,18 +19,26 @@ * */ +#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/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 -#define RECORD_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); @@ -52,10 +60,15 @@ static struct ramoops_context { 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) @@ -74,13 +87,13 @@ static void ramoops_do_dump(struct kmsg_dumper *dumper, return; /* Only dump oopses if dump_oops is set */ - if (reason == KMSG_DUMP_OOPS && !dump_oops) + if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops) return; - buf = cxt->virt_addr + (cxt->count * RECORD_SIZE); + buf = cxt->virt_addr + (cxt->count * cxt->record_size); buf_orig = buf; - memset(buf, '\0', RECORD_SIZE); + memset(buf, '\0', cxt->record_size); res = sprintf(buf, "%s", RAMOOPS_KERNMSG_HDR); buf += res; do_gettimeofday(×tamp); @@ -88,8 +101,8 @@ static void ramoops_do_dump(struct kmsg_dumper *dumper, buf += res; hdr_size = buf - buf_orig; - l2_cpy = min(l2, RECORD_SIZE - hdr_size); - l1_cpy = min(l1, RECORD_SIZE - hdr_size - l2_cpy); + 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; @@ -106,44 +119,59 @@ static int __init ramoops_probe(struct platform_device *pdev) struct ramoops_context *cxt = &oops_cxt; int err = -EINVAL; - if (pdata) { - mem_size = pdata->mem_size; - mem_address = pdata->mem_address; + if (!pdata->mem_size || !pdata->record_size) { + pr_err("The memory size and the record size must be " + "non-zero\n"); + goto fail3; } - if (!mem_size) { - printk(KERN_ERR "ramoops: invalid size specification"); + rounddown_pow_of_two(pdata->mem_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; } - rounddown_pow_of_two(mem_size); - - if (mem_size < RECORD_SIZE) { - printk(KERN_ERR "ramoops: size too small"); + 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 = mem_size / RECORD_SIZE; + cxt->max_count = pdata->mem_size / pdata->record_size; cxt->count = 0; - cxt->size = mem_size; - cxt->phys_addr = mem_address; + cxt->size = pdata->mem_size; + cxt->phys_addr = pdata->mem_address; + cxt->record_size = pdata->record_size; + cxt->dump_oops = pdata->dump_oops; + /* + * 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; if (!request_mem_region(cxt->phys_addr, cxt->size, "ramoops")) { - printk(KERN_ERR "ramoops: request mem region failed"); + pr_err("request mem region failed\n"); err = -EINVAL; goto fail3; } cxt->virt_addr = ioremap(cxt->phys_addr, cxt->size); if (!cxt->virt_addr) { - printk(KERN_ERR "ramoops: ioremap failed"); + pr_err("ioremap failed\n"); goto fail2; } cxt->dump.dump = ramoops_do_dump; err = kmsg_dump_register(&cxt->dump); if (err) { - printk(KERN_ERR "ramoops: registering kmsg dumper failed"); + pr_err("registering kmsg dumper failed\n"); goto fail1; } @@ -162,7 +190,7 @@ static int __exit ramoops_remove(struct platform_device *pdev) struct ramoops_context *cxt = &oops_cxt; if (kmsg_dump_unregister(&cxt->dump) < 0) - printk(KERN_WARNING "ramoops: could not unregister kmsg_dumper"); + pr_warn("could not unregister kmsg_dumper\n"); iounmap(cxt->virt_addr); release_mem_region(cxt->phys_addr, cxt->size); @@ -179,12 +207,39 @@ static struct platform_driver ramoops_driver = { static int __init ramoops_init(void) { - return platform_driver_probe(&ramoops_driver, ramoops_probe); + 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); diff --git a/drivers/char/random.c b/drivers/char/random.c index bb587127fb9d..63e19ba56bbe 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1314,330 +1314,14 @@ ctl_table random_table[] = { }; #endif /* CONFIG_SYSCTL */ -/******************************************************************** - * - * Random functions for networking - * - ********************************************************************/ - -/* - * TCP initial sequence number picking. This uses the random number - * generator to pick an initial secret value. This value is hashed - * along with the TCP endpoint information to provide a unique - * starting point for each pair of TCP endpoints. This defeats - * attacks which rely on guessing the initial TCP sequence number. - * This algorithm was suggested by Steve Bellovin. - * - * Using a very strong hash was taking an appreciable amount of the total - * TCP connection establishment time, so this is a weaker hash, - * compensated for by changing the secret periodically. - */ - -/* F, G and H are basic MD4 functions: selection, majority, parity */ -#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) -#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) - -/* - * The generic round function. The application is so specific that - * we don't bother protecting all the arguments with parens, as is generally - * good macro practice, in favor of extra legibility. - * Rotation is separate from addition to prevent recomputation - */ -#define ROUND(f, a, b, c, d, x, s) \ - (a += f(b, c, d) + x, a = (a << s) | (a >> (32 - s))) -#define K1 0 -#define K2 013240474631UL -#define K3 015666365641UL +static u32 random_int_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - -static __u32 twothirdsMD4Transform(__u32 const buf[4], __u32 const in[12]) +static int __init random_int_secret_init(void) { - __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; - - /* Round 1 */ - ROUND(F, a, b, c, d, in[ 0] + K1, 3); - ROUND(F, d, a, b, c, in[ 1] + K1, 7); - ROUND(F, c, d, a, b, in[ 2] + K1, 11); - ROUND(F, b, c, d, a, in[ 3] + K1, 19); - ROUND(F, a, b, c, d, in[ 4] + K1, 3); - ROUND(F, d, a, b, c, in[ 5] + K1, 7); - ROUND(F, c, d, a, b, in[ 6] + K1, 11); - ROUND(F, b, c, d, a, in[ 7] + K1, 19); - ROUND(F, a, b, c, d, in[ 8] + K1, 3); - ROUND(F, d, a, b, c, in[ 9] + K1, 7); - ROUND(F, c, d, a, b, in[10] + K1, 11); - ROUND(F, b, c, d, a, in[11] + K1, 19); - - /* Round 2 */ - ROUND(G, a, b, c, d, in[ 1] + K2, 3); - ROUND(G, d, a, b, c, in[ 3] + K2, 5); - ROUND(G, c, d, a, b, in[ 5] + K2, 9); - ROUND(G, b, c, d, a, in[ 7] + K2, 13); - ROUND(G, a, b, c, d, in[ 9] + K2, 3); - ROUND(G, d, a, b, c, in[11] + K2, 5); - ROUND(G, c, d, a, b, in[ 0] + K2, 9); - ROUND(G, b, c, d, a, in[ 2] + K2, 13); - ROUND(G, a, b, c, d, in[ 4] + K2, 3); - ROUND(G, d, a, b, c, in[ 6] + K2, 5); - ROUND(G, c, d, a, b, in[ 8] + K2, 9); - ROUND(G, b, c, d, a, in[10] + K2, 13); - - /* Round 3 */ - ROUND(H, a, b, c, d, in[ 3] + K3, 3); - ROUND(H, d, a, b, c, in[ 7] + K3, 9); - ROUND(H, c, d, a, b, in[11] + K3, 11); - ROUND(H, b, c, d, a, in[ 2] + K3, 15); - ROUND(H, a, b, c, d, in[ 6] + K3, 3); - ROUND(H, d, a, b, c, in[10] + K3, 9); - ROUND(H, c, d, a, b, in[ 1] + K3, 11); - ROUND(H, b, c, d, a, in[ 5] + K3, 15); - ROUND(H, a, b, c, d, in[ 9] + K3, 3); - ROUND(H, d, a, b, c, in[ 0] + K3, 9); - ROUND(H, c, d, a, b, in[ 4] + K3, 11); - ROUND(H, b, c, d, a, in[ 8] + K3, 15); - - return buf[1] + b; /* "most hashed" word */ - /* Alternative: return sum of all words? */ -} -#endif - -#undef ROUND -#undef F -#undef G -#undef H -#undef K1 -#undef K2 -#undef K3 - -/* This should not be decreased so low that ISNs wrap too fast. */ -#define REKEY_INTERVAL (300 * HZ) -/* - * Bit layout of the tcp sequence numbers (before adding current time): - * bit 24-31: increased after every key exchange - * bit 0-23: hash(source,dest) - * - * The implementation is similar to the algorithm described - * in the Appendix of RFC 1185, except that - * - it uses a 1 MHz clock instead of a 250 kHz clock - * - it performs a rekey every 5 minutes, which is equivalent - * to a (source,dest) tulple dependent forward jump of the - * clock by 0..2^(HASH_BITS+1) - * - * Thus the average ISN wraparound time is 68 minutes instead of - * 4.55 hours. - * - * SMP cleanup and lock avoidance with poor man's RCU. - * Manfred Spraul <manfred@colorfullife.com> - * - */ -#define COUNT_BITS 8 -#define COUNT_MASK ((1 << COUNT_BITS) - 1) -#define HASH_BITS 24 -#define HASH_MASK ((1 << HASH_BITS) - 1) - -static struct keydata { - __u32 count; /* already shifted to the final position */ - __u32 secret[12]; -} ____cacheline_aligned ip_keydata[2]; - -static unsigned int ip_cnt; - -static void rekey_seq_generator(struct work_struct *work); - -static DECLARE_DELAYED_WORK(rekey_work, rekey_seq_generator); - -/* - * Lock avoidance: - * The ISN generation runs lockless - it's just a hash over random data. - * State changes happen every 5 minutes when the random key is replaced. - * Synchronization is performed by having two copies of the hash function - * state and rekey_seq_generator always updates the inactive copy. - * The copy is then activated by updating ip_cnt. - * The implementation breaks down if someone blocks the thread - * that processes SYN requests for more than 5 minutes. Should never - * happen, and even if that happens only a not perfectly compliant - * ISN is generated, nothing fatal. - */ -static void rekey_seq_generator(struct work_struct *work) -{ - struct keydata *keyptr = &ip_keydata[1 ^ (ip_cnt & 1)]; - - get_random_bytes(keyptr->secret, sizeof(keyptr->secret)); - keyptr->count = (ip_cnt & COUNT_MASK) << HASH_BITS; - smp_wmb(); - ip_cnt++; - schedule_delayed_work(&rekey_work, - round_jiffies_relative(REKEY_INTERVAL)); -} - -static inline struct keydata *get_keyptr(void) -{ - struct keydata *keyptr = &ip_keydata[ip_cnt & 1]; - - smp_rmb(); - - return keyptr; -} - -static __init int seqgen_init(void) -{ - rekey_seq_generator(NULL); + get_random_bytes(random_int_secret, sizeof(random_int_secret)); return 0; } -late_initcall(seqgen_init); - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -__u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr, - __be16 sport, __be16 dport) -{ - __u32 seq; - __u32 hash[12]; - struct keydata *keyptr = get_keyptr(); - - /* The procedure is the same as for IPv4, but addresses are longer. - * Thus we must use twothirdsMD4Transform. - */ - - memcpy(hash, saddr, 16); - hash[4] = ((__force u16)sport << 16) + (__force u16)dport; - memcpy(&hash[5], keyptr->secret, sizeof(__u32) * 7); - - seq = twothirdsMD4Transform((const __u32 *)daddr, hash) & HASH_MASK; - seq += keyptr->count; - - seq += ktime_to_ns(ktime_get_real()); - - return seq; -} -EXPORT_SYMBOL(secure_tcpv6_sequence_number); -#endif - -/* The code below is shamelessly stolen from secure_tcp_sequence_number(). - * All blames to Andrey V. Savochkin <saw@msu.ru>. - */ -__u32 secure_ip_id(__be32 daddr) -{ - struct keydata *keyptr; - __u32 hash[4]; - - keyptr = get_keyptr(); - - /* - * Pick a unique starting offset for each IP destination. - * The dest ip address is placed in the starting vector, - * which is then hashed with random data. - */ - hash[0] = (__force __u32)daddr; - hash[1] = keyptr->secret[9]; - hash[2] = keyptr->secret[10]; - hash[3] = keyptr->secret[11]; - - return half_md4_transform(hash, keyptr->secret); -} - -#ifdef CONFIG_INET - -__u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, - __be16 sport, __be16 dport) -{ - __u32 seq; - __u32 hash[4]; - struct keydata *keyptr = get_keyptr(); - - /* - * Pick a unique starting offset for each TCP connection endpoints - * (saddr, daddr, sport, dport). - * Note that the words are placed into the starting vector, which is - * then mixed with a partial MD4 over random data. - */ - hash[0] = (__force u32)saddr; - hash[1] = (__force u32)daddr; - hash[2] = ((__force u16)sport << 16) + (__force u16)dport; - hash[3] = keyptr->secret[11]; - - seq = half_md4_transform(hash, keyptr->secret) & HASH_MASK; - seq += keyptr->count; - /* - * As close as possible to RFC 793, which - * suggests using a 250 kHz clock. - * Further reading shows this assumes 2 Mb/s networks. - * For 10 Mb/s Ethernet, a 1 MHz clock is appropriate. - * For 10 Gb/s Ethernet, a 1 GHz clock should be ok, but - * we also need to limit the resolution so that the u32 seq - * overlaps less than one time per MSL (2 minutes). - * Choosing a clock of 64 ns period is OK. (period of 274 s) - */ - seq += ktime_to_ns(ktime_get_real()) >> 6; - - return seq; -} - -/* Generate secure starting point for ephemeral IPV4 transport port search */ -u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport) -{ - struct keydata *keyptr = get_keyptr(); - u32 hash[4]; - - /* - * Pick a unique starting offset for each ephemeral port search - * (saddr, daddr, dport) and 48bits of random data. - */ - hash[0] = (__force u32)saddr; - hash[1] = (__force u32)daddr; - hash[2] = (__force u32)dport ^ keyptr->secret[10]; - hash[3] = keyptr->secret[11]; - - return half_md4_transform(hash, keyptr->secret); -} -EXPORT_SYMBOL_GPL(secure_ipv4_port_ephemeral); - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, - __be16 dport) -{ - struct keydata *keyptr = get_keyptr(); - u32 hash[12]; - - memcpy(hash, saddr, 16); - hash[4] = (__force u32)dport; - memcpy(&hash[5], keyptr->secret, sizeof(__u32) * 7); - - return twothirdsMD4Transform((const __u32 *)daddr, hash); -} -#endif - -#if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE) -/* Similar to secure_tcp_sequence_number but generate a 48 bit value - * bit's 32-47 increase every key exchange - * 0-31 hash(source, dest) - */ -u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr, - __be16 sport, __be16 dport) -{ - u64 seq; - __u32 hash[4]; - struct keydata *keyptr = get_keyptr(); - - hash[0] = (__force u32)saddr; - hash[1] = (__force u32)daddr; - hash[2] = ((__force u16)sport << 16) + (__force u16)dport; - hash[3] = keyptr->secret[11]; - - seq = half_md4_transform(hash, keyptr->secret); - seq |= ((u64)keyptr->count) << (32 - HASH_BITS); - - seq += ktime_to_ns(ktime_get_real()); - seq &= (1ull << 48) - 1; - - return seq; -} -EXPORT_SYMBOL(secure_dccp_sequence_number); -#endif - -#endif /* CONFIG_INET */ - +late_initcall(random_int_secret_init); /* * Get a random word for internal kernel use only. Similar to urandom but @@ -1645,10 +1329,9 @@ EXPORT_SYMBOL(secure_dccp_sequence_number); * value is not cryptographically secure but for several uses the cost of * depleting entropy is too high */ -DEFINE_PER_CPU(__u32 [4], get_random_int_hash); +DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash); unsigned int get_random_int(void) { - struct keydata *keyptr; __u32 *hash; unsigned int ret; @@ -1657,10 +1340,9 @@ unsigned int get_random_int(void) hash = get_cpu_var(get_random_int_hash); - keyptr = get_keyptr(); hash[0] += current->pid + jiffies + get_cycles(); - - ret = half_md4_transform(hash, keyptr->secret); + md5_transform(hash, random_int_secret); + ret = hash[0]; put_cpu_var(get_random_int_hash); return ret; diff --git a/drivers/char/raw.c b/drivers/char/raw.c index b33e8ea314ed..b6de2c047145 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -324,13 +324,12 @@ static int __init raw_init(void) max_raw_minors = MAX_RAW_MINORS; } - raw_devices = vmalloc(sizeof(struct raw_device_data) * max_raw_minors); + raw_devices = vzalloc(sizeof(struct raw_device_data) * max_raw_minors); if (!raw_devices) { printk(KERN_ERR "Not enough memory for raw device structures\n"); ret = -ENOMEM; goto error; } - memset(raw_devices, 0, sizeof(struct raw_device_data) * max_raw_minors); ret = register_chrdev_region(dev, max_raw_minors, "raw"); if (ret) diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index dfa8b3062fda..ccd124ab7ca7 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -80,6 +80,7 @@ #include <linux/bcd.h> #include <linux/delay.h> #include <linux/uaccess.h> +#include <linux/ratelimit.h> #include <asm/current.h> #include <asm/system.h> @@ -1195,10 +1196,8 @@ static void rtc_dropped_irq(unsigned long data) spin_unlock_irq(&rtc_lock); - if (printk_ratelimit()) { - printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n", - freq); - } + printk_ratelimited(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n", + freq); /* Now we have new data */ wake_up_interruptible(&rtc_wait); diff --git a/drivers/char/scc.h b/drivers/char/scc.h deleted file mode 100644 index 341b1142bea8..000000000000 --- a/drivers/char/scc.h +++ /dev/null @@ -1,613 +0,0 @@ -/* - * atari_SCC.h: Definitions for the Am8530 Serial Communications Controller - * - * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - * - */ - - -#ifndef _SCC_H -#define _SCC_H - -#include <linux/delay.h> - -/* Special configuration ioctls for the Atari SCC5380 Serial - * Communications Controller - */ - -/* ioctl command codes */ - -#define TIOCGATSCC 0x54c0 /* get SCC configuration */ -#define TIOCSATSCC 0x54c1 /* set SCC configuration */ -#define TIOCDATSCC 0x54c2 /* reset configuration to defaults */ - -/* Clock sources */ - -#define CLK_RTxC 0 -#define CLK_TRxC 1 -#define CLK_PCLK 2 - -/* baud_bases for the common clocks in the Atari. These are the real - * frequencies divided by 16. - */ - -#define SCC_BAUD_BASE_TIMC 19200 /* 0.3072 MHz from TT-MFP, Timer C */ -#define SCC_BAUD_BASE_BCLK 153600 /* 2.4576 MHz */ -#define SCC_BAUD_BASE_PCLK4 229500 /* 3.6720 MHz */ -#define SCC_BAUD_BASE_PCLK 503374 /* 8.0539763 MHz */ -#define SCC_BAUD_BASE_NONE 0 /* for not connected or unused - * clock sources */ - -/* The SCC clock configuration structure */ - -struct scc_clock_config { - unsigned RTxC_base; /* base_baud of RTxC */ - unsigned TRxC_base; /* base_baud of TRxC */ - unsigned PCLK_base; /* base_baud of PCLK, both channels! */ - struct { - unsigned clksrc; /* CLK_RTxC, CLK_TRxC or CLK_PCLK */ - unsigned divisor; /* divisor for base baud, valid values: - * see below */ - } baud_table[17]; /* For 50, 75, 110, 135, 150, 200, 300, - * 600, 1200, 1800, 2400, 4800, 9600, - * 19200, 38400, 57600 and 115200 bps. - * The last two could be replaced by - * other rates > 38400 if they're not - * possible. - */ -}; - -/* The following divisors are valid: - * - * - CLK_RTxC: 1 or even (1, 2 and 4 are the direct modes, > 4 use - * the BRG) - * - * - CLK_TRxC: 1, 2 or 4 (no BRG, only direct modes possible) - * - * - CLK_PCLK: >= 4 and even (no direct modes, only BRG) - * - */ - -struct scc_port { - struct gs_port gs; - volatile unsigned char *ctrlp; - volatile unsigned char *datap; - int x_char; /* xon/xoff character */ - int c_dcd; - int channel; - struct scc_port *port_a; /* Reference to port A and B */ - struct scc_port *port_b; /* structs for reg access */ -}; - -#define SCC_MAGIC 0x52696368 - -/***********************************************************************/ -/* */ -/* Register Names */ -/* */ -/***********************************************************************/ - -/* The SCC documentation gives no explicit names to the registers, - * they're just called WR0..15 and RR0..15. To make the source code - * better readable and make the transparent write reg read access (see - * below) possible, I christen them here with self-invented names. - * Note that (real) read registers are assigned numbers 16..31. WR7' - * has number 33. - */ - -#define COMMAND_REG 0 /* wo */ -#define INT_AND_DMA_REG 1 /* wo */ -#define INT_VECTOR_REG 2 /* rw, common to both channels */ -#define RX_CTRL_REG 3 /* rw */ -#define AUX1_CTRL_REG 4 /* rw */ -#define TX_CTRL_REG 5 /* rw */ -#define SYNC_ADR_REG 6 /* wo */ -#define SYNC_CHAR_REG 7 /* wo */ -#define SDLC_OPTION_REG 33 /* wo */ -#define TX_DATA_REG 8 /* wo */ -#define MASTER_INT_CTRL 9 /* wo, common to both channels */ -#define AUX2_CTRL_REG 10 /* rw */ -#define CLK_CTRL_REG 11 /* wo */ -#define TIMER_LOW_REG 12 /* rw */ -#define TIMER_HIGH_REG 13 /* rw */ -#define DPLL_CTRL_REG 14 /* wo */ -#define INT_CTRL_REG 15 /* rw */ - -#define STATUS_REG 16 /* ro */ -#define SPCOND_STATUS_REG 17 /* wo */ -/* RR2 is WR2 for Channel A, Channel B gives vector + current status: */ -#define CURR_VECTOR_REG 18 /* Ch. B only, Ch. A for rw */ -#define INT_PENDING_REG 19 /* Channel A only! */ -/* RR4 is WR4, if b6(MR7') == 1 */ -/* RR5 is WR5, if b6(MR7') == 1 */ -#define FS_FIFO_LOW_REG 22 /* ro */ -#define FS_FIFO_HIGH_REG 23 /* ro */ -#define RX_DATA_REG 24 /* ro */ -/* RR9 is WR3, if b6(MR7') == 1 */ -#define DPLL_STATUS_REG 26 /* ro */ -/* RR11 is WR10, if b6(MR7') == 1 */ -/* RR12 is WR12 */ -/* RR13 is WR13 */ -/* RR14 not present */ -/* RR15 is WR15 */ - - -/***********************************************************************/ -/* */ -/* Register Values */ -/* */ -/***********************************************************************/ - - -/* WR0: COMMAND_REG "CR" */ - -#define CR_RX_CRC_RESET 0x40 -#define CR_TX_CRC_RESET 0x80 -#define CR_TX_UNDERRUN_RESET 0xc0 - -#define CR_EXTSTAT_RESET 0x10 -#define CR_SEND_ABORT 0x18 -#define CR_ENAB_INT_NEXT_RX 0x20 -#define CR_TX_PENDING_RESET 0x28 -#define CR_ERROR_RESET 0x30 -#define CR_HIGHEST_IUS_RESET 0x38 - - -/* WR1: INT_AND_DMA_REG "IDR" */ - -#define IDR_EXTSTAT_INT_ENAB 0x01 -#define IDR_TX_INT_ENAB 0x02 -#define IDR_PARERR_AS_SPCOND 0x04 - -#define IDR_RX_INT_DISAB 0x00 -#define IDR_RX_INT_FIRST 0x08 -#define IDR_RX_INT_ALL 0x10 -#define IDR_RX_INT_SPCOND 0x18 -#define IDR_RX_INT_MASK 0x18 - -#define IDR_WAITREQ_RX 0x20 -#define IDR_WAITREQ_IS_REQ 0x40 -#define IDR_WAITREQ_ENAB 0x80 - - -/* WR3: RX_CTRL_REG "RCR" */ - -#define RCR_RX_ENAB 0x01 -#define RCR_DISCARD_SYNC_CHARS 0x02 -#define RCR_ADDR_SEARCH 0x04 -#define RCR_CRC_ENAB 0x08 -#define RCR_SEARCH_MODE 0x10 -#define RCR_AUTO_ENAB_MODE 0x20 - -#define RCR_CHSIZE_MASK 0xc0 -#define RCR_CHSIZE_5 0x00 -#define RCR_CHSIZE_6 0x40 -#define RCR_CHSIZE_7 0x80 -#define RCR_CHSIZE_8 0xc0 - - -/* WR4: AUX1_CTRL_REG "A1CR" */ - -#define A1CR_PARITY_MASK 0x03 -#define A1CR_PARITY_NONE 0x00 -#define A1CR_PARITY_ODD 0x01 -#define A1CR_PARITY_EVEN 0x03 - -#define A1CR_MODE_MASK 0x0c -#define A1CR_MODE_SYNCR 0x00 -#define A1CR_MODE_ASYNC_1 0x04 -#define A1CR_MODE_ASYNC_15 0x08 -#define A1CR_MODE_ASYNC_2 0x0c - -#define A1CR_SYNCR_MODE_MASK 0x30 -#define A1CR_SYNCR_MONOSYNC 0x00 -#define A1CR_SYNCR_BISYNC 0x10 -#define A1CR_SYNCR_SDLC 0x20 -#define A1CR_SYNCR_EXTCSYNC 0x30 - -#define A1CR_CLKMODE_MASK 0xc0 -#define A1CR_CLKMODE_x1 0x00 -#define A1CR_CLKMODE_x16 0x40 -#define A1CR_CLKMODE_x32 0x80 -#define A1CR_CLKMODE_x64 0xc0 - - -/* WR5: TX_CTRL_REG "TCR" */ - -#define TCR_TX_CRC_ENAB 0x01 -#define TCR_RTS 0x02 -#define TCR_USE_CRC_CCITT 0x00 -#define TCR_USE_CRC_16 0x04 -#define TCR_TX_ENAB 0x08 -#define TCR_SEND_BREAK 0x10 - -#define TCR_CHSIZE_MASK 0x60 -#define TCR_CHSIZE_5 0x00 -#define TCR_CHSIZE_6 0x20 -#define TCR_CHSIZE_7 0x40 -#define TCR_CHSIZE_8 0x60 - -#define TCR_DTR 0x80 - - -/* WR7': SLDC_OPTION_REG "SOR" */ - -#define SOR_AUTO_TX_ENAB 0x01 -#define SOR_AUTO_EOM_RESET 0x02 -#define SOR_AUTO_RTS_MODE 0x04 -#define SOR_NRZI_DISAB_HIGH 0x08 -#define SOR_ALT_DTRREQ_TIMING 0x10 -#define SOR_READ_CRC_CHARS 0x20 -#define SOR_EXTENDED_REG_ACCESS 0x40 - - -/* WR9: MASTER_INT_CTRL "MIC" */ - -#define MIC_VEC_INCL_STAT 0x01 -#define MIC_NO_VECTOR 0x02 -#define MIC_DISAB_LOWER_CHAIN 0x04 -#define MIC_MASTER_INT_ENAB 0x08 -#define MIC_STATUS_HIGH 0x10 -#define MIC_IGN_INTACK 0x20 - -#define MIC_NO_RESET 0x00 -#define MIC_CH_A_RESET 0x40 -#define MIC_CH_B_RESET 0x80 -#define MIC_HARD_RESET 0xc0 - - -/* WR10: AUX2_CTRL_REG "A2CR" */ - -#define A2CR_SYNC_6 0x01 -#define A2CR_LOOP_MODE 0x02 -#define A2CR_ABORT_ON_UNDERRUN 0x04 -#define A2CR_MARK_IDLE 0x08 -#define A2CR_GO_ACTIVE_ON_POLL 0x10 - -#define A2CR_CODING_MASK 0x60 -#define A2CR_CODING_NRZ 0x00 -#define A2CR_CODING_NRZI 0x20 -#define A2CR_CODING_FM1 0x40 -#define A2CR_CODING_FM0 0x60 - -#define A2CR_PRESET_CRC_1 0x80 - - -/* WR11: CLK_CTRL_REG "CCR" */ - -#define CCR_TRxCOUT_MASK 0x03 -#define CCR_TRxCOUT_XTAL 0x00 -#define CCR_TRxCOUT_TXCLK 0x01 -#define CCR_TRxCOUT_BRG 0x02 -#define CCR_TRxCOUT_DPLL 0x03 - -#define CCR_TRxC_OUTPUT 0x04 - -#define CCR_TXCLK_MASK 0x18 -#define CCR_TXCLK_RTxC 0x00 -#define CCR_TXCLK_TRxC 0x08 -#define CCR_TXCLK_BRG 0x10 -#define CCR_TXCLK_DPLL 0x18 - -#define CCR_RXCLK_MASK 0x60 -#define CCR_RXCLK_RTxC 0x00 -#define CCR_RXCLK_TRxC 0x20 -#define CCR_RXCLK_BRG 0x40 -#define CCR_RXCLK_DPLL 0x60 - -#define CCR_RTxC_XTAL 0x80 - - -/* WR14: DPLL_CTRL_REG "DCR" */ - -#define DCR_BRG_ENAB 0x01 -#define DCR_BRG_USE_PCLK 0x02 -#define DCR_DTRREQ_IS_REQ 0x04 -#define DCR_AUTO_ECHO 0x08 -#define DCR_LOCAL_LOOPBACK 0x10 - -#define DCR_DPLL_EDGE_SEARCH 0x20 -#define DCR_DPLL_ERR_RESET 0x40 -#define DCR_DPLL_DISAB 0x60 -#define DCR_DPLL_CLK_BRG 0x80 -#define DCR_DPLL_CLK_RTxC 0xa0 -#define DCR_DPLL_FM 0xc0 -#define DCR_DPLL_NRZI 0xe0 - - -/* WR15: INT_CTRL_REG "ICR" */ - -#define ICR_OPTIONREG_SELECT 0x01 -#define ICR_ENAB_BRG_ZERO_INT 0x02 -#define ICR_USE_FS_FIFO 0x04 -#define ICR_ENAB_DCD_INT 0x08 -#define ICR_ENAB_SYNC_INT 0x10 -#define ICR_ENAB_CTS_INT 0x20 -#define ICR_ENAB_UNDERRUN_INT 0x40 -#define ICR_ENAB_BREAK_INT 0x80 - - -/* RR0: STATUS_REG "SR" */ - -#define SR_CHAR_AVAIL 0x01 -#define SR_BRG_ZERO 0x02 -#define SR_TX_BUF_EMPTY 0x04 -#define SR_DCD 0x08 -#define SR_SYNC_ABORT 0x10 -#define SR_CTS 0x20 -#define SR_TX_UNDERRUN 0x40 -#define SR_BREAK 0x80 - - -/* RR1: SPCOND_STATUS_REG "SCSR" */ - -#define SCSR_ALL_SENT 0x01 -#define SCSR_RESIDUAL_MASK 0x0e -#define SCSR_PARITY_ERR 0x10 -#define SCSR_RX_OVERRUN 0x20 -#define SCSR_CRC_FRAME_ERR 0x40 -#define SCSR_END_OF_FRAME 0x80 - - -/* RR3: INT_PENDING_REG "IPR" */ - -#define IPR_B_EXTSTAT 0x01 -#define IPR_B_TX 0x02 -#define IPR_B_RX 0x04 -#define IPR_A_EXTSTAT 0x08 -#define IPR_A_TX 0x10 -#define IPR_A_RX 0x20 - - -/* RR7: FS_FIFO_HIGH_REG "FFHR" */ - -#define FFHR_CNT_MASK 0x3f -#define FFHR_IS_FROM_FIFO 0x40 -#define FFHR_FIFO_OVERRUN 0x80 - - -/* RR10: DPLL_STATUS_REG "DSR" */ - -#define DSR_ON_LOOP 0x02 -#define DSR_ON_LOOP_SENDING 0x10 -#define DSR_TWO_CLK_MISSING 0x40 -#define DSR_ONE_CLK_MISSING 0x80 - -/***********************************************************************/ -/* */ -/* Register Access */ -/* */ -/***********************************************************************/ - - -/* The SCC needs 3.5 PCLK cycles recovery time between to register - * accesses. PCLK runs with 8 MHz on an Atari, so this delay is 3.5 * - * 125 ns = 437.5 ns. This is too short for udelay(). - * 10/16/95: A tstb st_mfp.par_dt_reg takes 600ns (sure?) and thus should be - * quite right - */ - -#define scc_reg_delay() \ - do { \ - if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) \ - __asm__ __volatile__ ( " nop; nop"); \ - else if (MACH_IS_ATARI) \ - __asm__ __volatile__ ( "tstb %0" : : "g" (*_scc_del) : "cc" );\ - } while (0) - -static unsigned char scc_shadow[2][16]; - -/* The following functions should relax the somehow complicated - * register access of the SCC. _SCCwrite() stores all written values - * (except for WR0 and WR8) in shadow registers for later recall. This - * removes the burden of remembering written values as needed. The - * extra work of storing the value doesn't count, since a delay is - * needed after a SCC access anyway. Additionally, _SCCwrite() manages - * writes to WR0 and WR8 differently, because these can be accessed - * directly with less overhead. Another special case are WR7 and WR7'. - * _SCCwrite automatically checks what of this registers is selected - * and changes b0 of WR15 if needed. - * - * _SCCread() for standard read registers is straightforward, except - * for RR2 (split into two "virtual" registers: one for the value - * written to WR2 (from the shadow) and one for the vector including - * status from RR2, Ch. B) and RR3. The latter must be read from - * Channel A, because it reads as all zeros on Ch. B. RR0 and RR8 can - * be accessed directly as before. - * - * The two inline function contain complicated switch statements. But - * I rely on regno and final_delay being constants, so gcc can reduce - * the whole stuff to just some assembler statements. - * - * _SCCwrite and _SCCread aren't intended to be used directly under - * normal circumstances. The macros SCCread[_ND] and SCCwrite[_ND] are - * for that purpose. They assume that a local variable 'port' is - * declared and pointing to the port's scc_struct entry. The - * variants with "_NB" appended should be used if no other SCC - * accesses follow immediately (within 0.5 usecs). They just skip the - * final delay nops. - * - * Please note that accesses to SCC registers should only take place - * when interrupts are turned off (at least if SCC interrupts are - * enabled). Otherwise, an interrupt could interfere with the - * two-stage accessing process. - * - */ - - -static __inline__ void _SCCwrite( - struct scc_port *port, - unsigned char *shadow, - volatile unsigned char *_scc_del, - int regno, - unsigned char val, int final_delay ) -{ - switch( regno ) { - - case COMMAND_REG: - /* WR0 can be written directly without pointing */ - *port->ctrlp = val; - break; - - case SYNC_CHAR_REG: - /* For WR7, first set b0 of WR15 to 0, if needed */ - if (shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT) { - *port->ctrlp = 15; - shadow[INT_CTRL_REG] &= ~ICR_OPTIONREG_SELECT; - scc_reg_delay(); - *port->ctrlp = shadow[INT_CTRL_REG]; - scc_reg_delay(); - } - goto normal_case; - - case SDLC_OPTION_REG: - /* For WR7', first set b0 of WR15 to 1, if needed */ - if (!(shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT)) { - *port->ctrlp = 15; - shadow[INT_CTRL_REG] |= ICR_OPTIONREG_SELECT; - scc_reg_delay(); - *port->ctrlp = shadow[INT_CTRL_REG]; - scc_reg_delay(); - } - *port->ctrlp = 7; - shadow[8] = val; /* WR7' shadowed at WR8 */ - scc_reg_delay(); - *port->ctrlp = val; - break; - - case TX_DATA_REG: /* WR8 */ - /* TX_DATA_REG can be accessed directly on some h/w */ - if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) - { - *port->ctrlp = regno; - scc_reg_delay(); - *port->ctrlp = val; - } - else - *port->datap = val; - break; - - case MASTER_INT_CTRL: - *port->ctrlp = regno; - val &= 0x3f; /* bits 6..7 are the reset commands */ - scc_shadow[0][regno] = val; - scc_reg_delay(); - *port->ctrlp = val; - break; - - case DPLL_CTRL_REG: - *port->ctrlp = regno; - val &= 0x1f; /* bits 5..7 are the DPLL commands */ - shadow[regno] = val; - scc_reg_delay(); - *port->ctrlp = val; - break; - - case 1 ... 6: - case 10 ... 13: - case 15: - normal_case: - *port->ctrlp = regno; - shadow[regno] = val; - scc_reg_delay(); - *port->ctrlp = val; - break; - - default: - printk( "Bad SCC write access to WR%d\n", regno ); - break; - - } - - if (final_delay) - scc_reg_delay(); -} - - -static __inline__ unsigned char _SCCread( - struct scc_port *port, - unsigned char *shadow, - volatile unsigned char *_scc_del, - int regno, int final_delay ) -{ - unsigned char rv; - - switch( regno ) { - - /* --- real read registers --- */ - case STATUS_REG: - rv = *port->ctrlp; - break; - - case INT_PENDING_REG: - /* RR3: read only from Channel A! */ - port = port->port_a; - goto normal_case; - - case RX_DATA_REG: - /* RR8 can be accessed directly on some h/w */ - if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) - { - *port->ctrlp = 8; - scc_reg_delay(); - rv = *port->ctrlp; - } - else - rv = *port->datap; - break; - - case CURR_VECTOR_REG: - /* RR2 (vector including status) from Ch. B */ - port = port->port_b; - goto normal_case; - - /* --- reading write registers: access the shadow --- */ - case 1 ... 7: - case 10 ... 15: - return shadow[regno]; /* no final delay! */ - - /* WR7' is special, because it is shadowed at the place of WR8 */ - case SDLC_OPTION_REG: - return shadow[8]; /* no final delay! */ - - /* WR9 is special too, because it is common for both channels */ - case MASTER_INT_CTRL: - return scc_shadow[0][9]; /* no final delay! */ - - default: - printk( "Bad SCC read access to %cR%d\n", (regno & 16) ? 'R' : 'W', - regno & ~16 ); - break; - - case SPCOND_STATUS_REG: - case FS_FIFO_LOW_REG: - case FS_FIFO_HIGH_REG: - case DPLL_STATUS_REG: - normal_case: - *port->ctrlp = regno & 0x0f; - scc_reg_delay(); - rv = *port->ctrlp; - break; - - } - - if (final_delay) - scc_reg_delay(); - return rv; -} - -#define SCC_ACCESS_INIT(port) \ - unsigned char *_scc_shadow = &scc_shadow[port->channel][0] - -#define SCCwrite(reg,val) _SCCwrite(port,_scc_shadow,scc_del,(reg),(val),1) -#define SCCwrite_NB(reg,val) _SCCwrite(port,_scc_shadow,scc_del,(reg),(val),0) -#define SCCread(reg) _SCCread(port,_scc_shadow,scc_del,(reg),1) -#define SCCread_NB(reg) _SCCread(port,_scc_shadow,scc_del,(reg),0) - -#define SCCmod(reg,and,or) SCCwrite((reg),(SCCread(reg)&(and))|(or)) - -#endif /* _SCC_H */ diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c new file mode 100644 index 000000000000..cf3ee008dca2 --- /dev/null +++ b/drivers/char/tile-srom.c @@ -0,0 +1,481 @@ +/* + * Copyright 2011 Tilera Corporation. 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 + * as published by the Free Software Foundation, version 2. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + * + * SPI Flash ROM driver + * + * This source code is derived from code provided in "Linux Device + * Drivers, Third Edition", by Jonathan Corbet, Alessandro Rubini, and + * Greg Kroah-Hartman, published by O'Reilly Media, Inc. + */ + +#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... */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/proc_fs.h> +#include <linux/fcntl.h> /* O_ACCMODE */ +#include <linux/aio.h> +#include <linux/pagemap.h> +#include <linux/hugetlb.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> +#include <hv/hypervisor.h> +#include <linux/ioctl.h> +#include <linux/cdev.h> +#include <linux/delay.h> +#include <hv/drv_srom_intf.h> + +/* + * Size of our hypervisor I/O requests. We break up large transfers + * so that we don't spend large uninterrupted spans of time in the + * hypervisor. Erasing an SROM sector takes a significant fraction of + * a second, so if we allowed the user to, say, do one I/O to write the + * entire ROM, we'd get soft lockup timeouts, or worse. + */ +#define SROM_CHUNK_SIZE ((size_t)4096) + +/* + * When hypervisor is busy (e.g. erasing), poll the status periodically. + */ + +/* + * Interval to poll the state in msec + */ +#define SROM_WAIT_TRY_INTERVAL 20 + +/* + * Maximum times to poll the state + */ +#define SROM_MAX_WAIT_TRY_TIMES 1000 + +struct srom_dev { + int hv_devhdl; /* Handle for hypervisor device */ + u32 total_size; /* Size of this device */ + u32 sector_size; /* Size of a sector */ + u32 page_size; /* Size of a page */ + struct mutex lock; /* Allow only one accessor at a time */ +}; + +static int srom_major; /* Dynamic major by default */ +module_param(srom_major, int, 0); +MODULE_AUTHOR("Tilera Corporation"); +MODULE_LICENSE("GPL"); + +static int srom_devs; /* Number of SROM partitions */ +static struct cdev srom_cdev; +static struct class *srom_class; +static struct srom_dev *srom_devices; + +/* + * Handle calling the hypervisor and managing EAGAIN/EBUSY. + */ + +static ssize_t _srom_read(int hv_devhdl, void *buf, + loff_t off, size_t count) +{ + int retval, retries = SROM_MAX_WAIT_TRY_TIMES; + for (;;) { + retval = hv_dev_pread(hv_devhdl, 0, (HV_VirtAddr)buf, + count, off); + if (retval >= 0) + return retval; + if (retval == HV_EAGAIN) + continue; + if (retval == HV_EBUSY && --retries > 0) { + msleep(SROM_WAIT_TRY_INTERVAL); + continue; + } + pr_err("_srom_read: error %d\n", retval); + return -EIO; + } +} + +static ssize_t _srom_write(int hv_devhdl, const void *buf, + loff_t off, size_t count) +{ + int retval, retries = SROM_MAX_WAIT_TRY_TIMES; + for (;;) { + retval = hv_dev_pwrite(hv_devhdl, 0, (HV_VirtAddr)buf, + count, off); + if (retval >= 0) + return retval; + if (retval == HV_EAGAIN) + continue; + if (retval == HV_EBUSY && --retries > 0) { + msleep(SROM_WAIT_TRY_INTERVAL); + continue; + } + pr_err("_srom_write: error %d\n", retval); + return -EIO; + } +} + +/** + * srom_open() - Device open routine. + * @inode: Inode for this device. + * @filp: File for this specific open of the device. + * + * Returns zero, or an error code. + */ +static int srom_open(struct inode *inode, struct file *filp) +{ + filp->private_data = &srom_devices[iminor(inode)]; + return 0; +} + + +/** + * srom_release() - Device release routine. + * @inode: Inode for this device. + * @filp: File for this specific open of the device. + * + * Returns zero, or an error code. + */ +static int srom_release(struct inode *inode, struct file *filp) +{ + struct srom_dev *srom = filp->private_data; + char dummy; + + /* Make sure we've flushed anything written to the ROM. */ + mutex_lock(&srom->lock); + if (srom->hv_devhdl >= 0) + _srom_write(srom->hv_devhdl, &dummy, SROM_FLUSH_OFF, 1); + mutex_unlock(&srom->lock); + + filp->private_data = NULL; + + return 0; +} + + +/** + * srom_read() - Read data from the device. + * @filp: File for this specific open of the device. + * @buf: User's data buffer. + * @count: Number of bytes requested. + * @f_pos: File position. + * + * Returns number of bytes read, or an error code. + */ +static ssize_t srom_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + int retval = 0; + void *kernbuf; + struct srom_dev *srom = filp->private_data; + + kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL); + if (!kernbuf) + return -ENOMEM; + + if (mutex_lock_interruptible(&srom->lock)) { + retval = -ERESTARTSYS; + kfree(kernbuf); + return retval; + } + + while (count) { + int hv_retval; + int bytes_this_pass = min(count, SROM_CHUNK_SIZE); + + 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 (retval == 0) + retval = hv_retval; + break; + } + + retval += hv_retval; + *f_pos += hv_retval; + buf += hv_retval; + count -= hv_retval; + } + + mutex_unlock(&srom->lock); + kfree(kernbuf); + + return retval; +} + +/** + * srom_write() - Write data to the device. + * @filp: File for this specific open of the device. + * @buf: User's data buffer. + * @count: Number of bytes requested. + * @f_pos: File position. + * + * Returns number of bytes written, or an error code. + */ +static ssize_t srom_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + int retval = 0; + void *kernbuf; + struct srom_dev *srom = filp->private_data; + + kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL); + if (!kernbuf) + return -ENOMEM; + + if (mutex_lock_interruptible(&srom->lock)) { + retval = -ERESTARTSYS; + kfree(kernbuf); + return retval; + } + + while (count) { + int hv_retval; + int bytes_this_pass = min(count, SROM_CHUNK_SIZE); + + if (copy_from_user(kernbuf, buf, bytes_this_pass) != 0) { + retval = -EFAULT; + break; + } + + hv_retval = _srom_write(srom->hv_devhdl, kernbuf, + *f_pos, bytes_this_pass); + if (hv_retval <= 0) { + if (retval == 0) + retval = hv_retval; + break; + } + + retval += hv_retval; + *f_pos += hv_retval; + buf += hv_retval; + count -= hv_retval; + } + + mutex_unlock(&srom->lock); + kfree(kernbuf); + + return retval; +} + +/* Provide our own implementation so we can use srom->total_size. */ +loff_t srom_llseek(struct file *filp, 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; +} + +static ssize_t total_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 ssize_t sector_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 ssize_t page_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 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 char *srom_devnode(struct device *dev, mode_t *mode) +{ + *mode = S_IRUGO | S_IWUSR; + return kasprintf(GFP_KERNEL, "srom/%s", dev_name(dev)); +} + +/* + * The fops + */ +static const struct file_operations srom_fops = { + .owner = THIS_MODULE, + .llseek = srom_llseek, + .read = srom_read, + .write = srom_write, + .open = srom_open, + .release = srom_release, +}; + +/** + * srom_setup_minor() - Initialize per-minor information. + * @srom: Per-device SROM state. + * @index: Device to set up. + */ +static int srom_setup_minor(struct srom_dev *srom, int index) +{ + struct device *dev; + int devhdl = srom->hv_devhdl; + + mutex_init(&srom->lock); + + if (_srom_read(devhdl, &srom->total_size, + SROM_TOTAL_SIZE_OFF, sizeof(srom->total_size)) < 0) + return -EIO; + if (_srom_read(devhdl, &srom->sector_size, + SROM_SECTOR_SIZE_OFF, sizeof(srom->sector_size)) < 0) + return -EIO; + if (_srom_read(devhdl, &srom->page_size, + SROM_PAGE_SIZE_OFF, sizeof(srom->page_size)) < 0) + return -EIO; + + dev = device_create(srom_class, &platform_bus, + MKDEV(srom_major, index), srom, "%d", index); + return IS_ERR(dev) ? PTR_ERR(dev) : 0; +} + +/** srom_init() - Initialize the driver's module. */ +static int srom_init(void) +{ + int result, i; + dev_t dev = MKDEV(srom_major, 0); + + /* + * Start with a plausible number of partitions; the krealloc() call + * below will yield about log(srom_devs) additional allocations. + */ + srom_devices = kzalloc(4 * sizeof(struct srom_dev), GFP_KERNEL); + + /* Discover the number of srom partitions. */ + for (i = 0; ; i++) { + int devhdl; + char buf[20]; + struct srom_dev *new_srom_devices = + krealloc(srom_devices, (i+1) * sizeof(struct srom_dev), + GFP_KERNEL | __GFP_ZERO); + if (!new_srom_devices) { + result = -ENOMEM; + goto fail_mem; + } + srom_devices = new_srom_devices; + sprintf(buf, "srom/0/%d", i); + devhdl = hv_dev_open((HV_VirtAddr)buf, 0); + if (devhdl < 0) { + if (devhdl != HV_ENODEV) + pr_notice("srom/%d: hv_dev_open failed: %d.\n", + i, devhdl); + break; + } + srom_devices[i].hv_devhdl = devhdl; + } + srom_devs = i; + + /* Bail out early if we have no partitions at all. */ + if (srom_devs == 0) { + result = -ENODEV; + goto fail_mem; + } + + /* Register our major, and accept a dynamic number. */ + if (srom_major) + result = register_chrdev_region(dev, srom_devs, "srom"); + else { + result = alloc_chrdev_region(&dev, 0, srom_devs, "srom"); + srom_major = MAJOR(dev); + } + if (result < 0) + goto fail_mem; + + /* Register a character device. */ + cdev_init(&srom_cdev, &srom_fops); + srom_cdev.owner = THIS_MODULE; + srom_cdev.ops = &srom_fops; + result = cdev_add(&srom_cdev, dev, srom_devs); + if (result < 0) + goto fail_chrdev; + + /* Create a sysfs class. */ + srom_class = class_create(THIS_MODULE, "srom"); + if (IS_ERR(srom_class)) { + result = PTR_ERR(srom_class); + goto fail_cdev; + } + srom_class->dev_attrs = srom_dev_attrs; + srom_class->devnode = srom_devnode; + + /* Do per-partition initialization */ + for (i = 0; i < srom_devs; i++) { + result = srom_setup_minor(srom_devices + i, i); + if (result < 0) + goto fail_class; + } + + return 0; + +fail_class: + for (i = 0; i < srom_devs; i++) + device_destroy(srom_class, MKDEV(srom_major, i)); + class_destroy(srom_class); +fail_cdev: + cdev_del(&srom_cdev); +fail_chrdev: + unregister_chrdev_region(dev, srom_devs); +fail_mem: + kfree(srom_devices); + return result; +} + +/** srom_cleanup() - Clean up the driver's module. */ +static void srom_cleanup(void) +{ + int i; + for (i = 0; i < srom_devs; i++) + device_destroy(srom_class, MKDEV(srom_major, i)); + class_destroy(srom_class); + cdev_del(&srom_cdev); + unregister_chrdev_region(MKDEV(srom_major, 0), srom_devs); + kfree(srom_devices); +} + +module_init(srom_init); +module_exit(srom_cleanup); diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index f6595aba4f0f..fa567f1158c2 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -43,6 +43,7 @@ config TCG_NSC config TCG_ATMEL tristate "Atmel TPM Interface" + depends on PPC64 || HAS_IOPORT ---help--- If you have a TPM security chip from Atmel say Yes and it will be accessible from within Linux. To compile this driver diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 7beb0e25f1e1..361a1dff8f77 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -383,6 +383,9 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, u32 count, ordinal; unsigned long stop; + if (bufsiz > TPM_BUFSIZE) + bufsiz = TPM_BUFSIZE; + count = be32_to_cpu(*((__be32 *) (buf + 2))); ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); if (count == 0) @@ -534,6 +537,7 @@ void tpm_get_timeouts(struct tpm_chip *chip) struct duration_t *duration_cap; ssize_t rc; u32 timeout; + unsigned int scale = 1; tpm_cmd.header.in = tpm_getcap_header; tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; @@ -545,24 +549,30 @@ void tpm_get_timeouts(struct tpm_chip *chip) if (rc) goto duration; - if (be32_to_cpu(tpm_cmd.header.out.length) - != 4 * sizeof(u32)) - goto duration; + if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 || + be32_to_cpu(tpm_cmd.header.out.length) + != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32)) + return; timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout; /* Don't overwrite default if value is 0 */ timeout = be32_to_cpu(timeout_cap->a); + if (timeout && timeout < 1000) { + /* timeouts in msec rather usec */ + scale = 1000; + chip->vendor.timeout_adjusted = true; + } if (timeout) - chip->vendor.timeout_a = usecs_to_jiffies(timeout); + chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale); timeout = be32_to_cpu(timeout_cap->b); if (timeout) - chip->vendor.timeout_b = usecs_to_jiffies(timeout); + chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale); timeout = be32_to_cpu(timeout_cap->c); if (timeout) - chip->vendor.timeout_c = usecs_to_jiffies(timeout); + chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale); timeout = be32_to_cpu(timeout_cap->d); if (timeout) - chip->vendor.timeout_d = usecs_to_jiffies(timeout); + chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale); duration: tpm_cmd.header.in = tpm_getcap_header; @@ -575,23 +585,31 @@ duration: if (rc) return; - if (be32_to_cpu(tpm_cmd.header.out.return_code) - != 3 * sizeof(u32)) + if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 || + be32_to_cpu(tpm_cmd.header.out.length) + != sizeof(tpm_cmd.header.out) + sizeof(u32) + 3 * sizeof(u32)) return; + duration_cap = &tpm_cmd.params.getcap_out.cap.duration; chip->vendor.duration[TPM_SHORT] = usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short)); + chip->vendor.duration[TPM_MEDIUM] = + usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium)); + chip->vendor.duration[TPM_LONG] = + usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long)); + /* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above * value wrong and apparently reports msecs rather than usecs. So we * fix up the resulting too-small TPM_SHORT value to make things work. + * We also scale the TPM_MEDIUM and -_LONG values by 1000. */ - if (chip->vendor.duration[TPM_SHORT] < (HZ/100)) + if (chip->vendor.duration[TPM_SHORT] < (HZ / 100)) { chip->vendor.duration[TPM_SHORT] = HZ; - - chip->vendor.duration[TPM_MEDIUM] = - usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium)); - chip->vendor.duration[TPM_LONG] = - usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long)); + chip->vendor.duration[TPM_MEDIUM] *= 1000; + chip->vendor.duration[TPM_LONG] *= 1000; + chip->vendor.duration_adjusted = true; + dev_info(chip->dev, "Adjusting TPM timeout parameters."); + } } EXPORT_SYMBOL_GPL(tpm_get_timeouts); @@ -600,7 +618,7 @@ void tpm_continue_selftest(struct tpm_chip *chip) u8 data[] = { 0, 193, /* TPM_TAG_RQU_COMMAND */ 0, 0, 0, 10, /* length */ - 0, 0, 0, 83, /* TPM_ORD_GetCapability */ + 0, 0, 0, 83, /* TPM_ORD_ContinueSelfTest */ }; tpm_transmit(chip, data, sizeof(data)); @@ -863,18 +881,24 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr, data = tpm_cmd.params.readpubek_out_buffer; str += sprintf(str, - "Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n" - "Sigscheme: %02X %02X\nParameters: %02X %02X %02X %02X" - " %02X %02X %02X %02X %02X %02X %02X %02X\n" - "Modulus length: %d\nModulus: \n", - data[10], data[11], data[12], data[13], data[14], - data[15], data[16], data[17], data[22], data[23], - data[24], data[25], data[26], data[27], data[28], - data[29], data[30], data[31], data[32], data[33], - be32_to_cpu(*((__be32 *) (data + 34)))); + "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 + 38]); + str += sprintf(str, "%02X ", data[i + 28]); if ((i + 1) % 16 == 0) str += sprintf(str, "\n"); } @@ -937,6 +961,38 @@ ssize_t tpm_show_caps_1_2(struct device * dev, } 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) { @@ -1052,6 +1108,7 @@ ssize_t tpm_read(struct file *file, char __user *buf, { 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); @@ -1062,8 +1119,11 @@ ssize_t tpm_read(struct file *file, char __user *buf, ret_size = size; mutex_lock(&chip->buffer_mutex); - if (copy_to_user(buf, chip->data_buffer, ret_size)) + 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); } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 72ddb031b69a..9c4163cfa3ce 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -56,6 +56,10 @@ 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; @@ -67,6 +71,7 @@ struct tpm_vendor_specific { unsigned long base; /* TPM base address */ int irq; + int probed_irq; int region_size; int have_region; @@ -81,7 +86,9 @@ struct tpm_vendor_specific { 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; wait_queue_head_t read_queue; wait_queue_head_t int_queue; diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index a605cb7dd898..4d2464871ada 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -330,12 +330,12 @@ static int __init init_nsc(void) pdev->dev.driver = &nsc_drv.driver; pdev->dev.release = tpm_nsc_remove; - if ((rc = platform_device_register(pdev)) < 0) - goto err_free_dev; + if ((rc = platform_device_add(pdev)) < 0) + goto err_put_dev; if (request_region(base, 2, "tpm_nsc0") == NULL ) { rc = -EBUSY; - goto err_unreg_dev; + goto err_del_dev; } if (!(chip = tpm_register_hardware(&pdev->dev, &tpm_nsc))) { @@ -382,10 +382,10 @@ static int __init init_nsc(void) err_rel_reg: release_region(base, 2); -err_unreg_dev: - platform_device_unregister(pdev); -err_free_dev: - kfree(pdev); +err_del_dev: + platform_device_del(pdev); +err_put_dev: + platform_device_put(pdev); err_unreg_drv: platform_driver_unregister(&nsc_drv); return rc; @@ -396,8 +396,6 @@ static void __exit cleanup_nsc(void) if (pdev) { tpm_nsc_remove(&pdev->dev); platform_device_unregister(pdev); - kfree(pdev); - pdev = NULL; } platform_driver_unregister(&nsc_drv); diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index dd21df55689d..3f4051a7c5a7 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -26,6 +26,7 @@ #include <linux/interrupt.h> #include <linux/wait.h> #include <linux/acpi.h> +#include <linux/freezer.h> #include "tpm.h" #define TPM_HEADER_SIZE 10 @@ -79,7 +80,7 @@ enum tis_defaults { static LIST_HEAD(tis_chips); static DEFINE_SPINLOCK(tis_lock); -#ifdef CONFIG_ACPI +#if defined(CONFIG_PNP) && defined(CONFIG_ACPI) static int is_itpm(struct pnp_dev *dev) { struct acpi_device *acpi = pnp_acpi_device(dev); @@ -93,7 +94,7 @@ static int is_itpm(struct pnp_dev *dev) return 0; } #else -static int is_itpm(struct pnp_dev *dev) +static inline int is_itpm(struct pnp_dev *dev) { return 0; } @@ -120,7 +121,7 @@ static void release_locality(struct tpm_chip *chip, int l, int force) static int request_locality(struct tpm_chip *chip, int l) { - unsigned long stop; + unsigned long stop, timeout; long rc; if (check_locality(chip, l) >= 0) @@ -129,17 +130,25 @@ static int request_locality(struct tpm_chip *chip, int l) iowrite8(TPM_ACCESS_REQUEST_USE, chip->vendor.iobase + TPM_ACCESS(l)); + stop = jiffies + chip->vendor.timeout_a; + if (chip->vendor.irq) { +again: + timeout = stop - jiffies; + if ((long)timeout <= 0) + return -1; rc = wait_event_interruptible_timeout(chip->vendor.int_queue, (check_locality (chip, l) >= 0), - chip->vendor.timeout_a); + timeout); if (rc > 0) return l; - + if (rc == -ERESTARTSYS && freezing(current)) { + clear_thread_flag(TIF_SIGPENDING); + goto again; + } } else { /* wait for burstcount */ - stop = jiffies + chip->vendor.timeout_a; do { if (check_locality(chip, l) >= 0) return l; @@ -196,15 +205,24 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, if ((status & mask) == mask) return 0; + stop = jiffies + timeout; + if (chip->vendor.irq) { +again: + timeout = stop - jiffies; + if ((long)timeout <= 0) + return -ETIME; rc = wait_event_interruptible_timeout(*queue, ((tpm_tis_status (chip) & mask) == mask), timeout); if (rc > 0) return 0; + if (rc == -ERESTARTSYS && freezing(current)) { + clear_thread_flag(TIF_SIGPENDING); + goto again; + } } else { - stop = jiffies + timeout; do { msleep(TPM_TIMEOUT); status = tpm_tis_status(chip); @@ -288,11 +306,10 @@ MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)"); * tpm.c can skip polling for the data to be available as the interrupt is * waited for here */ -static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) { int rc, status, burstcnt; size_t count = 0; - u32 ordinal; if (request_locality(chip, 0) < 0) return -EBUSY; @@ -327,8 +344,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) /* write last byte */ iowrite8(buf[count], - chip->vendor.iobase + - TPM_DATA_FIFO(chip->vendor.locality)); + chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality)); wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &chip->vendor.int_queue); status = tpm_tis_status(chip); @@ -337,6 +353,28 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) goto out_err; } + return 0; + +out_err: + tpm_tis_ready(chip); + release_locality(chip, chip->vendor.locality, 0); + return rc; +} + +/* + * 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 tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) +{ + int rc; + u32 ordinal; + + rc = tpm_tis_send_data(chip, buf, len); + if (rc < 0) + return rc; + /* go and do it */ iowrite8(TPM_STS_GO, chip->vendor.iobase + TPM_STS(chip->vendor.locality)); @@ -358,6 +396,47 @@ out_err: return rc; } +/* + * Early probing for iTPM with STS_DATA_EXPECT flaw. + * Try sending command without itpm flag set and if that + * fails, repeat with itpm flag set. + */ +static int probe_itpm(struct tpm_chip *chip) +{ + int rc = 0; + u8 cmd_getticks[] = { + 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x00, 0xf1 + }; + size_t len = sizeof(cmd_getticks); + int rem_itpm = itpm; + + itpm = 0; + + rc = tpm_tis_send_data(chip, cmd_getticks, len); + if (rc == 0) + goto out; + + tpm_tis_ready(chip); + release_locality(chip, chip->vendor.locality, 0); + + itpm = 1; + + rc = tpm_tis_send_data(chip, cmd_getticks, len); + if (rc == 0) { + dev_info(chip->dev, "Detected an iTPM.\n"); + rc = 1; + } else + rc = -EFAULT; + +out: + itpm = rem_itpm; + tpm_tis_ready(chip); + release_locality(chip, chip->vendor.locality, 0); + + return rc; +} + static const struct file_operations tis_ops = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -376,6 +455,8 @@ 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, @@ -385,7 +466,9 @@ static struct attribute *tis_attrs[] = { &dev_attr_owned.attr, &dev_attr_temp_deactivated.attr, &dev_attr_caps.attr, - &dev_attr_cancel.attr, NULL, + &dev_attr_cancel.attr, + &dev_attr_durations.attr, + &dev_attr_timeouts.attr, NULL, }; static struct attribute_group tis_attr_grp = { @@ -416,7 +499,7 @@ static irqreturn_t tis_int_probe(int irq, void *dev_id) if (interrupt == 0) return IRQ_NONE; - chip->vendor.irq = irq; + chip->vendor.probed_irq = irq; /* Clear interrupts handled with TPM_EOI */ iowrite32(interrupt, @@ -464,7 +547,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; + int rc, i, irq_s, irq_e; struct tpm_chip *chip; if (!(chip = tpm_register_hardware(dev, &tpm_tis))) @@ -493,6 +576,14 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, "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) { + rc = -ENODEV; + goto out_err; + } + } + if (itpm) dev_info(dev, "Intel iTPM workaround enabled\n"); @@ -522,6 +613,9 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, if (intfcaps & TPM_INTF_DATA_AVAIL_INT) dev_dbg(dev, "\tData Avail Int Support\n"); + /* get the timeouts before testing for irqs */ + tpm_get_timeouts(chip); + /* INTERRUPT Setup */ init_waitqueue_head(&chip->vendor.read_queue); init_waitqueue_head(&chip->vendor.int_queue); @@ -540,13 +634,19 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, if (interrupts) chip->vendor.irq = irq; if (interrupts && !chip->vendor.irq) { - chip->vendor.irq = + irq_s = ioread8(chip->vendor.iobase + TPM_INT_VECTOR(chip->vendor.locality)); + if (irq_s) { + irq_e = irq_s; + } else { + irq_s = 3; + irq_e = 15; + } - for (i = 3; i < 16 && chip->vendor.irq == 0; i++) { + for (i = irq_s; i <= irq_e && chip->vendor.irq == 0; i++) { iowrite8(i, chip->vendor.iobase + - TPM_INT_VECTOR(chip->vendor.locality)); + TPM_INT_VECTOR(chip->vendor.locality)); if (request_irq (i, tis_int_probe, IRQF_SHARED, chip->vendor.miscdev.name, chip) != 0) { @@ -568,9 +668,22 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); + chip->vendor.probed_irq = 0; + /* Generate Interrupts */ tpm_gen_interrupt(chip); + chip->vendor.irq = chip->vendor.probed_irq; + + /* free_irq will call into tis_int_probe; + clear all irqs we haven't seen while doing + tpm_gen_interrupt */ + iowrite32(ioread32 + (chip->vendor.iobase + + TPM_INT_STATUS(chip->vendor.locality)), + chip->vendor.iobase + + TPM_INT_STATUS(chip->vendor.locality)); + /* Turn off */ iowrite32(intmask, chip->vendor.iobase + @@ -609,7 +722,6 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, list_add(&chip->vendor.list, &tis_chips); spin_unlock(&tis_lock); - tpm_get_timeouts(chip); tpm_continue_selftest(chip); return 0; @@ -619,6 +731,29 @@ out_err: tpm_remove_hardware(chip->dev); return rc; } + +static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) +{ + u32 intmask; + + /* reenable interrupts that device may have lost or + BIOS/firmware may have disabled */ + iowrite8(chip->vendor.irq, chip->vendor.iobase + + TPM_INT_VECTOR(chip->vendor.locality)); + + intmask = + ioread32(chip->vendor.iobase + + TPM_INT_ENABLE(chip->vendor.locality)); + + intmask |= TPM_INTF_CMD_READY_INT + | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT + | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE; + + iowrite32(intmask, + chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); +} + + #ifdef CONFIG_PNP static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, const struct pnp_device_id *pnp_id) @@ -650,6 +785,9 @@ 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_continue_selftest(chip); @@ -702,6 +840,11 @@ static int tpm_tis_suspend(struct platform_device *dev, pm_message_t 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 = { diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index 39ccdeada791..e90e1c74fd4c 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -621,7 +621,7 @@ static int __devinit hwicap_setup(struct device *dev, int id, drvdata->mem_start = regs_res->start; drvdata->mem_end = regs_res->end; - drvdata->mem_size = regs_res->end - regs_res->start + 1; + drvdata->mem_size = resource_size(regs_res); if (!request_mem_region(drvdata->mem_start, drvdata->mem_size, DRIVER_NAME)) { |