diff options
author | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-04-11 10:41:07 +0300 |
---|---|---|
committer | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-04-13 08:17:18 +0300 |
commit | e09b41010ba33a20a87472ee821fa407a5b8da36 (patch) | |
tree | d10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/drivers/char | |
parent | f93b97fd65072de626c074dbe099a1fff05ce060 (diff) |
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page.
During the rebasing, the following patch collided:
Force tick interrupt and get rid of softirq magic(I70131fb85).
Collisions have been removed because its logic was found on the
source already.
Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769
Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/drivers/char')
58 files changed, 1816 insertions, 2091 deletions
diff --git a/kernel/drivers/char/Kconfig b/kernel/drivers/char/Kconfig index a4af82217..a043107da 100644 --- a/kernel/drivers/char/Kconfig +++ b/kernel/drivers/char/Kconfig @@ -590,14 +590,6 @@ config DEVPORT source "drivers/s390/char/Kconfig" -config MSM_SMD_PKT - bool "Enable device interface for some SMD packet ports" - default n - depends on MSM_SMD - help - 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 diff --git a/kernel/drivers/char/Makefile b/kernel/drivers/char/Makefile index d06cde260..d8a757930 100644 --- a/kernel/drivers/char/Makefile +++ b/kernel/drivers/char/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o obj-$(CONFIG_RAW_DRIVER) += raw.o obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o -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 @@ -36,7 +35,6 @@ else obj-$(CONFIG_NVRAM) += nvram.o endif obj-$(CONFIG_TOSHIBA) += toshiba.o -obj-$(CONFIG_I8K) += i8k.o obj-$(CONFIG_DS1620) += ds1620.o obj-$(CONFIG_HW_RANDOM) += hw_random/ obj-$(CONFIG_PPDEV) += ppdev.o diff --git a/kernel/drivers/char/agp/intel-gtt.c b/kernel/drivers/char/agp/intel-gtt.c index c6dea3f69..1341a94cc 100644 --- a/kernel/drivers/char/agp/intel-gtt.c +++ b/kernel/drivers/char/agp/intel-gtt.c @@ -1408,8 +1408,8 @@ int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, } EXPORT_SYMBOL(intel_gmch_probe); -void intel_gtt_get(size_t *gtt_total, size_t *stolen_size, - phys_addr_t *mappable_base, unsigned long *mappable_end) +void intel_gtt_get(u64 *gtt_total, size_t *stolen_size, + phys_addr_t *mappable_base, u64 *mappable_end) { *gtt_total = intel_private.gtt_total_entries << PAGE_SHIFT; *stolen_size = intel_private.stolen_size; diff --git a/kernel/drivers/char/agp/uninorth-agp.c b/kernel/drivers/char/agp/uninorth-agp.c index a56ee9bed..057554412 100644 --- a/kernel/drivers/char/agp/uninorth-agp.c +++ b/kernel/drivers/char/agp/uninorth-agp.c @@ -361,6 +361,10 @@ static int agp_uninorth_resume(struct pci_dev *pdev) } #endif /* CONFIG_PM */ +static struct { + struct page **pages_arr; +} uninorth_priv; + static int uninorth_create_gatt_table(struct agp_bridge_data *bridge) { char *table; @@ -371,7 +375,6 @@ static int uninorth_create_gatt_table(struct agp_bridge_data *bridge) int i; void *temp; struct page *page; - struct page **pages; /* We can't handle 2 level gatt's */ if (bridge->driver->size_type == LVL2_APER_SIZE) @@ -400,8 +403,8 @@ static int uninorth_create_gatt_table(struct agp_bridge_data *bridge) if (table == NULL) return -ENOMEM; - pages = kmalloc((1 << page_order) * sizeof(struct page*), GFP_KERNEL); - if (pages == NULL) + uninorth_priv.pages_arr = kmalloc((1 << page_order) * sizeof(struct page*), GFP_KERNEL); + if (uninorth_priv.pages_arr == NULL) goto enomem; table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); @@ -409,14 +412,14 @@ static int uninorth_create_gatt_table(struct agp_bridge_data *bridge) for (page = virt_to_page(table), i = 0; page <= virt_to_page(table_end); page++, i++) { SetPageReserved(page); - pages[i] = page; + uninorth_priv.pages_arr[i] = page; } bridge->gatt_table_real = (u32 *) table; /* Need to clear out any dirty data still sitting in caches */ flush_dcache_range((unsigned long)table, (unsigned long)table_end + 1); - bridge->gatt_table = vmap(pages, (1 << page_order), 0, PAGE_KERNEL_NCG); + bridge->gatt_table = vmap(uninorth_priv.pages_arr, (1 << page_order), 0, PAGE_KERNEL_NCG); if (bridge->gatt_table == NULL) goto enomem; @@ -434,7 +437,7 @@ static int uninorth_create_gatt_table(struct agp_bridge_data *bridge) return 0; enomem: - kfree(pages); + kfree(uninorth_priv.pages_arr); if (table) free_pages((unsigned long)table, page_order); return -ENOMEM; @@ -456,6 +459,7 @@ static int uninorth_free_gatt_table(struct agp_bridge_data *bridge) */ vunmap(bridge->gatt_table); + kfree(uninorth_priv.pages_arr); table = (char *) bridge->gatt_table_real; table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); diff --git a/kernel/drivers/char/efirtc.c b/kernel/drivers/char/efirtc.c index e39e7402e..dc62568b7 100644 --- a/kernel/drivers/char/efirtc.c +++ b/kernel/drivers/char/efirtc.c @@ -30,7 +30,6 @@ #include <linux/types.h> #include <linux/errno.h> #include <linux/miscdevice.h> -#include <linux/module.h> #include <linux/init.h> #include <linux/rtc.h> #include <linux/proc_fs.h> @@ -395,14 +394,8 @@ efi_rtc_init(void) } return 0; } +device_initcall(efi_rtc_init); -static void __exit -efi_rtc_exit(void) -{ - /* not yet used */ -} - -module_init(efi_rtc_init); -module_exit(efi_rtc_exit); - +/* MODULE_LICENSE("GPL"); +*/ diff --git a/kernel/drivers/char/hpet.c b/kernel/drivers/char/hpet.c index 5c0baa9ff..240b6cf1d 100644 --- a/kernel/drivers/char/hpet.c +++ b/kernel/drivers/char/hpet.c @@ -12,7 +12,6 @@ */ #include <linux/interrupt.h> -#include <linux/module.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/miscdevice.h> @@ -1043,24 +1042,16 @@ static int hpet_acpi_add(struct acpi_device *device) return hpet_alloc(&data); } -static int hpet_acpi_remove(struct acpi_device *device) -{ - /* XXX need to unregister clocksource, dealloc mem, etc */ - return -EINVAL; -} - static const struct acpi_device_id hpet_device_ids[] = { {"PNP0103", 0}, {"", 0}, }; -MODULE_DEVICE_TABLE(acpi, hpet_device_ids); static struct acpi_driver hpet_acpi_driver = { .name = "hpet", .ids = hpet_device_ids, .ops = { .add = hpet_acpi_add, - .remove = hpet_acpi_remove, }, }; @@ -1086,19 +1077,9 @@ static int __init hpet_init(void) return 0; } +device_initcall(hpet_init); -static void __exit hpet_exit(void) -{ - acpi_bus_unregister_driver(&hpet_acpi_driver); - - if (sysctl_header) - unregister_sysctl_table(sysctl_header); - misc_deregister(&hpet_misc); - - return; -} - -module_init(hpet_init); -module_exit(hpet_exit); +/* MODULE_AUTHOR("Bob Picco <Robert.Picco@hp.com>"); MODULE_LICENSE("GPL"); +*/ diff --git a/kernel/drivers/char/hw_random/Kconfig b/kernel/drivers/char/hw_random/Kconfig index f48cf11c6..dbf227194 100644 --- a/kernel/drivers/char/hw_random/Kconfig +++ b/kernel/drivers/char/hw_random/Kconfig @@ -10,7 +10,7 @@ menuconfig HW_RANDOM To compile this driver as a module, choose M here: the module will be called rng-core. This provides a device - that's usually called /dev/hw_random, and which exposes one + that's usually called /dev/hwrng, and which exposes one of possibly several hardware random number generators. These hardware random number generators do not feed directly @@ -346,6 +346,16 @@ config HW_RANDOM_MSM If unsure, say Y. +config HW_RANDOM_ST + tristate "ST Microelectronics HW Random Number Generator support" + depends on HW_RANDOM && ARCH_STI + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on STi series of SoCs. + + To compile this driver as a module, choose M here: the + module will be called st-rng. + config HW_RANDOM_XGENE tristate "APM X-Gene True Random Number Generator (TRNG) support" depends on HW_RANDOM && ARCH_XGENE @@ -359,6 +369,18 @@ config HW_RANDOM_XGENE If unsure, say Y. +config HW_RANDOM_STM32 + tristate "STMicroelectronics STM32 random number generator" + depends on HW_RANDOM && (ARCH_STM32 || COMPILE_TEST) + help + This driver provides kernel-side support for the Random Number + Generator hardware found on STM32 microcontrollers. + + To compile this driver as a module, choose M here: the + module will be called stm32-rng. + + If unsure, say N. + endif # HW_RANDOM config UML_RANDOM diff --git a/kernel/drivers/char/hw_random/Makefile b/kernel/drivers/char/hw_random/Makefile index 055bb0151..5ad397635 100644 --- a/kernel/drivers/char/hw_random/Makefile +++ b/kernel/drivers/char/hw_random/Makefile @@ -30,4 +30,6 @@ obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o +obj-$(CONFIG_HW_RANDOM_ST) += st-rng.o obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o +obj-$(CONFIG_HW_RANDOM_STM32) += stm32-rng.o diff --git a/kernel/drivers/char/hw_random/core.c b/kernel/drivers/char/hw_random/core.c index 5643b65ce..6f497aa1b 100644 --- a/kernel/drivers/char/hw_random/core.c +++ b/kernel/drivers/char/hw_random/core.c @@ -323,7 +323,7 @@ static ssize_t hwrng_attr_current_store(struct device *dev, return -ERESTARTSYS; err = -ENODEV; list_for_each_entry(rng, &rng_list, list) { - if (strcmp(rng->name, buf) == 0) { + if (sysfs_streq(rng->name, buf)) { err = 0; if (rng != current_rng) err = set_current_rng(rng); diff --git a/kernel/drivers/char/hw_random/exynos-rng.c b/kernel/drivers/char/hw_random/exynos-rng.c index dc4701fd8..30cf46231 100644 --- a/kernel/drivers/char/hw_random/exynos-rng.c +++ b/kernel/drivers/char/hw_random/exynos-rng.c @@ -53,15 +53,11 @@ 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) +static int exynos_rng_configure(struct exynos_rng *exynos_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); @@ -70,6 +66,17 @@ static int exynos_init(struct hwrng *rng) & SEED_SETTING_DONE)) ret = -EIO; + return ret; +} + +static int exynos_init(struct hwrng *rng) +{ + struct exynos_rng *exynos_rng = container_of(rng, + struct exynos_rng, rng); + int ret = 0; + + pm_runtime_get_sync(exynos_rng->dev); + ret = exynos_rng_configure(exynos_rng); pm_runtime_put_noidle(exynos_rng->dev); return ret; @@ -81,21 +88,24 @@ static int exynos_read(struct hwrng *rng, void *buf, struct exynos_rng *exynos_rng = container_of(rng, struct exynos_rng, rng); u32 *data = buf; + int retry = 100; 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)) + EXYNOS_PRNG_STATUS_OFFSET) & PRNG_DONE) && --retry) cpu_relax(); + if (!retry) + return -ETIMEDOUT; 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); + pm_runtime_put_sync_autosuspend(exynos_rng->dev); return 4; } @@ -152,15 +162,45 @@ static int exynos_rng_runtime_resume(struct device *dev) return clk_prepare_enable(exynos_rng->clk); } + +static int exynos_rng_suspend(struct device *dev) +{ + return pm_runtime_force_suspend(dev); +} + +static int exynos_rng_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct exynos_rng *exynos_rng = platform_get_drvdata(pdev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + return exynos_rng_configure(exynos_rng); +} #endif -static UNIVERSAL_DEV_PM_OPS(exynos_rng_pm_ops, exynos_rng_runtime_suspend, - exynos_rng_runtime_resume, NULL); +static const struct dev_pm_ops exynos_rng_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(exynos_rng_suspend, exynos_rng_resume) + SET_RUNTIME_PM_OPS(exynos_rng_runtime_suspend, + exynos_rng_runtime_resume, NULL) +}; + +static const struct of_device_id exynos_rng_dt_match[] = { + { + .compatible = "samsung,exynos4-rng", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, exynos_rng_dt_match); static struct platform_driver exynos_rng_driver = { .driver = { .name = "exynos-rng", .pm = &exynos_rng_pm_ops, + .of_match_table = exynos_rng_dt_match, }, .probe = exynos_rng_probe, }; diff --git a/kernel/drivers/char/hw_random/mxc-rnga.c b/kernel/drivers/char/hw_random/mxc-rnga.c index 6cbb72ec6..467362262 100644 --- a/kernel/drivers/char/hw_random/mxc-rnga.c +++ b/kernel/drivers/char/hw_random/mxc-rnga.c @@ -141,12 +141,11 @@ static void mxc_rnga_cleanup(struct hwrng *rng) static int __init mxc_rnga_probe(struct platform_device *pdev) { - int err = -ENODEV; + int err; struct resource *res; struct mxc_rng *mxc_rng; - mxc_rng = devm_kzalloc(&pdev->dev, sizeof(struct mxc_rng), - GFP_KERNEL); + mxc_rng = devm_kzalloc(&pdev->dev, sizeof(*mxc_rng), GFP_KERNEL); if (!mxc_rng) return -ENOMEM; @@ -160,13 +159,12 @@ static int __init mxc_rnga_probe(struct platform_device *pdev) 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(mxc_rng->clk); - goto out; + return PTR_ERR(mxc_rng->clk); } err = clk_prepare_enable(mxc_rng->clk); if (err) - goto out; + return err; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mxc_rng->mem = devm_ioremap_resource(&pdev->dev, res); @@ -181,14 +179,10 @@ static int __init mxc_rnga_probe(struct platform_device *pdev) goto err_ioremap; } - dev_info(&pdev->dev, "MXC RNGA Registered.\n"); - return 0; err_ioremap: clk_disable_unprepare(mxc_rng->clk); - -out: return err; } diff --git a/kernel/drivers/char/hw_random/octeon-rng.c b/kernel/drivers/char/hw_random/octeon-rng.c index 6234a4a19..8c78aa090 100644 --- a/kernel/drivers/char/hw_random/octeon-rng.c +++ b/kernel/drivers/char/hw_random/octeon-rng.c @@ -96,7 +96,7 @@ static int octeon_rng_probe(struct platform_device *pdev) rng->ops = ops; platform_set_drvdata(pdev, &rng->ops); - ret = hwrng_register(&rng->ops); + ret = devm_hwrng_register(&pdev->dev, &rng->ops); if (ret) return -ENOENT; @@ -105,21 +105,11 @@ static int octeon_rng_probe(struct platform_device *pdev) return 0; } -static int octeon_rng_remove(struct platform_device *pdev) -{ - struct hwrng *rng = platform_get_drvdata(pdev); - - hwrng_unregister(rng); - - return 0; -} - static struct platform_driver octeon_rng_driver = { .driver = { .name = "octeon_rng", }, .probe = octeon_rng_probe, - .remove = octeon_rng_remove, }; module_platform_driver(octeon_rng_driver); diff --git a/kernel/drivers/char/hw_random/pasemi-rng.c b/kernel/drivers/char/hw_random/pasemi-rng.c index 51cb1d5cc..699b7259f 100644 --- a/kernel/drivers/char/hw_random/pasemi-rng.c +++ b/kernel/drivers/char/hw_random/pasemi-rng.c @@ -138,6 +138,7 @@ static const struct of_device_id rng_match[] = { { .compatible = "pasemi,pwrficient-rng", }, { }, }; +MODULE_DEVICE_TABLE(of, rng_match); static struct platform_driver rng_driver = { .driver = { diff --git a/kernel/drivers/char/hw_random/ppc4xx-rng.c b/kernel/drivers/char/hw_random/ppc4xx-rng.c index b2cfda0fa..c0db4387d 100644 --- a/kernel/drivers/char/hw_random/ppc4xx-rng.c +++ b/kernel/drivers/char/hw_random/ppc4xx-rng.c @@ -129,6 +129,7 @@ static const struct of_device_id ppc4xx_rng_match[] = { { .compatible = "amcc,ppc440epx-rng", }, {}, }; +MODULE_DEVICE_TABLE(of, ppc4xx_rng_match); static struct platform_driver ppc4xx_rng_driver = { .driver = { diff --git a/kernel/drivers/char/hw_random/st-rng.c b/kernel/drivers/char/hw_random/st-rng.c new file mode 100644 index 000000000..1d35363d2 --- /dev/null +++ b/kernel/drivers/char/hw_random/st-rng.c @@ -0,0 +1,151 @@ +/* + * ST Random Number Generator Driver ST's Platforms + * + * Author: Pankaj Dev: <pankaj.dev@st.com> + * Lee Jones <lee.jones@linaro.org> + * + * Copyright (C) 2015 STMicroelectronics (R&D) Limited + * + * 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/clk.h> +#include <linux/delay.h> +#include <linux/hw_random.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +/* Registers */ +#define ST_RNG_STATUS_REG 0x20 +#define ST_RNG_DATA_REG 0x24 + +/* Registers fields */ +#define ST_RNG_STATUS_BAD_SEQUENCE BIT(0) +#define ST_RNG_STATUS_BAD_ALTERNANCE BIT(1) +#define ST_RNG_STATUS_FIFO_FULL BIT(5) + +#define ST_RNG_SAMPLE_SIZE 2 /* 2 Byte (16bit) samples */ +#define ST_RNG_FIFO_DEPTH 4 +#define ST_RNG_FIFO_SIZE (ST_RNG_FIFO_DEPTH * ST_RNG_SAMPLE_SIZE) + +/* + * Samples are documented to be available every 0.667us, so in theory + * the 4 sample deep FIFO should take 2.668us to fill. However, during + * thorough testing, it became apparent that filling the FIFO actually + * takes closer to 12us. We then multiply by 2 in order to account for + * the lack of udelay()'s reliability, suggested by Russell King. + */ +#define ST_RNG_FILL_FIFO_TIMEOUT (12 * 2) + +struct st_rng_data { + void __iomem *base; + struct clk *clk; + struct hwrng ops; +}; + +static int st_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ + struct st_rng_data *ddata = (struct st_rng_data *)rng->priv; + u32 status; + int i; + + if (max < sizeof(u16)) + return -EINVAL; + + /* Wait until FIFO is full - max 4uS*/ + for (i = 0; i < ST_RNG_FILL_FIFO_TIMEOUT; i++) { + status = readl_relaxed(ddata->base + ST_RNG_STATUS_REG); + if (status & ST_RNG_STATUS_FIFO_FULL) + break; + udelay(1); + } + + if (i == ST_RNG_FILL_FIFO_TIMEOUT) + return 0; + + for (i = 0; i < ST_RNG_FIFO_SIZE && i < max; i += 2) + *(u16 *)(data + i) = + readl_relaxed(ddata->base + ST_RNG_DATA_REG); + + return i; /* No of bytes read */ +} + +static int st_rng_probe(struct platform_device *pdev) +{ + struct st_rng_data *ddata; + struct resource *res; + struct clk *clk; + void __iomem *base; + int ret; + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ret = clk_prepare_enable(clk); + if (ret) + return ret; + + ddata->ops.priv = (unsigned long)ddata; + ddata->ops.read = st_rng_read; + ddata->ops.name = pdev->name; + ddata->base = base; + ddata->clk = clk; + + dev_set_drvdata(&pdev->dev, ddata); + + ret = hwrng_register(&ddata->ops); + if (ret) { + dev_err(&pdev->dev, "Failed to register HW RNG\n"); + return ret; + } + + dev_info(&pdev->dev, "Successfully registered HW RNG\n"); + + return 0; +} + +static int st_rng_remove(struct platform_device *pdev) +{ + struct st_rng_data *ddata = dev_get_drvdata(&pdev->dev); + + hwrng_unregister(&ddata->ops); + + clk_disable_unprepare(ddata->clk); + + return 0; +} + +static const struct of_device_id st_rng_match[] = { + { .compatible = "st,rng" }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_rng_match); + +static struct platform_driver st_rng_driver = { + .driver = { + .name = "st-hwrandom", + .of_match_table = of_match_ptr(st_rng_match), + }, + .probe = st_rng_probe, + .remove = st_rng_remove +}; + +module_platform_driver(st_rng_driver); + +MODULE_AUTHOR("Pankaj Dev <pankaj.dev@st.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/kernel/drivers/char/hw_random/stm32-rng.c b/kernel/drivers/char/hw_random/stm32-rng.c new file mode 100644 index 000000000..92a810648 --- /dev/null +++ b/kernel/drivers/char/hw_random/stm32-rng.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2015, Daniel Thompson + * + * This file 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 file 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/delay.h> +#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/pm_runtime.h> +#include <linux/slab.h> + +#define RNG_CR 0x00 +#define RNG_CR_RNGEN BIT(2) + +#define RNG_SR 0x04 +#define RNG_SR_SEIS BIT(6) +#define RNG_SR_CEIS BIT(5) +#define RNG_SR_DRDY BIT(0) + +#define RNG_DR 0x08 + +/* + * It takes 40 cycles @ 48MHz to generate each random number (e.g. <1us). + * At the time of writing STM32 parts max out at ~200MHz meaning a timeout + * of 500 leaves us a very comfortable margin for error. The loop to which + * the timeout applies takes at least 4 instructions per iteration so the + * timeout is enough to take us up to multi-GHz parts! + */ +#define RNG_TIMEOUT 500 + +struct stm32_rng_private { + struct hwrng rng; + void __iomem *base; + struct clk *clk; +}; + +static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ + struct stm32_rng_private *priv = + container_of(rng, struct stm32_rng_private, rng); + u32 sr; + int retval = 0; + + pm_runtime_get_sync((struct device *) priv->rng.priv); + + while (max > sizeof(u32)) { + sr = readl_relaxed(priv->base + RNG_SR); + if (!sr && wait) { + unsigned int timeout = RNG_TIMEOUT; + + do { + cpu_relax(); + sr = readl_relaxed(priv->base + RNG_SR); + } while (!sr && --timeout); + } + + /* If error detected or data not ready... */ + if (sr != RNG_SR_DRDY) + break; + + *(u32 *)data = readl_relaxed(priv->base + RNG_DR); + + retval += sizeof(u32); + data += sizeof(u32); + max -= sizeof(u32); + } + + if (WARN_ONCE(sr & (RNG_SR_SEIS | RNG_SR_CEIS), + "bad RNG status - %x\n", sr)) + writel_relaxed(0, priv->base + RNG_SR); + + pm_runtime_mark_last_busy((struct device *) priv->rng.priv); + pm_runtime_put_sync_autosuspend((struct device *) priv->rng.priv); + + return retval || !wait ? retval : -EIO; +} + +static int stm32_rng_init(struct hwrng *rng) +{ + struct stm32_rng_private *priv = + container_of(rng, struct stm32_rng_private, rng); + int err; + + err = clk_prepare_enable(priv->clk); + if (err) + return err; + + writel_relaxed(RNG_CR_RNGEN, priv->base + RNG_CR); + + /* clear error indicators */ + writel_relaxed(0, priv->base + RNG_SR); + + return 0; +} + +static void stm32_rng_cleanup(struct hwrng *rng) +{ + struct stm32_rng_private *priv = + container_of(rng, struct stm32_rng_private, rng); + + writel_relaxed(0, priv->base + RNG_CR); + clk_disable_unprepare(priv->clk); +} + +static int stm32_rng_probe(struct platform_device *ofdev) +{ + struct device *dev = &ofdev->dev; + struct device_node *np = ofdev->dev.of_node; + struct stm32_rng_private *priv; + struct resource res; + int err; + + priv = devm_kzalloc(dev, sizeof(struct stm32_rng_private), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + err = of_address_to_resource(np, 0, &res); + if (err) + return err; + + priv->base = devm_ioremap_resource(dev, &res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->clk = devm_clk_get(&ofdev->dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + dev_set_drvdata(dev, priv); + + priv->rng.name = dev_driver_string(dev), +#ifndef CONFIG_PM + priv->rng.init = stm32_rng_init, + priv->rng.cleanup = stm32_rng_cleanup, +#endif + priv->rng.read = stm32_rng_read, + priv->rng.priv = (unsigned long) dev; + + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + return devm_hwrng_register(dev, &priv->rng); +} + +#ifdef CONFIG_PM +static int stm32_rng_runtime_suspend(struct device *dev) +{ + struct stm32_rng_private *priv = dev_get_drvdata(dev); + + stm32_rng_cleanup(&priv->rng); + + return 0; +} + +static int stm32_rng_runtime_resume(struct device *dev) +{ + struct stm32_rng_private *priv = dev_get_drvdata(dev); + + return stm32_rng_init(&priv->rng); +} +#endif + +static UNIVERSAL_DEV_PM_OPS(stm32_rng_pm_ops, stm32_rng_runtime_suspend, + stm32_rng_runtime_resume, NULL); + +static const struct of_device_id stm32_rng_match[] = { + { + .compatible = "st,stm32-rng", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, stm32_rng_match); + +static struct platform_driver stm32_rng_driver = { + .driver = { + .name = "stm32-rng", + .pm = &stm32_rng_pm_ops, + .of_match_table = stm32_rng_match, + }, + .probe = stm32_rng_probe, +}; + +module_platform_driver(stm32_rng_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Daniel Thompson <daniel.thompson@linaro.org>"); +MODULE_DESCRIPTION("STMicroelectronics STM32 RNG device driver"); diff --git a/kernel/drivers/char/hw_random/via-rng.c b/kernel/drivers/char/hw_random/via-rng.c index a3bebef25..0c98a9d51 100644 --- a/kernel/drivers/char/hw_random/via-rng.c +++ b/kernel/drivers/char/hw_random/via-rng.c @@ -33,7 +33,7 @@ #include <asm/io.h> #include <asm/msr.h> #include <asm/cpufeature.h> -#include <asm/i387.h> +#include <asm/fpu/api.h> diff --git a/kernel/drivers/char/hw_random/xgene-rng.c b/kernel/drivers/char/hw_random/xgene-rng.c index c37cf754a..3c7764540 100644 --- a/kernel/drivers/char/hw_random/xgene-rng.c +++ b/kernel/drivers/char/hw_random/xgene-rng.c @@ -344,11 +344,12 @@ static int xgene_rng_probe(struct platform_device *pdev) if (IS_ERR(ctx->csr_base)) return PTR_ERR(ctx->csr_base); - ctx->irq = platform_get_irq(pdev, 0); - if (ctx->irq < 0) { + rc = platform_get_irq(pdev, 0); + if (rc < 0) { dev_err(&pdev->dev, "No IRQ resource\n"); - return ctx->irq; + return rc; } + ctx->irq = rc; dev_dbg(&pdev->dev, "APM X-Gene RNG BASE %p ALARM IRQ %d", ctx->csr_base, ctx->irq); diff --git a/kernel/drivers/char/i8k.c b/kernel/drivers/char/i8k.c deleted file mode 100644 index 3c1a123f9..000000000 --- a/kernel/drivers/char/i8k.c +++ /dev/null @@ -1,1023 +0,0 @@ -/* - * i8k.c -- Linux driver for accessing the SMM BIOS on Dell laptops. - * - * Copyright (C) 2001 Massimo Dal Zotto <dz@debian.org> - * - * Hwmon integration: - * Copyright (C) 2011 Jean Delvare <jdelvare@suse.de> - * Copyright (C) 2013, 2014 Guenter Roeck <linux@roeck-us.net> - * Copyright (C) 2014 Pali Rohár <pali.rohar@gmail.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, 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. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/delay.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/init.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/dmi.h> -#include <linux/capability.h> -#include <linux/mutex.h> -#include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> -#include <linux/uaccess.h> -#include <linux/io.h> -#include <linux/sched.h> - -#include <linux/i8k.h> - -#define I8K_SMM_FN_STATUS 0x0025 -#define I8K_SMM_POWER_STATUS 0x0069 -#define I8K_SMM_SET_FAN 0x01a3 -#define I8K_SMM_GET_FAN 0x00a3 -#define I8K_SMM_GET_SPEED 0x02a3 -#define I8K_SMM_GET_FAN_TYPE 0x03a3 -#define I8K_SMM_GET_NOM_SPEED 0x04a3 -#define I8K_SMM_GET_TEMP 0x10a3 -#define I8K_SMM_GET_TEMP_TYPE 0x11a3 -#define I8K_SMM_GET_DELL_SIG1 0xfea3 -#define I8K_SMM_GET_DELL_SIG2 0xffa3 - -#define I8K_FAN_MULT 30 -#define I8K_FAN_MAX_RPM 30000 -#define I8K_MAX_TEMP 127 - -#define I8K_FN_NONE 0x00 -#define I8K_FN_UP 0x01 -#define I8K_FN_DOWN 0x02 -#define I8K_FN_MUTE 0x04 -#define I8K_FN_MASK 0x07 -#define I8K_FN_SHIFT 8 - -#define I8K_POWER_AC 0x05 -#define I8K_POWER_BATTERY 0x01 - -static DEFINE_MUTEX(i8k_mutex); -static char bios_version[4]; -static struct device *i8k_hwmon_dev; -static u32 i8k_hwmon_flags; -static uint i8k_fan_mult = I8K_FAN_MULT; -static uint i8k_pwm_mult; -static uint i8k_fan_max = I8K_FAN_HIGH; - -#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"); -MODULE_LICENSE("GPL"); - -static bool force; -module_param(force, bool, 0); -MODULE_PARM_DESC(force, "Force loading without checking for supported models"); - -static bool ignore_dmi; -module_param(ignore_dmi, bool, 0); -MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match"); - -static bool restricted; -module_param(restricted, bool, 0); -MODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set"); - -static bool power_status; -module_param(power_status, bool, 0600); -MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k"); - -static uint fan_mult; -module_param(fan_mult, uint, 0); -MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with (default: autodetect)"); - -static uint fan_max; -module_param(fan_max, uint, 0); -MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)"); - -static int i8k_open_fs(struct inode *inode, struct file *file); -static long i8k_ioctl(struct file *, unsigned int, unsigned long); - -static const struct file_operations i8k_fops = { - .owner = THIS_MODULE, - .open = i8k_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .unlocked_ioctl = i8k_ioctl, -}; - -struct smm_regs { - unsigned int eax; - 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) -{ - const char *dmi_data = dmi_get_system_info(field); - - return dmi_data && *dmi_data ? dmi_data : "?"; -} - -/* - * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard. - */ -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" - "movl 0(%%rax),%%edx\n\t" - "pushq %%rdx\n\t" - "movl 4(%%rax),%%ebx\n\t" - "movl 8(%%rax),%%ecx\n\t" - "movl 12(%%rax),%%edx\n\t" - "movl 16(%%rax),%%esi\n\t" - "movl 20(%%rax),%%edi\n\t" - "popq %%rax\n\t" - "out %%al,$0xb2\n\t" - "out %%al,$0x84\n\t" - "xchgq %%rax,(%%rsp)\n\t" - "movl %%ebx,4(%%rax)\n\t" - "movl %%ecx,8(%%rax)\n\t" - "movl %%edx,12(%%rax)\n\t" - "movl %%esi,16(%%rax)\n\t" - "movl %%edi,20(%%rax)\n\t" - "popq %%rdx\n\t" - "movl %%edx,0(%%rax)\n\t" - "pushfq\n\t" - "popq %%rax\n\t" - "andl $1,%%eax\n" - : "=a"(rc) - : "a"(regs) - : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); -#else - asm volatile("pushl %%eax\n\t" - "movl 0(%%eax),%%edx\n\t" - "push %%edx\n\t" - "movl 4(%%eax),%%ebx\n\t" - "movl 8(%%eax),%%ecx\n\t" - "movl 12(%%eax),%%edx\n\t" - "movl 16(%%eax),%%esi\n\t" - "movl 20(%%eax),%%edi\n\t" - "popl %%eax\n\t" - "out %%al,$0xb2\n\t" - "out %%al,$0x84\n\t" - "xchgl %%eax,(%%esp)\n\t" - "movl %%ebx,4(%%eax)\n\t" - "movl %%ecx,8(%%eax)\n\t" - "movl %%edx,12(%%eax)\n\t" - "movl %%esi,16(%%eax)\n\t" - "movl %%edi,20(%%eax)\n\t" - "popl %%edx\n\t" - "movl %%edx,0(%%eax)\n\t" - "lahf\n\t" - "shrl $8,%%eax\n\t" - "andl $1,%%eax\n" - : "=a"(rc) - : "a"(regs) - : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); -#endif - if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax) - rc = -EINVAL; - -out: - set_cpus_allowed_ptr(current, old_mask); - free_cpumask_var(old_mask); - return rc; -} - -/* - * Read the Fn key status. - */ -static int i8k_get_fn_status(void) -{ - struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, }; - int rc; - - rc = i8k_smm(®s); - if (rc < 0) - return rc; - - switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) { - case I8K_FN_UP: - return I8K_VOL_UP; - case I8K_FN_DOWN: - return I8K_VOL_DOWN; - case I8K_FN_MUTE: - return I8K_VOL_MUTE; - default: - return 0; - } -} - -/* - * Read the power status. - */ -static int i8k_get_power_status(void) -{ - struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, }; - int rc; - - rc = i8k_smm(®s); - if (rc < 0) - return rc; - - return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY; -} - -/* - * Read the fan status. - */ -static int i8k_get_fan_status(int fan) -{ - struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, }; - - regs.ebx = fan & 0xff; - return i8k_smm(®s) ? : regs.eax & 0xff; -} - -/* - * Read the fan speed in RPM. - */ -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) * i8k_fan_mult; -} - -/* - * Read the fan type. - */ -static int i8k_get_fan_type(int fan) -{ - struct smm_regs regs = { .eax = I8K_SMM_GET_FAN_TYPE, }; - - regs.ebx = fan & 0xff; - return i8k_smm(®s) ? : regs.eax & 0xff; -} - -/* - * Read the fan nominal rpm for specific fan speed. - */ -static int i8k_get_fan_nominal_speed(int fan, int speed) -{ - struct smm_regs regs = { .eax = I8K_SMM_GET_NOM_SPEED, }; - - regs.ebx = (fan & 0xff) | (speed << 8); - return i8k_smm(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult; -} - -/* - * Set the fan speed (off, low, high). Returns the new fan status. - */ -static int i8k_set_fan(int fan, int speed) -{ - struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, }; - - speed = (speed < 0) ? 0 : ((speed > i8k_fan_max) ? i8k_fan_max : speed); - regs.ebx = (fan & 0xff) | (speed << 8); - - return i8k_smm(®s) ? : i8k_get_fan_status(fan); -} - -static int i8k_get_temp_type(int sensor) -{ - struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP_TYPE, }; - - regs.ebx = sensor & 0xff; - return i8k_smm(®s) ? : regs.eax & 0xff; -} - -/* - * Read the cpu temperature. - */ -static int _i8k_get_temp(int sensor) -{ - struct smm_regs regs = { - .eax = I8K_SMM_GET_TEMP, - .ebx = sensor & 0xff, - }; - - return i8k_smm(®s) ? : regs.eax & 0xff; -} - -static int i8k_get_temp(int sensor) -{ - int temp = _i8k_get_temp(sensor); - - /* - * Sometimes the temperature sensor returns 0x99, which is out of range. - * In this case we retry (once) before returning an error. - # 1003655137 00000058 00005a4b - # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees - # 1003655139 00000054 00005c52 - */ - if (temp == 0x99) { - msleep(100); - temp = _i8k_get_temp(sensor); - } - /* - * Return -ENODATA for all invalid temperatures. - * - * Known instances are the 0x99 value as seen above as well as - * 0xc1 (193), which may be returned when trying to read the GPU - * temperature if the system supports a GPU and it is currently - * turned off. - */ - if (temp > I8K_MAX_TEMP) - return -ENODATA; - - return temp; -} - -static int i8k_get_dell_signature(int req_fn) -{ - struct smm_regs regs = { .eax = req_fn, }; - int rc; - - rc = i8k_smm(®s); - if (rc < 0) - return rc; - - return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1; -} - -static int -i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) -{ - int val = 0; - int speed; - unsigned char buff[16]; - int __user *argp = (int __user *)arg; - - if (!argp) - return -EINVAL; - - switch (cmd) { - case I8K_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)); - break; - - case I8K_FN_STATUS: - val = i8k_get_fn_status(); - break; - - case I8K_POWER_STATUS: - val = i8k_get_power_status(); - break; - - case I8K_GET_TEMP: - val = i8k_get_temp(0); - break; - - case I8K_GET_SPEED: - if (copy_from_user(&val, argp, sizeof(int))) - return -EFAULT; - - val = i8k_get_fan_speed(val); - break; - - case I8K_GET_FAN: - if (copy_from_user(&val, argp, sizeof(int))) - return -EFAULT; - - val = i8k_get_fan_status(val); - break; - - case I8K_SET_FAN: - if (restricted && !capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (copy_from_user(&val, argp, sizeof(int))) - return -EFAULT; - - if (copy_from_user(&speed, argp + 1, sizeof(int))) - return -EFAULT; - - val = i8k_set_fan(val, speed); - break; - - default: - return -EINVAL; - } - - if (val < 0) - return val; - - switch (cmd) { - case I8K_BIOS_VERSION: - if (copy_to_user(argp, &val, 4)) - return -EFAULT; - - break; - case I8K_MACHINE_ID: - if (copy_to_user(argp, buff, 16)) - return -EFAULT; - - break; - default: - if (copy_to_user(argp, &val, sizeof(int))) - return -EFAULT; - - break; - } - - return 0; -} - -static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) -{ - long ret; - - mutex_lock(&i8k_mutex); - ret = i8k_ioctl_unlocked(fp, cmd, arg); - mutex_unlock(&i8k_mutex); - - return ret; -} - -/* - * Print the information for /proc/i8k. - */ -static int i8k_proc_show(struct seq_file *seq, void *offset) -{ - int fn_key, cpu_temp, ac_power; - int left_fan, right_fan, left_speed, right_speed; - - cpu_temp = i8k_get_temp(0); /* 11100 µs */ - left_fan = i8k_get_fan_status(I8K_FAN_LEFT); /* 580 µs */ - right_fan = i8k_get_fan_status(I8K_FAN_RIGHT); /* 580 µs */ - left_speed = i8k_get_fan_speed(I8K_FAN_LEFT); /* 580 µs */ - right_speed = i8k_get_fan_speed(I8K_FAN_RIGHT); /* 580 µs */ - fn_key = i8k_get_fn_status(); /* 750 µs */ - if (power_status) - ac_power = i8k_get_power_status(); /* 14700 µs */ - else - ac_power = -1; - - /* - * Info: - * - * 1) Format version (this will change if format changes) - * 2) BIOS version - * 3) BIOS machine ID - * 4) Cpu temperature - * 5) Left fan status - * 6) Right fan status - * 7) Left fan speed - * 8) Right fan speed - * 9) AC power - * 10) Fn Key status - */ - seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n", - I8K_PROC_FMT, - bios_version, - i8k_get_dmi_data(DMI_PRODUCT_SERIAL), - cpu_temp, - left_fan, right_fan, left_speed, right_speed, - ac_power, fn_key); - - return 0; -} - -static int i8k_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, i8k_proc_show, NULL); -} - - -/* - * Hwmon interface - */ - -static ssize_t i8k_hwmon_show_temp_label(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - static const char * const labels[] = { - "CPU", - "GPU", - "SODIMM", - "Other", - "Ambient", - "Other", - }; - int index = to_sensor_dev_attr(devattr)->index; - int type; - - type = i8k_get_temp_type(index); - if (type < 0) - return type; - if (type >= ARRAY_SIZE(labels)) - type = ARRAY_SIZE(labels) - 1; - return sprintf(buf, "%s\n", labels[type]); -} - -static ssize_t i8k_hwmon_show_temp(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - int temp; - - temp = i8k_get_temp(index); - if (temp < 0) - return temp; - return sprintf(buf, "%d\n", temp * 1000); -} - -static ssize_t i8k_hwmon_show_fan_label(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - static const char * const labels[] = { - "Processor Fan", - "Motherboard Fan", - "Video Fan", - "Power Supply Fan", - "Chipset Fan", - "Other Fan", - }; - int index = to_sensor_dev_attr(devattr)->index; - bool dock = false; - int type; - - type = i8k_get_fan_type(index); - if (type < 0) - return type; - - if (type & 0x10) { - dock = true; - type &= 0x0F; - } - - if (type >= ARRAY_SIZE(labels)) - type = (ARRAY_SIZE(labels) - 1); - - return sprintf(buf, "%s%s\n", (dock ? "Docking " : ""), labels[type]); -} - -static ssize_t i8k_hwmon_show_fan(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - int fan_speed; - - fan_speed = i8k_get_fan_speed(index); - if (fan_speed < 0) - return fan_speed; - 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 * i8k_pwm_mult, 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, i8k_pwm_mult), 0, i8k_fan_max); - - mutex_lock(&i8k_mutex); - err = i8k_set_fan(index, val); - mutex_unlock(&i8k_mutex); - - return err < 0 ? -EIO : count; -} - -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 0); -static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_temp_label, NULL, - 0); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 1); -static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, i8k_hwmon_show_temp_label, NULL, - 1); -static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 2); -static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, i8k_hwmon_show_temp_label, NULL, - 2); -static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 3); -static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, i8k_hwmon_show_temp_label, NULL, - 3); -static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL, 0); -static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_fan_label, NULL, - 0); -static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm, - i8k_hwmon_set_pwm, 0); -static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL, - 1); -static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_fan_label, NULL, - 1); -static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm, - i8k_hwmon_set_pwm, 1); - -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_temp2_label.dev_attr.attr, /* 3 */ - &sensor_dev_attr_temp3_input.dev_attr.attr, /* 4 */ - &sensor_dev_attr_temp3_label.dev_attr.attr, /* 5 */ - &sensor_dev_attr_temp4_input.dev_attr.attr, /* 6 */ - &sensor_dev_attr_temp4_label.dev_attr.attr, /* 7 */ - &sensor_dev_attr_fan1_input.dev_attr.attr, /* 8 */ - &sensor_dev_attr_fan1_label.dev_attr.attr, /* 9 */ - &sensor_dev_attr_pwm1.dev_attr.attr, /* 10 */ - &sensor_dev_attr_fan2_input.dev_attr.attr, /* 11 */ - &sensor_dev_attr_fan2_label.dev_attr.attr, /* 12 */ - &sensor_dev_attr_pwm2.dev_attr.attr, /* 13 */ - NULL -}; - -static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr, - int index) -{ - if (index >= 0 && index <= 1 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1)) - return 0; - if (index >= 2 && index <= 3 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2)) - return 0; - if (index >= 4 && index <= 5 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3)) - return 0; - if (index >= 6 && index <= 7 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4)) - return 0; - if (index >= 8 && index <= 10 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1)) - return 0; - if (index >= 11 && index <= 13 && - !(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_flags = 0; - - /* CPU temperature attributes, if temperature type is OK */ - err = i8k_get_temp_type(0); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP1; - /* check for additional temperature sensors */ - err = i8k_get_temp_type(1); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP2; - err = i8k_get_temp_type(2); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP3; - err = i8k_get_temp_type(3); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4; - - /* First fan attributes, if fan type is OK */ - err = i8k_get_fan_type(0); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN1; - - /* Second fan attributes, if fan type is OK */ - err = i8k_get_fan_type(1); - 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; -} - -struct i8k_config_data { - uint fan_mult; - uint fan_max; -}; - -enum i8k_configs { - DELL_LATITUDE_D520, - DELL_PRECISION_490, - DELL_STUDIO, - DELL_XPS, -}; - -static const struct i8k_config_data i8k_config_data[] = { - [DELL_LATITUDE_D520] = { - .fan_mult = 1, - .fan_max = I8K_FAN_TURBO, - }, - [DELL_PRECISION_490] = { - .fan_mult = 1, - .fan_max = I8K_FAN_TURBO, - }, - [DELL_STUDIO] = { - .fan_mult = 1, - .fan_max = I8K_FAN_HIGH, - }, - [DELL_XPS] = { - .fan_mult = 1, - .fan_max = I8K_FAN_HIGH, - }, -}; - -static struct dmi_system_id i8k_dmi_table[] __initdata = { - { - .ident = "Dell Inspiron", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"), - }, - }, - { - .ident = "Dell Latitude", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), - }, - }, - { - .ident = "Dell Inspiron 2", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"), - }, - }, - { - .ident = "Dell Latitude D520", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D520"), - }, - .driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520], - }, - { - .ident = "Dell Latitude 2", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), - }, - }, - { /* UK Inspiron 6400 */ - .ident = "Dell Inspiron 3", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "MM061"), - }, - }, - { - .ident = "Dell Inspiron 3", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "MP061"), - }, - }, - { - .ident = "Dell Precision 490", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, - "Precision WorkStation 490"), - }, - .driver_data = (void *)&i8k_config_data[DELL_PRECISION_490], - }, - { - .ident = "Dell Precision", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Precision"), - }, - }, - { - .ident = "Dell Vostro", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - 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 *)&i8k_config_data[DELL_STUDIO], - }, - { - .ident = "Dell XPS 13", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "XPS13"), - }, - .driver_data = (void *)&i8k_config_data[DELL_XPS], - }, - { - .ident = "Dell XPS M140", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"), - }, - .driver_data = (void *)&i8k_config_data[DELL_XPS], - }, - { } -}; - -MODULE_DEVICE_TABLE(dmi, i8k_dmi_table); - -static struct dmi_system_id i8k_blacklist_dmi_table[] __initdata = { - { - /* - * CPU fan speed going up and down on Dell Studio XPS 8100 - * for unknown reasons. - */ - .ident = "Dell Studio XPS 8100", - .matches = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8100"), - }, - }, - { } -}; - -/* - * Probe for the presence of a supported laptop. - */ -static int __init i8k_probe(void) -{ - const struct dmi_system_id *id; - int fan, ret; - - /* - * Get DMI information - */ - if (!dmi_check_system(i8k_dmi_table) || - dmi_check_system(i8k_blacklist_dmi_table)) { - if (!ignore_dmi && !force) - return -ENODEV; - - 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)); - - /* - * Get SMM Dell signature - */ - if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) && - i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) { - pr_err("unable to get SMM Dell signature\n"); - if (!force) - return -ENODEV; - } - - /* - * Set fan multiplier and maximal fan speed from dmi config - * Values specified in module parameters override values from dmi - */ - id = dmi_first_match(i8k_dmi_table); - if (id && id->driver_data) { - const struct i8k_config_data *conf = id->driver_data; - if (!fan_mult && conf->fan_mult) - fan_mult = conf->fan_mult; - if (!fan_max && conf->fan_max) - fan_max = conf->fan_max; - } - - i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */ - i8k_pwm_mult = DIV_ROUND_UP(255, i8k_fan_max); - - if (!fan_mult) { - /* - * Autodetect fan multiplier based on nominal rpm - * If fan reports rpm value too high then set multiplier to 1 - */ - for (fan = 0; fan < 2; ++fan) { - ret = i8k_get_fan_nominal_speed(fan, i8k_fan_max); - if (ret < 0) - continue; - if (ret > I8K_FAN_MAX_RPM) - i8k_fan_mult = 1; - break; - } - } else { - /* Fan multiplier was specified in module param or in dmi */ - i8k_fan_mult = fan_mult; - } - - return 0; -} - -static int __init i8k_init(void) -{ - struct proc_dir_entry *proc_i8k; - int err; - - /* Are we running on an supported laptop? */ - if (i8k_probe()) - return -ENODEV; - - /* Register the proc entry */ - proc_i8k = proc_create("i8k", 0, NULL, &i8k_fops); - if (!proc_i8k) - return -ENOENT; - - err = i8k_init_hwmon(); - if (err) - goto exit_remove_proc; - - return 0; - - exit_remove_proc: - remove_proc_entry("i8k", NULL); - return err; -} - -static void __exit i8k_exit(void) -{ - hwmon_device_unregister(i8k_hwmon_dev); - remove_proc_entry("i8k", NULL); -} - -module_init(i8k_init); -module_exit(i8k_exit); diff --git a/kernel/drivers/char/ipmi/ipmi_bt_sm.c b/kernel/drivers/char/ipmi/ipmi_bt_sm.c index 61e716166..feafdab73 100644 --- a/kernel/drivers/char/ipmi/ipmi_bt_sm.c +++ b/kernel/drivers/char/ipmi/ipmi_bt_sm.c @@ -694,7 +694,7 @@ static int bt_size(void) return sizeof(struct si_sm_data); } -struct si_sm_handlers bt_smi_handlers = { +const struct si_sm_handlers bt_smi_handlers = { .init_data = bt_init_data, .start_transaction = bt_start_transaction, .get_result = bt_get_result, diff --git a/kernel/drivers/char/ipmi/ipmi_kcs_sm.c b/kernel/drivers/char/ipmi/ipmi_kcs_sm.c index 8c25f5968..1da61af7f 100644 --- a/kernel/drivers/char/ipmi/ipmi_kcs_sm.c +++ b/kernel/drivers/char/ipmi/ipmi_kcs_sm.c @@ -540,7 +540,7 @@ static void kcs_cleanup(struct si_sm_data *kcs) { } -struct si_sm_handlers kcs_smi_handlers = { +const struct si_sm_handlers kcs_smi_handlers = { .init_data = init_kcs_data, .start_transaction = start_kcs_transaction, .get_result = get_kcs_result, diff --git a/kernel/drivers/char/ipmi/ipmi_msghandler.c b/kernel/drivers/char/ipmi/ipmi_msghandler.c index bf75f6361..e3536da05 100644 --- a/kernel/drivers/char/ipmi/ipmi_msghandler.c +++ b/kernel/drivers/char/ipmi/ipmi_msghandler.c @@ -342,7 +342,7 @@ struct ipmi_smi { * an umpreemptible region to use this. You must fetch the * value into a local variable and make sure it is not NULL. */ - struct ipmi_smi_handlers *handlers; + const struct ipmi_smi_handlers *handlers; void *send_info; #ifdef CONFIG_PROC_FS @@ -744,7 +744,13 @@ static void deliver_response(struct ipmi_recv_msg *msg) ipmi_inc_stat(intf, unhandled_local_responses); } ipmi_free_recv_msg(msg); - } else { + } else if (!oops_in_progress) { + /* + * If we are running in the panic context, calling the + * receive handler doesn't much meaning and has a deadlock + * risk. At this moment, simply skip it in that case. + */ + ipmi_user_t user = msg->user; user->handler->ipmi_recv_hndl(msg, user->handler_data); } @@ -1015,7 +1021,7 @@ int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data) { int rv = 0; ipmi_smi_t intf; - struct ipmi_smi_handlers *handlers; + const struct ipmi_smi_handlers *handlers; mutex_lock(&ipmi_interfaces_mutex); list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { @@ -1501,7 +1507,7 @@ static struct ipmi_smi_msg *smi_add_send_msg(ipmi_smi_t intf, } -static void smi_send(ipmi_smi_t intf, struct ipmi_smi_handlers *handlers, +static void smi_send(ipmi_smi_t intf, const struct ipmi_smi_handlers *handlers, struct ipmi_smi_msg *smi_msg, int priority) { int run_to_completion = intf->run_to_completion; @@ -2747,7 +2753,7 @@ void ipmi_poll_interface(ipmi_user_t user) } EXPORT_SYMBOL(ipmi_poll_interface); -int ipmi_register_smi(struct ipmi_smi_handlers *handlers, +int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, void *send_info, struct ipmi_device_id *device_id, struct device *si_dev, @@ -3959,6 +3965,10 @@ free_msg: if (!run_to_completion) spin_lock_irqsave(&intf->xmit_msgs_lock, flags); + /* + * We can get an asynchronous event or receive message in addition + * to commands we send. + */ if (msg == intf->curr_msg) intf->curr_msg = NULL; if (!run_to_completion) @@ -4015,7 +4025,7 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, unsigned int *waiting_msgs) { struct ipmi_recv_msg *msg; - struct ipmi_smi_handlers *handlers; + const struct ipmi_smi_handlers *handlers; if (intf->in_shutdown) return; @@ -4082,7 +4092,7 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, ipmi_inc_stat(intf, retransmitted_ipmb_commands); - smi_send(intf, intf->handlers, smi_msg, 0); + smi_send(intf, handlers, smi_msg, 0); } else ipmi_free_smi_msg(smi_msg); @@ -4291,6 +4301,9 @@ static void ipmi_panic_request_and_wait(ipmi_smi_t intf, 0, 1); /* Don't retry, and don't wait. */ if (rv) atomic_sub(2, &panic_done_count); + else if (intf->handlers->flush_messages) + intf->handlers->flush_messages(intf->send_info); + while (atomic_read(&panic_done_count) != 0) ipmi_poll(intf); } @@ -4364,9 +4377,7 @@ static void send_panic_events(char *str) /* Interface is not ready. */ continue; - intf->run_to_completion = 1; /* Send the event announcing the panic. */ - intf->handlers->set_run_to_completion(intf->send_info, 1); ipmi_panic_request_and_wait(intf, &addr, &msg); } @@ -4506,6 +4517,23 @@ static int panic_event(struct notifier_block *this, /* Interface is not ready. */ continue; + /* + * If we were interrupted while locking xmit_msgs_lock or + * waiting_rcv_msgs_lock, the corresponding list may be + * corrupted. In this case, drop items on the list for + * the safety. + */ + if (!spin_trylock(&intf->xmit_msgs_lock)) { + INIT_LIST_HEAD(&intf->xmit_msgs); + INIT_LIST_HEAD(&intf->hp_xmit_msgs); + } else + spin_unlock(&intf->xmit_msgs_lock); + + if (!spin_trylock(&intf->waiting_rcv_msgs_lock)) + INIT_LIST_HEAD(&intf->waiting_rcv_msgs); + else + spin_unlock(&intf->waiting_rcv_msgs_lock); + intf->run_to_completion = 1; intf->handlers->set_run_to_completion(intf->send_info, 1); } diff --git a/kernel/drivers/char/ipmi/ipmi_powernv.c b/kernel/drivers/char/ipmi/ipmi_powernv.c index 8753b0f6a..6e658aa11 100644 --- a/kernel/drivers/char/ipmi/ipmi_powernv.c +++ b/kernel/drivers/char/ipmi/ipmi_powernv.c @@ -15,6 +15,8 @@ #include <linux/list.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/interrupt.h> #include <asm/opal.h> @@ -23,8 +25,7 @@ struct ipmi_smi_powernv { u64 interface_id; struct ipmi_device_id ipmi_id; ipmi_smi_t intf; - u64 event; - struct notifier_block event_nb; + unsigned int irq; /** * We assume that there can only be one outstanding request, so @@ -142,8 +143,15 @@ static int ipmi_powernv_recv(struct ipmi_smi_powernv *smi) pr_devel("%s: -> %d (size %lld)\n", __func__, rc, rc == 0 ? size : 0); if (rc) { + /* If came via the poll, and response was not yet ready */ + if (rc == OPAL_EMPTY) { + spin_unlock_irqrestore(&smi->msg_lock, flags); + return 0; + } + + smi->cur_msg = NULL; spin_unlock_irqrestore(&smi->msg_lock, flags); - ipmi_free_smi_msg(msg); + send_error_reply(smi, msg, IPMI_ERR_UNSPECIFIED); return 0; } @@ -197,15 +205,12 @@ static struct ipmi_smi_handlers ipmi_powernv_smi_handlers = { .poll = ipmi_powernv_poll, }; -static int ipmi_opal_event(struct notifier_block *nb, - unsigned long events, void *change) +static irqreturn_t ipmi_opal_event(int irq, void *data) { - struct ipmi_smi_powernv *smi = container_of(nb, - struct ipmi_smi_powernv, event_nb); + struct ipmi_smi_powernv *smi = data; - if (events & smi->event) - ipmi_powernv_recv(smi); - return 0; + ipmi_powernv_recv(smi); + return IRQ_HANDLED; } static int ipmi_powernv_probe(struct platform_device *pdev) @@ -240,13 +245,16 @@ static int ipmi_powernv_probe(struct platform_device *pdev) goto err_free; } - ipmi->event = 1ull << prop; - ipmi->event_nb.notifier_call = ipmi_opal_event; + ipmi->irq = irq_of_parse_and_map(dev->of_node, 0); + if (!ipmi->irq) { + dev_info(dev, "Unable to map irq from device tree\n"); + ipmi->irq = opal_event_request(prop); + } - rc = opal_notifier_register(&ipmi->event_nb); - if (rc) { - dev_warn(dev, "OPAL notifier registration failed (%d)\n", rc); - goto err_free; + if (request_irq(ipmi->irq, ipmi_opal_event, IRQ_TYPE_LEVEL_HIGH, + "opal-ipmi", ipmi)) { + dev_warn(dev, "Unable to request irq\n"); + goto err_dispose; } ipmi->opal_msg = devm_kmalloc(dev, @@ -271,7 +279,9 @@ static int ipmi_powernv_probe(struct platform_device *pdev) err_free_msg: devm_kfree(dev, ipmi->opal_msg); err_unregister: - opal_notifier_unregister(&ipmi->event_nb); + free_irq(ipmi->irq, ipmi); +err_dispose: + irq_dispose_mapping(ipmi->irq); err_free: devm_kfree(dev, ipmi); return rc; @@ -282,7 +292,9 @@ static int ipmi_powernv_remove(struct platform_device *pdev) struct ipmi_smi_powernv *smi = dev_get_drvdata(&pdev->dev); ipmi_unregister_smi(smi->intf); - opal_notifier_unregister(&smi->event_nb); + free_irq(smi->irq, smi); + irq_dispose_mapping(smi->irq); + return 0; } @@ -295,7 +307,6 @@ static const struct of_device_id ipmi_powernv_match[] = { static struct platform_driver powernv_ipmi_driver = { .driver = { .name = "ipmi-powernv", - .owner = THIS_MODULE, .of_match_table = ipmi_powernv_match, }, .probe = ipmi_powernv_probe, diff --git a/kernel/drivers/char/ipmi/ipmi_si_intf.c b/kernel/drivers/char/ipmi/ipmi_si_intf.c index 8a45e92ff..4cc72fa01 100644 --- a/kernel/drivers/char/ipmi/ipmi_si_intf.c +++ b/kernel/drivers/char/ipmi/ipmi_si_intf.c @@ -64,7 +64,6 @@ #include <linux/dmi.h> #include <linux/string.h> #include <linux/ctype.h> -#include <linux/pnp.h> #include <linux/of_device.h> #include <linux/of_platform.h> #include <linux/of_address.h> @@ -164,7 +163,7 @@ struct smi_info { int intf_num; ipmi_smi_t intf; struct si_sm_data *si_sm; - struct si_sm_handlers *handlers; + const struct si_sm_handlers *handlers; enum si_type si_type; spinlock_t si_lock; struct ipmi_smi_msg *waiting_msg; @@ -263,9 +262,21 @@ struct smi_info { bool supports_event_msg_buff; /* - * Can we clear the global enables receive irq bit? + * Can we disable interrupts the global enables receive irq + * bit? There are currently two forms of brokenness, some + * systems cannot disable the bit (which is technically within + * the spec but a bad idea) and some systems have the bit + * forced to zero even though interrupts work (which is + * clearly outside the spec). The next bool tells which form + * of brokenness is present. */ - bool cannot_clear_recv_irq_bit; + bool cannot_disable_irq; + + /* + * Some systems are broken and cannot set the irq enable + * bit, even if they support interrupts. + */ + bool irq_enable_broken; /* * Did we get an attention that we did not handle? @@ -309,9 +320,6 @@ static int num_force_kipmid; #ifdef CONFIG_PCI static bool pci_registered; #endif -#ifdef CONFIG_ACPI -static bool pnp_registered; -#endif #ifdef CONFIG_PARISC static bool parisc_registered; #endif @@ -404,18 +412,42 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info) return rv; } -static void start_check_enables(struct smi_info *smi_info) +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; +} + +/* + * Start a new message and (re)start the timer and thread. + */ +static void start_new_msg(struct smi_info *smi_info, unsigned char *msg, + unsigned int size) +{ + smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES); + + if (smi_info->thread) + wake_up_process(smi_info->thread); + + smi_info->handlers->start_transaction(smi_info->si_sm, msg, size); +} + +static void start_check_enables(struct smi_info *smi_info, bool start_timer) { unsigned char msg[2]; msg[0] = (IPMI_NETFN_APP_REQUEST << 2); msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; - smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2); + if (start_timer) + start_new_msg(smi_info, msg, 2); + else + smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2); smi_info->si_state = SI_CHECKING_ENABLES; } -static void start_clear_flags(struct smi_info *smi_info) +static void start_clear_flags(struct smi_info *smi_info, bool start_timer) { unsigned char msg[3]; @@ -424,7 +456,10 @@ static void start_clear_flags(struct smi_info *smi_info) msg[1] = IPMI_CLEAR_MSG_FLAGS_CMD; msg[2] = WDT_PRE_TIMEOUT_INT; - smi_info->handlers->start_transaction(smi_info->si_sm, msg, 3); + if (start_timer) + start_new_msg(smi_info, msg, 3); + else + smi_info->handlers->start_transaction(smi_info->si_sm, msg, 3); smi_info->si_state = SI_CLEARING_FLAGS; } @@ -434,10 +469,8 @@ static void start_getting_msg_queue(struct smi_info *smi_info) smi_info->curr_msg->data[1] = IPMI_GET_MSG_CMD; smi_info->curr_msg->data_size = 2; - smi_info->handlers->start_transaction( - smi_info->si_sm, - smi_info->curr_msg->data, - smi_info->curr_msg->data_size); + start_new_msg(smi_info, smi_info->curr_msg->data, + smi_info->curr_msg->data_size); smi_info->si_state = SI_GETTING_MESSAGES; } @@ -447,20 +480,11 @@ static void start_getting_events(struct smi_info *smi_info) smi_info->curr_msg->data[1] = IPMI_READ_EVENT_MSG_BUFFER_CMD; smi_info->curr_msg->data_size = 2; - smi_info->handlers->start_transaction( - smi_info->si_sm, - smi_info->curr_msg->data, - smi_info->curr_msg->data_size); + start_new_msg(smi_info, smi_info->curr_msg->data, + smi_info->curr_msg->data_size); smi_info->si_state = SI_GETTING_EVENTS; } -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 @@ -470,11 +494,11 @@ static void smi_mod_timer(struct smi_info *smi_info, unsigned long new_val) * Note that we cannot just use disable_irq(), since the interrupt may * be shared. */ -static inline bool disable_si_irq(struct smi_info *smi_info) +static inline bool disable_si_irq(struct smi_info *smi_info, bool start_timer) { if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { smi_info->interrupt_disabled = true; - start_check_enables(smi_info); + start_check_enables(smi_info, start_timer); return true; } return false; @@ -484,7 +508,7 @@ static inline bool enable_si_irq(struct smi_info *smi_info) { if ((smi_info->irq) && (smi_info->interrupt_disabled)) { smi_info->interrupt_disabled = false; - start_check_enables(smi_info); + start_check_enables(smi_info, true); return true; } return false; @@ -502,7 +526,7 @@ static struct ipmi_smi_msg *alloc_msg_handle_irq(struct smi_info *smi_info) msg = ipmi_alloc_smi_msg(); if (!msg) { - if (!disable_si_irq(smi_info)) + if (!disable_si_irq(smi_info, true)) smi_info->si_state = SI_NORMAL; } else if (enable_si_irq(smi_info)) { ipmi_free_smi_msg(msg); @@ -518,7 +542,7 @@ static void handle_flags(struct smi_info *smi_info) /* Watchdog pre-timeout */ smi_inc_stat(smi_info, watchdog_pretimeouts); - start_clear_flags(smi_info); + start_clear_flags(smi_info, true); smi_info->msg_flags &= ~WDT_PRE_TIMEOUT_INT; if (smi_info->intf) ipmi_smi_watchdog_pretimeout(smi_info->intf); @@ -558,13 +582,14 @@ static u8 current_global_enables(struct smi_info *smi_info, u8 base, if (smi_info->supports_event_msg_buff) enables |= IPMI_BMC_EVT_MSG_BUFF; - if ((smi_info->irq && !smi_info->interrupt_disabled) || - smi_info->cannot_clear_recv_irq_bit) + if (((smi_info->irq && !smi_info->interrupt_disabled) || + smi_info->cannot_disable_irq) && + !smi_info->irq_enable_broken) enables |= IPMI_BMC_RCV_MSG_INTR; if (smi_info->supports_event_msg_buff && - smi_info->irq && !smi_info->interrupt_disabled) - + smi_info->irq && !smi_info->interrupt_disabled && + !smi_info->irq_enable_broken) enables |= IPMI_BMC_EVT_MSG_INTR; *irq_on = enables & (IPMI_BMC_EVT_MSG_INTR | IPMI_BMC_RCV_MSG_INTR); @@ -870,8 +895,7 @@ static enum si_sm_result smi_event_handler(struct smi_info *smi_info, msg[0] = (IPMI_NETFN_APP_REQUEST << 2); msg[1] = IPMI_GET_MSG_FLAGS_CMD; - smi_info->handlers->start_transaction( - smi_info->si_sm, msg, 2); + start_new_msg(smi_info, msg, 2); smi_info->si_state = SI_GETTING_FLAGS; goto restart; } @@ -901,7 +925,7 @@ static enum si_sm_result smi_event_handler(struct smi_info *smi_info, * disable and messages disabled. */ if (smi_info->supports_event_msg_buff || smi_info->irq) { - start_check_enables(smi_info); + start_check_enables(smi_info, true); } else { smi_info->curr_msg = alloc_msg_handle_irq(smi_info); if (!smi_info->curr_msg) @@ -911,6 +935,13 @@ static enum si_sm_result smi_event_handler(struct smi_info *smi_info, } goto restart; } + + if (si_sm_result == SI_SM_IDLE && smi_info->timer_running) { + /* Ok it if fails, the timer will just go off. */ + if (del_timer(&smi_info->si_timer)) + smi_info->timer_running = false; + } + out: return si_sm_result; } @@ -928,33 +959,36 @@ static void check_start_timer_thread(struct smi_info *smi_info) } } +static void flush_messages(void *send_info) +{ + struct smi_info *smi_info = send_info; + enum si_sm_result result; + + /* + * Currently, this function is called only in run-to-completion + * mode. This means we are single-threaded, no need for locks. + */ + result = smi_event_handler(smi_info, 0); + while (result != SI_SM_IDLE) { + udelay(SI_SHORT_TIMEOUT_USEC); + result = smi_event_handler(smi_info, SI_SHORT_TIMEOUT_USEC); + } +} + static void sender(void *send_info, struct ipmi_smi_msg *msg) { struct smi_info *smi_info = send_info; - enum si_sm_result result; unsigned long flags; debug_timestamp("Enqueue"); if (smi_info->run_to_completion) { /* - * If we are running to completion, start it and run - * transactions until everything is clear. + * If we are running to completion, start it. Upper + * layer will call flush_messages to clear it out. */ smi_info->waiting_msg = msg; - - /* - * Run to completion means we are single-threaded, no - * need for locks. - */ - - result = smi_event_handler(smi_info, 0); - while (result != SI_SM_IDLE) { - udelay(SI_SHORT_TIMEOUT_USEC); - result = smi_event_handler(smi_info, - SI_SHORT_TIMEOUT_USEC); - } return; } @@ -975,17 +1009,10 @@ static void sender(void *send_info, 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; smi_info->run_to_completion = i_run_to_completion; - if (i_run_to_completion) { - result = smi_event_handler(smi_info, 0); - while (result != SI_SM_IDLE) { - udelay(SI_SHORT_TIMEOUT_USEC); - result = smi_event_handler(smi_info, - SI_SHORT_TIMEOUT_USEC); - } - } + if (i_run_to_completion) + flush_messages(smi_info); } /* @@ -1203,14 +1230,14 @@ static int smi_start_processing(void *send_info, new_smi->intf = intf; - /* Try to claim any interrupts. */ - if (new_smi->irq_setup) - new_smi->irq_setup(new_smi); - /* Set up the timer that drives the interface. */ setup_timer(&new_smi->si_timer, smi_timeout, (long)new_smi); smi_mod_timer(new_smi, jiffies + SI_TIMEOUT_JIFFIES); + /* Try to claim any interrupts. */ + if (new_smi->irq_setup) + new_smi->irq_setup(new_smi); + /* * Check if the user forcefully enabled the daemon. */ @@ -1258,7 +1285,7 @@ static void set_maintenance_mode(void *send_info, bool enable) atomic_set(&smi_info->req_events, 0); } -static struct ipmi_smi_handlers handlers = { +static const struct ipmi_smi_handlers handlers = { .owner = THIS_MODULE, .start_processing = smi_start_processing, .get_smi_info = get_smi_info, @@ -1267,6 +1294,7 @@ static struct ipmi_smi_handlers handlers = { .set_need_watch = set_need_watch, .set_maintenance_mode = set_maintenance_mode, .set_run_to_completion = set_run_to_completion, + .flush_messages = flush_messages, .poll = poll, }; @@ -1283,14 +1311,14 @@ static int smi_num; /* Used to sequence the SMIs */ #define DEFAULT_REGSIZE 1 #ifdef CONFIG_ACPI -static bool si_tryacpi = 1; +static bool si_tryacpi = true; #endif #ifdef CONFIG_DMI -static bool si_trydmi = 1; +static bool si_trydmi = true; #endif -static bool si_tryplatform = 1; +static bool si_tryplatform = true; #ifdef CONFIG_PCI -static bool si_trypci = 1; +static bool si_trypci = true; #endif static bool si_trydefaults = IS_ENABLED(CONFIG_IPMI_SI_PROBE_DEFAULTS); static char *si_type[SI_MAX_PARMS]; @@ -1446,14 +1474,14 @@ static int std_irq_setup(struct smi_info *info) return rv; } -static unsigned char port_inb(struct si_sm_io *io, unsigned int offset) +static unsigned char port_inb(const struct si_sm_io *io, unsigned int offset) { unsigned int addr = io->addr_data; return inb(addr + (offset * io->regspacing)); } -static void port_outb(struct si_sm_io *io, unsigned int offset, +static void port_outb(const struct si_sm_io *io, unsigned int offset, unsigned char b) { unsigned int addr = io->addr_data; @@ -1461,14 +1489,14 @@ static void port_outb(struct si_sm_io *io, unsigned int offset, outb(b, addr + (offset * io->regspacing)); } -static unsigned char port_inw(struct si_sm_io *io, unsigned int offset) +static unsigned char port_inw(const struct si_sm_io *io, unsigned int offset) { unsigned int addr = io->addr_data; return (inw(addr + (offset * io->regspacing)) >> io->regshift) & 0xff; } -static void port_outw(struct si_sm_io *io, unsigned int offset, +static void port_outw(const struct si_sm_io *io, unsigned int offset, unsigned char b) { unsigned int addr = io->addr_data; @@ -1476,14 +1504,14 @@ static void port_outw(struct si_sm_io *io, unsigned int offset, outw(b << io->regshift, addr + (offset * io->regspacing)); } -static unsigned char port_inl(struct si_sm_io *io, unsigned int offset) +static unsigned char port_inl(const struct si_sm_io *io, unsigned int offset) { unsigned int addr = io->addr_data; return (inl(addr + (offset * io->regspacing)) >> io->regshift) & 0xff; } -static void port_outl(struct si_sm_io *io, unsigned int offset, +static void port_outl(const struct si_sm_io *io, unsigned int offset, unsigned char b) { unsigned int addr = io->addr_data; @@ -1556,49 +1584,52 @@ static int port_setup(struct smi_info *info) return 0; } -static unsigned char intf_mem_inb(struct si_sm_io *io, unsigned int offset) +static unsigned char intf_mem_inb(const struct si_sm_io *io, + unsigned int offset) { return readb((io->addr)+(offset * io->regspacing)); } -static void intf_mem_outb(struct si_sm_io *io, unsigned int offset, - unsigned char b) +static void intf_mem_outb(const struct si_sm_io *io, unsigned int offset, + unsigned char b) { writeb(b, (io->addr)+(offset * io->regspacing)); } -static unsigned char intf_mem_inw(struct si_sm_io *io, unsigned int offset) +static unsigned char intf_mem_inw(const struct si_sm_io *io, + unsigned int offset) { return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift) & 0xff; } -static void intf_mem_outw(struct si_sm_io *io, unsigned int offset, - unsigned char b) +static void intf_mem_outw(const struct si_sm_io *io, unsigned int offset, + unsigned char b) { writeb(b << io->regshift, (io->addr)+(offset * io->regspacing)); } -static unsigned char intf_mem_inl(struct si_sm_io *io, unsigned int offset) +static unsigned char intf_mem_inl(const struct si_sm_io *io, + unsigned int offset) { return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift) & 0xff; } -static void intf_mem_outl(struct si_sm_io *io, unsigned int offset, - unsigned char b) +static void intf_mem_outl(const struct si_sm_io *io, unsigned int offset, + unsigned char b) { writel(b << io->regshift, (io->addr)+(offset * io->regspacing)); } #ifdef readq -static unsigned char mem_inq(struct si_sm_io *io, unsigned int offset) +static unsigned char mem_inq(const struct si_sm_io *io, unsigned int offset) { return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift) & 0xff; } -static void mem_outq(struct si_sm_io *io, unsigned int offset, +static void mem_outq(const struct si_sm_io *io, unsigned int offset, unsigned char b) { writeq(b << io->regshift, (io->addr)+(offset * io->regspacing)); @@ -2233,134 +2264,6 @@ static void spmi_find_bmc(void) try_init_spmi(spmi); } } - -static int ipmi_pnp_probe(struct pnp_dev *dev, - const struct pnp_device_id *dev_id) -{ - struct acpi_device *acpi_dev; - struct smi_info *info; - struct resource *res, *res_second; - acpi_handle handle; - acpi_status status; - unsigned long long tmp; - int rv = -EINVAL; - - acpi_dev = pnp_acpi_device(dev); - if (!acpi_dev) - return -ENODEV; - - info = smi_info_alloc(); - if (!info) - return -ENOMEM; - - info->addr_source = SI_ACPI; - printk(KERN_INFO PFX "probing via ACPI\n"); - - handle = acpi_dev->handle; - info->addr_info.acpi_info.acpi_handle = handle; - - /* _IFT tells us the interface type: KCS, BT, etc */ - status = acpi_evaluate_integer(handle, "_IFT", NULL, &tmp); - if (ACPI_FAILURE(status)) { - dev_err(&dev->dev, "Could not find ACPI IPMI interface type\n"); - goto err_free; - } - - switch (tmp) { - case 1: - info->si_type = SI_KCS; - break; - case 2: - info->si_type = SI_SMIC; - break; - case 3: - info->si_type = SI_BT; - break; - case 4: /* SSIF, just ignore */ - rv = -ENODEV; - goto err_free; - default: - dev_info(&dev->dev, "unknown IPMI type %lld\n", tmp); - goto err_free; - } - - res = pnp_get_resource(dev, IORESOURCE_IO, 0); - if (res) { - info->io_setup = port_setup; - info->io.addr_type = IPMI_IO_ADDR_SPACE; - } else { - res = pnp_get_resource(dev, IORESOURCE_MEM, 0); - if (res) { - info->io_setup = mem_setup; - info->io.addr_type = IPMI_MEM_ADDR_SPACE; - } - } - if (!res) { - dev_err(&dev->dev, "no I/O or memory address\n"); - goto err_free; - } - info->io.addr_data = res->start; - - info->io.regspacing = DEFAULT_REGSPACING; - res_second = pnp_get_resource(dev, - (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? - IORESOURCE_IO : IORESOURCE_MEM, - 1); - if (res_second) { - if (res_second->start > info->io.addr_data) - info->io.regspacing = res_second->start - info->io.addr_data; - } - info->io.regsize = DEFAULT_REGSPACING; - info->io.regshift = 0; - - /* If _GPE exists, use it; otherwise use standard interrupts */ - status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp); - if (ACPI_SUCCESS(status)) { - info->irq = tmp; - info->irq_setup = acpi_gpe_irq_setup; - } else if (pnp_irq_valid(dev, 0)) { - info->irq = pnp_irq(dev, 0); - info->irq_setup = std_irq_setup; - } - - info->dev = &dev->dev; - pnp_set_drvdata(dev, info); - - dev_info(info->dev, "%pR regsize %d spacing %d irq %d\n", - res, info->io.regsize, info->io.regspacing, - info->irq); - - rv = add_smi(info); - if (rv) - kfree(info); - - return rv; - -err_free: - kfree(info); - return rv; -} - -static void ipmi_pnp_remove(struct pnp_dev *dev) -{ - struct smi_info *info = pnp_get_drvdata(dev); - - cleanup_one_si(info); -} - -static const struct pnp_device_id pnp_dev_table[] = { - {"IPI0001", 0}, - {"", 0}, -}; - -static struct pnp_driver ipmi_pnp_driver = { - .name = DEVICE_NAME, - .probe = ipmi_pnp_probe, - .remove = ipmi_pnp_remove, - .id_table = pnp_dev_table, -}; - -MODULE_DEVICE_TABLE(pnp, pnp_dev_table); #endif #ifdef CONFIG_DMI @@ -2654,7 +2557,7 @@ static void ipmi_pci_remove(struct pci_dev *pdev) pci_disable_device(pdev); } -static struct pci_device_id ipmi_pci_devices[] = { +static const 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) }, { 0, } @@ -2669,10 +2572,20 @@ static struct pci_driver ipmi_pci_driver = { }; #endif /* CONFIG_PCI */ -static const struct of_device_id ipmi_match[]; -static int ipmi_probe(struct platform_device *dev) -{ #ifdef CONFIG_OF +static const struct of_device_id of_ipmi_match[] = { + { .type = "ipmi", .compatible = "ipmi-kcs", + .data = (void *)(unsigned long) SI_KCS }, + { .type = "ipmi", .compatible = "ipmi-smic", + .data = (void *)(unsigned long) SI_SMIC }, + { .type = "ipmi", .compatible = "ipmi-bt", + .data = (void *)(unsigned long) SI_BT }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_ipmi_match); + +static int of_ipmi_probe(struct platform_device *dev) +{ const struct of_device_id *match; struct smi_info *info; struct resource resource; @@ -2683,9 +2596,9 @@ static int ipmi_probe(struct platform_device *dev) dev_info(&dev->dev, "probing via device tree\n"); - match = of_match_device(ipmi_match, &dev->dev); + match = of_match_device(of_ipmi_match, &dev->dev); if (!match) - return -EINVAL; + return -ENODEV; if (!of_device_is_available(np)) return -EINVAL; @@ -2754,33 +2667,159 @@ static int ipmi_probe(struct platform_device *dev) kfree(info); return ret; } -#endif return 0; } +#else +#define of_ipmi_match NULL +static int of_ipmi_probe(struct platform_device *dev) +{ + return -ENODEV; +} +#endif -static int ipmi_remove(struct platform_device *dev) +#ifdef CONFIG_ACPI +static int acpi_ipmi_probe(struct platform_device *dev) { -#ifdef CONFIG_OF - cleanup_one_si(dev_get_drvdata(&dev->dev)); + struct smi_info *info; + struct resource *res, *res_second; + acpi_handle handle; + acpi_status status; + unsigned long long tmp; + int rv = -EINVAL; + + handle = ACPI_HANDLE(&dev->dev); + if (!handle) + return -ENODEV; + + info = smi_info_alloc(); + if (!info) + return -ENOMEM; + + info->addr_source = SI_ACPI; + dev_info(&dev->dev, PFX "probing via ACPI\n"); + + info->addr_info.acpi_info.acpi_handle = handle; + + /* _IFT tells us the interface type: KCS, BT, etc */ + status = acpi_evaluate_integer(handle, "_IFT", NULL, &tmp); + if (ACPI_FAILURE(status)) { + dev_err(&dev->dev, "Could not find ACPI IPMI interface type\n"); + goto err_free; + } + + switch (tmp) { + case 1: + info->si_type = SI_KCS; + break; + case 2: + info->si_type = SI_SMIC; + break; + case 3: + info->si_type = SI_BT; + break; + case 4: /* SSIF, just ignore */ + rv = -ENODEV; + goto err_free; + default: + dev_info(&dev->dev, "unknown IPMI type %lld\n", tmp); + goto err_free; + } + + res = platform_get_resource(dev, IORESOURCE_IO, 0); + if (res) { + info->io_setup = port_setup; + info->io.addr_type = IPMI_IO_ADDR_SPACE; + } else { + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (res) { + info->io_setup = mem_setup; + info->io.addr_type = IPMI_MEM_ADDR_SPACE; + } + } + if (!res) { + dev_err(&dev->dev, "no I/O or memory address\n"); + goto err_free; + } + info->io.addr_data = res->start; + + info->io.regspacing = DEFAULT_REGSPACING; + res_second = platform_get_resource(dev, + (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? + IORESOURCE_IO : IORESOURCE_MEM, + 1); + if (res_second) { + if (res_second->start > info->io.addr_data) + info->io.regspacing = + res_second->start - info->io.addr_data; + } + info->io.regsize = DEFAULT_REGSPACING; + info->io.regshift = 0; + + /* If _GPE exists, use it; otherwise use standard interrupts */ + status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp); + if (ACPI_SUCCESS(status)) { + info->irq = tmp; + info->irq_setup = acpi_gpe_irq_setup; + } else { + int irq = platform_get_irq(dev, 0); + + if (irq > 0) { + info->irq = irq; + info->irq_setup = std_irq_setup; + } + } + + info->dev = &dev->dev; + platform_set_drvdata(dev, info); + + dev_info(info->dev, "%pR regsize %d spacing %d irq %d\n", + res, info->io.regsize, info->io.regspacing, + info->irq); + + rv = add_smi(info); + if (rv) + kfree(info); + + return rv; + +err_free: + kfree(info); + return rv; +} + +static const struct acpi_device_id acpi_ipmi_match[] = { + { "IPI0001", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, acpi_ipmi_match); +#else +static int acpi_ipmi_probe(struct platform_device *dev) +{ + return -ENODEV; +} #endif - return 0; + +static int ipmi_probe(struct platform_device *dev) +{ + if (of_ipmi_probe(dev) == 0) + return 0; + + return acpi_ipmi_probe(dev); } -static const struct of_device_id ipmi_match[] = +static int ipmi_remove(struct platform_device *dev) { - { .type = "ipmi", .compatible = "ipmi-kcs", - .data = (void *)(unsigned long) SI_KCS }, - { .type = "ipmi", .compatible = "ipmi-smic", - .data = (void *)(unsigned long) SI_SMIC }, - { .type = "ipmi", .compatible = "ipmi-bt", - .data = (void *)(unsigned long) SI_BT }, - {}, -}; + struct smi_info *info = dev_get_drvdata(&dev->dev); + + cleanup_one_si(info); + return 0; +} static struct platform_driver ipmi_driver = { .driver = { .name = DEVICE_NAME, - .of_match_table = ipmi_match, + .of_match_table = of_ipmi_match, + .acpi_match_table = ACPI_PTR(acpi_ipmi_match), }, .probe = ipmi_probe, .remove = ipmi_remove, @@ -2905,12 +2944,7 @@ static int try_get_dev_id(struct smi_info *smi_info) return rv; } -/* - * Some BMCs do not support clearing the receive irq bit in the global - * enables (even if they don't support interrupts on the BMC). Check - * for this and handle it properly. - */ -static void check_clr_rcv_irq(struct smi_info *smi_info) +static int get_global_enables(struct smi_info *smi_info, u8 *enables) { unsigned char msg[3]; unsigned char *resp; @@ -2918,12 +2952,8 @@ static void check_clr_rcv_irq(struct smi_info *smi_info) int rv; resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); - if (!resp) { - printk(KERN_WARNING PFX "Out of memory allocating response for" - " global enables command, cannot check recv irq bit" - " handling.\n"); - return; - } + if (!resp) + return -ENOMEM; msg[0] = IPMI_NETFN_APP_REQUEST << 2; msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; @@ -2931,9 +2961,9 @@ static void check_clr_rcv_irq(struct smi_info *smi_info) rv = wait_for_msg_done(smi_info); if (rv) { - printk(KERN_WARNING PFX "Error getting response from get" - " global enables command, cannot check recv irq bit" - " handling.\n"); + dev_warn(smi_info->dev, + "Error getting response from get global enables command: %d\n", + rv); goto out; } @@ -2944,27 +2974,44 @@ static void check_clr_rcv_irq(struct smi_info *smi_info) resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || resp[1] != IPMI_GET_BMC_GLOBAL_ENABLES_CMD || resp[2] != 0) { - printk(KERN_WARNING PFX "Invalid return from get global" - " enables command, cannot check recv irq bit" - " handling.\n"); + dev_warn(smi_info->dev, + "Invalid return from get global enables command: %ld %x %x %x\n", + resp_len, resp[0], resp[1], resp[2]); rv = -EINVAL; goto out; + } else { + *enables = resp[3]; } - if ((resp[3] & IPMI_BMC_RCV_MSG_INTR) == 0) - /* Already clear, should work ok. */ - goto out; +out: + kfree(resp); + return rv; +} + +/* + * Returns 1 if it gets an error from the command. + */ +static int set_global_enables(struct smi_info *smi_info, u8 enables) +{ + unsigned char msg[3]; + unsigned char *resp; + unsigned long resp_len; + int rv; + + resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); + if (!resp) + return -ENOMEM; msg[0] = IPMI_NETFN_APP_REQUEST << 2; msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; - msg[2] = resp[3] & ~IPMI_BMC_RCV_MSG_INTR; + msg[2] = enables; smi_info->handlers->start_transaction(smi_info->si_sm, msg, 3); rv = wait_for_msg_done(smi_info); if (rv) { - printk(KERN_WARNING PFX "Error getting response from set" - " global enables command, cannot check recv irq bit" - " handling.\n"); + dev_warn(smi_info->dev, + "Error getting response from set global enables command: %d\n", + rv); goto out; } @@ -2974,25 +3021,93 @@ static void check_clr_rcv_irq(struct smi_info *smi_info) if (resp_len < 3 || resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || resp[1] != IPMI_SET_BMC_GLOBAL_ENABLES_CMD) { - printk(KERN_WARNING PFX "Invalid return from get global" - " enables command, cannot check recv irq bit" - " handling.\n"); + dev_warn(smi_info->dev, + "Invalid return from set global enables command: %ld %x %x\n", + resp_len, resp[0], resp[1]); rv = -EINVAL; goto out; } - if (resp[2] != 0) { + if (resp[2] != 0) + rv = 1; + +out: + kfree(resp); + return rv; +} + +/* + * Some BMCs do not support clearing the receive irq bit in the global + * enables (even if they don't support interrupts on the BMC). Check + * for this and handle it properly. + */ +static void check_clr_rcv_irq(struct smi_info *smi_info) +{ + u8 enables = 0; + int rv; + + rv = get_global_enables(smi_info, &enables); + if (!rv) { + if ((enables & IPMI_BMC_RCV_MSG_INTR) == 0) + /* Already clear, should work ok. */ + return; + + enables &= ~IPMI_BMC_RCV_MSG_INTR; + rv = set_global_enables(smi_info, enables); + } + + if (rv < 0) { + dev_err(smi_info->dev, + "Cannot check clearing the rcv irq: %d\n", rv); + return; + } + + if (rv) { /* * An error when setting the event buffer bit means * clearing the bit is not supported. */ - printk(KERN_WARNING PFX "The BMC does not support clearing" - " the recv irq bit, compensating, but the BMC needs to" - " be fixed.\n"); - smi_info->cannot_clear_recv_irq_bit = true; + dev_warn(smi_info->dev, + "The BMC does not support clearing the recv irq bit, compensating, but the BMC needs to be fixed.\n"); + smi_info->cannot_disable_irq = true; + } +} + +/* + * Some BMCs do not support setting the interrupt bits in the global + * enables even if they support interrupts. Clearly bad, but we can + * compensate. + */ +static void check_set_rcv_irq(struct smi_info *smi_info) +{ + u8 enables = 0; + int rv; + + if (!smi_info->irq) + return; + + rv = get_global_enables(smi_info, &enables); + if (!rv) { + enables |= IPMI_BMC_RCV_MSG_INTR; + rv = set_global_enables(smi_info, enables); + } + + if (rv < 0) { + dev_err(smi_info->dev, + "Cannot check setting the rcv irq: %d\n", rv); + return; + } + + if (rv) { + /* + * An error when setting the event buffer bit means + * setting the bit is not supported. + */ + dev_warn(smi_info->dev, + "The BMC does not support setting the recv irq bit, compensating, but the BMC needs to be fixed.\n"); + smi_info->cannot_disable_irq = true; + smi_info->irq_enable_broken = true; } - out: - kfree(resp); } static int try_enable_event_buffer(struct smi_info *smi_info) @@ -3313,6 +3428,12 @@ static void setup_xaction_handlers(struct smi_info *smi_info) setup_dell_poweredge_bt_xaction_handler(smi_info); } +static void check_for_broken_irqs(struct smi_info *smi_info) +{ + check_clr_rcv_irq(smi_info); + check_set_rcv_irq(smi_info); +} + static inline void wait_for_timer_and_thread(struct smi_info *smi_info) { if (smi_info->thread != NULL) @@ -3321,7 +3442,7 @@ static inline void wait_for_timer_and_thread(struct smi_info *smi_info) del_timer_sync(&smi_info->si_timer); } -static struct ipmi_default_vals +static const struct ipmi_default_vals { int type; int port; @@ -3490,10 +3611,9 @@ static int try_smi_init(struct smi_info *new_smi) goto out_err; } - check_clr_rcv_irq(new_smi); - setup_oem_data_handler(new_smi); setup_xaction_handlers(new_smi); + check_for_broken_irqs(new_smi); new_smi->waiting_msg = NULL; new_smi->curr_msg = NULL; @@ -3515,7 +3635,7 @@ static int try_smi_init(struct smi_info *new_smi) * Start clearing the flags before we enable interrupts or the * timer to avoid racing with the timer. */ - start_clear_flags(new_smi); + start_clear_flags(new_smi, false); /* * IRQ is defined to be set when non-zero. req_events will @@ -3692,13 +3812,6 @@ static int init_ipmi_si(void) } #endif -#ifdef CONFIG_ACPI - if (si_tryacpi) { - pnp_register_driver(&ipmi_pnp_driver); - pnp_registered = true; - } -#endif - #ifdef CONFIG_DMI if (si_trydmi) dmi_find_bmc(); @@ -3817,7 +3930,7 @@ static void cleanup_one_si(struct smi_info *to_clean) poll(to_clean); schedule_timeout_uninterruptible(1); } - disable_si_irq(to_clean); + disable_si_irq(to_clean, false); while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { poll(to_clean); schedule_timeout_uninterruptible(1); @@ -3850,10 +3963,6 @@ static void cleanup_ipmi_si(void) if (pci_registered) pci_unregister_driver(&ipmi_pci_driver); #endif -#ifdef CONFIG_ACPI - if (pnp_registered) - pnp_unregister_driver(&ipmi_pnp_driver); -#endif #ifdef CONFIG_PARISC if (parisc_registered) unregister_parisc_driver(&ipmi_parisc_driver); diff --git a/kernel/drivers/char/ipmi/ipmi_si_sm.h b/kernel/drivers/char/ipmi/ipmi_si_sm.h index df89f7347..a705027c0 100644 --- a/kernel/drivers/char/ipmi/ipmi_si_sm.h +++ b/kernel/drivers/char/ipmi/ipmi_si_sm.h @@ -46,8 +46,8 @@ struct si_sm_data; * this interface. */ struct si_sm_io { - unsigned char (*inputb)(struct si_sm_io *io, unsigned int offset); - void (*outputb)(struct si_sm_io *io, + unsigned char (*inputb)(const struct si_sm_io *io, unsigned int offset); + void (*outputb)(const struct si_sm_io *io, unsigned int offset, unsigned char b); @@ -135,7 +135,7 @@ struct si_sm_handlers { }; /* Current state machines that we can use. */ -extern struct si_sm_handlers kcs_smi_handlers; -extern struct si_sm_handlers smic_smi_handlers; -extern struct si_sm_handlers bt_smi_handlers; +extern const struct si_sm_handlers kcs_smi_handlers; +extern const struct si_sm_handlers smic_smi_handlers; +extern const struct si_sm_handlers bt_smi_handlers; diff --git a/kernel/drivers/char/ipmi/ipmi_smic_sm.c b/kernel/drivers/char/ipmi/ipmi_smic_sm.c index c8e77afa8..8f7c73ff5 100644 --- a/kernel/drivers/char/ipmi/ipmi_smic_sm.c +++ b/kernel/drivers/char/ipmi/ipmi_smic_sm.c @@ -589,7 +589,7 @@ static int smic_size(void) return sizeof(struct si_sm_data); } -struct si_sm_handlers smic_smi_handlers = { +const struct si_sm_handlers smic_smi_handlers = { .init_data = init_smic_data, .start_transaction = start_smic_transaction, .get_result = smic_get_result, diff --git a/kernel/drivers/char/ipmi/ipmi_ssif.c b/kernel/drivers/char/ipmi/ipmi_ssif.c index 207689c44..90e624662 100644 --- a/kernel/drivers/char/ipmi/ipmi_ssif.c +++ b/kernel/drivers/char/ipmi/ipmi_ssif.c @@ -52,6 +52,7 @@ #include <linux/kthread.h> #include <linux/acpi.h> #include <linux/ctype.h> +#include <linux/time64.h> #define PFX "ipmi_ssif: " #define DEVICE_NAME "ipmi_ssif" @@ -1041,12 +1042,12 @@ static void sender(void *send_info, start_next_msg(ssif_info, flags); if (ssif_info->ssif_debug & SSIF_DEBUG_TIMING) { - struct timeval t; + struct timespec64 t; - do_gettimeofday(&t); - pr_info("**Enqueue %02x %02x: %ld.%6.6ld\n", + ktime_get_real_ts64(&t); + pr_info("**Enqueue %02x %02x: %lld.%6.6ld\n", msg->data[0], msg->data[1], - (long) t.tv_sec, (long) t.tv_usec); + (long long) t.tv_sec, (long) t.tv_nsec / NSEC_PER_USEC); } } @@ -1136,6 +1137,10 @@ module_param_array(slave_addrs, int, &num_slave_addrs, 0); MODULE_PARM_DESC(slave_addrs, "The default IPMB slave address for the controller."); +static bool alerts_broken; +module_param(alerts_broken, bool, 0); +MODULE_PARM_DESC(alerts_broken, "Don't enable alerts for the controller."); + /* * Bit 0 enables message debugging, bit 1 enables state debugging, and * bit 2 enables timing debugging. This is an array indexed by @@ -1154,11 +1159,11 @@ static int use_thread; module_param(use_thread, int, 0); MODULE_PARM_DESC(use_thread, "Use the thread interface."); -static bool ssif_tryacpi = 1; +static bool ssif_tryacpi = true; module_param_named(tryacpi, ssif_tryacpi, bool, 0); MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the default scan of the interfaces identified via ACPI"); -static bool ssif_trydmi = 1; +static bool ssif_trydmi = true; module_param_named(trydmi, ssif_trydmi, bool, 0); MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the default scan of the interfaces identified via DMI (SMBIOS)"); @@ -1582,6 +1587,10 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ssif_info->global_enables |= IPMI_BMC_EVT_MSG_BUFF; } + /* Some systems don't behave well if you enable alerts. */ + if (alerts_broken) + goto found; + msg[0] = IPMI_NETFN_APP_REQUEST << 2; msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; msg[2] = ssif_info->global_enables | IPMI_BMC_RCV_MSG_INTR; @@ -1787,7 +1796,7 @@ skip_addr: } #ifdef CONFIG_ACPI -static struct acpi_device_id ssif_acpi_match[] = { +static const struct acpi_device_id ssif_acpi_match[] = { { "IPI0001", 0 }, { }, }; diff --git a/kernel/drivers/char/ipmi/ipmi_watchdog.c b/kernel/drivers/char/ipmi/ipmi_watchdog.c index 37b8be7cb..096f0cef4 100644 --- a/kernel/drivers/char/ipmi/ipmi_watchdog.c +++ b/kernel/drivers/char/ipmi/ipmi_watchdog.c @@ -153,6 +153,9 @@ static int timeout = 10; /* The pre-timeout is disabled by default. */ static int pretimeout; +/* Default timeout to set on panic */ +static int panic_wdt_timeout = 255; + /* Default action is to reset the board on a timeout. */ static unsigned char action_val = WDOG_TIMEOUT_RESET; @@ -208,7 +211,7 @@ static int set_param_timeout(const char *val, const struct kernel_param *kp) return rv; } -static struct kernel_param_ops param_ops_timeout = { +static const struct kernel_param_ops param_ops_timeout = { .set = set_param_timeout, .get = param_get_int, }; @@ -270,14 +273,14 @@ static int set_param_wdog_ifnum(const char *val, const struct kernel_param *kp) return 0; } -static struct kernel_param_ops param_ops_wdog_ifnum = { +static const struct kernel_param_ops param_ops_wdog_ifnum = { .set = set_param_wdog_ifnum, .get = param_get_int, }; #define param_check_wdog_ifnum param_check_int -static struct kernel_param_ops param_ops_str = { +static const struct kernel_param_ops param_ops_str = { .set = set_param_str, .get = get_param_str, }; @@ -293,6 +296,9 @@ MODULE_PARM_DESC(timeout, "Timeout value in seconds."); module_param(pretimeout, timeout, 0644); MODULE_PARM_DESC(pretimeout, "Pretimeout value in seconds."); +module_param(panic_wdt_timeout, timeout, 0644); +MODULE_PARM_DESC(timeout, "Timeout value on kernel panic in seconds."); + module_param_cb(action, ¶m_ops_str, action_op, 0644); MODULE_PARM_DESC(action, "Timeout action. One of: " "reset, none, power_cycle, power_off."); @@ -1189,7 +1195,7 @@ static int wdog_panic_handler(struct notifier_block *this, /* Make sure we do this only once. */ panic_event_handled = 1; - timeout = 255; + timeout = panic_wdt_timeout; pretimeout = 0; panic_halt_ipmi_set_timeout(); } diff --git a/kernel/drivers/char/misc.c b/kernel/drivers/char/misc.c index 9fd5a91e0..8069b361b 100644 --- a/kernel/drivers/char/misc.c +++ b/kernel/drivers/char/misc.c @@ -117,14 +117,14 @@ static int misc_open(struct inode * inode, struct file * file) const struct file_operations *new_fops = NULL; mutex_lock(&misc_mtx); - + list_for_each_entry(c, &misc_list, list) { if (c->minor == minor) { - new_fops = fops_get(c->fops); + new_fops = fops_get(c->fops); break; } } - + if (!new_fops) { mutex_unlock(&misc_mtx); request_module("char-major-%d-%d", MISC_MAJOR, minor); @@ -167,7 +167,7 @@ static const struct file_operations misc_fops = { /** * misc_register - register a miscellaneous device * @misc: device structure - * + * * Register a miscellaneous device with the kernel. If the minor * number is set to %MISC_DYNAMIC_MINOR a minor number is assigned * and placed in the minor field of the structure. For other cases @@ -181,17 +181,18 @@ static const struct file_operations misc_fops = { * A zero is returned on success and a negative errno code for * failure. */ - + int misc_register(struct miscdevice * misc) { dev_t dev; int err = 0; + bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR); INIT_LIST_HEAD(&misc->list); mutex_lock(&misc_mtx); - if (misc->minor == MISC_DYNAMIC_MINOR) { + if (is_dynamic) { int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); if (i >= DYNAMIC_MINORS) { err = -EBUSY; @@ -216,9 +217,13 @@ int misc_register(struct miscdevice * misc) device_create_with_groups(misc_class, misc->parent, dev, misc, misc->groups, "%s", misc->name); if (IS_ERR(misc->this_device)) { - int i = DYNAMIC_MINORS - misc->minor - 1; - if (i < DYNAMIC_MINORS && i >= 0) - clear_bit(i, misc_minors); + if (is_dynamic) { + int i = DYNAMIC_MINORS - misc->minor - 1; + + if (i < DYNAMIC_MINORS && i >= 0) + clear_bit(i, misc_minors); + misc->minor = MISC_DYNAMIC_MINOR; + } err = PTR_ERR(misc->this_device); goto out; } @@ -238,17 +243,15 @@ int misc_register(struct miscdevice * misc) * @misc: device to unregister * * Unregister a miscellaneous device that was previously - * successfully registered with misc_register(). Success - * is indicated by a zero return, a negative errno code - * indicates an error. + * successfully registered with misc_register(). */ -int misc_deregister(struct miscdevice *misc) +void misc_deregister(struct miscdevice *misc) { int i = DYNAMIC_MINORS - misc->minor - 1; if (WARN_ON(list_empty(&misc->list))) - return -EINVAL; + return; mutex_lock(&misc_mtx); list_del(&misc->list); @@ -256,7 +259,6 @@ int misc_deregister(struct miscdevice *misc) if (i < DYNAMIC_MINORS && i >= 0) clear_bit(i, misc_minors); mutex_unlock(&misc_mtx); - return 0; } EXPORT_SYMBOL(misc_register); @@ -276,10 +278,9 @@ static char *misc_devnode(struct device *dev, umode_t *mode) static int __init misc_init(void) { int err; + struct proc_dir_entry *ret; -#ifdef CONFIG_PROC_FS - proc_create("misc", 0, NULL, &misc_proc_fops); -#endif + ret = proc_create("misc", 0, NULL, &misc_proc_fops); misc_class = class_create(THIS_MODULE, "misc"); err = PTR_ERR(misc_class); if (IS_ERR(misc_class)) @@ -295,7 +296,8 @@ fail_printk: printk("unable to get major %d for misc devices\n", MISC_MAJOR); class_destroy(misc_class); fail_remove: - remove_proc_entry("misc", NULL); + if (ret) + remove_proc_entry("misc", NULL); return err; } subsys_initcall(misc_init); diff --git a/kernel/drivers/char/msm_smd_pkt.c b/kernel/drivers/char/msm_smd_pkt.c deleted file mode 100644 index ba82a06d9..000000000 --- a/kernel/drivers/char/msm_smd_pkt.c +++ /dev/null @@ -1,465 +0,0 @@ -/* Copyright (c) 2008-2010, Code Aurora Forum. 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. - * - * 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. - * - */ -/* - * SMD Packet Driver -- Provides userspace interface to SMD packet ports. - */ - -#include <linux/slab.h> -#include <linux/cdev.h> -#include <linux/module.h> -#include <linux/fs.h> -#include <linux/device.h> -#include <linux/sched.h> -#include <linux/mutex.h> -#include <linux/delay.h> -#include <linux/uaccess.h> -#include <linux/workqueue.h> -#include <linux/poll.h> - -#include <mach/msm_smd.h> - -#define NUM_SMD_PKT_PORTS 9 -#define DEVICE_NAME "smdpkt" -#define MAX_BUF_SIZE 2048 - -struct smd_pkt_dev { - struct cdev cdev; - struct device *devicep; - - struct smd_channel *ch; - int open_count; - struct mutex ch_lock; - struct mutex rx_lock; - struct mutex tx_lock; - wait_queue_head_t ch_read_wait_queue; - wait_queue_head_t ch_opened_wait_queue; - - int i; - - unsigned char tx_buf[MAX_BUF_SIZE]; - unsigned char rx_buf[MAX_BUF_SIZE]; - int remote_open; - -} *smd_pkt_devp[NUM_SMD_PKT_PORTS]; - -struct class *smd_pkt_classp; -static dev_t smd_pkt_number; - -static int msm_smd_pkt_debug_enable; -module_param_named(debug_enable, msm_smd_pkt_debug_enable, - int, S_IRUGO | S_IWUSR | S_IWGRP); - -#ifdef DEBUG -#define D_DUMP_BUFFER(prestr, cnt, buf) do { \ - int i; \ - if (msm_smd_pkt_debug_enable) { \ - pr_debug("%s", prestr); \ - for (i = 0; i < cnt; i++) \ - pr_debug("%.2x", buf[i]); \ - pr_debug("\n"); \ - } \ - } while (0) -#else -#define D_DUMP_BUFFER(prestr, cnt, buf) do {} while (0) -#endif - -#ifdef DEBUG -#define DBG(x...) do { \ - if (msm_smd_pkt_debug_enable) \ - pr_debug(x); \ - } while (0) -#else -#define DBG(x...) do {} while (0) -#endif - -static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp) -{ - int sz; - - if (!smd_pkt_devp || !smd_pkt_devp->ch) - return; - - sz = smd_cur_packet_size(smd_pkt_devp->ch); - if (sz == 0) { - DBG("no packet\n"); - return; - } - if (sz > smd_read_avail(smd_pkt_devp->ch)) { - DBG("incomplete packet\n"); - return; - } - - DBG("waking up reader\n"); - wake_up_interruptible(&smd_pkt_devp->ch_read_wait_queue); -} - -static int smd_pkt_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - int r, bytes_read; - struct smd_pkt_dev *smd_pkt_devp; - struct smd_channel *chl; - - DBG("read %d bytes\n", count); - if (count > MAX_BUF_SIZE) - return -EINVAL; - - smd_pkt_devp = file->private_data; - if (!smd_pkt_devp || !smd_pkt_devp->ch) - return -EINVAL; - - chl = smd_pkt_devp->ch; -wait_for_packet: - r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue, - (smd_cur_packet_size(chl) > 0 && - smd_read_avail(chl) >= - smd_cur_packet_size(chl))); - - if (r < 0) { - if (r != -ERESTARTSYS) - pr_err("wait returned %d\n", r); - return r; - } - - mutex_lock(&smd_pkt_devp->rx_lock); - - bytes_read = smd_cur_packet_size(smd_pkt_devp->ch); - if (bytes_read == 0 || - bytes_read < smd_read_avail(smd_pkt_devp->ch)) { - mutex_unlock(&smd_pkt_devp->rx_lock); - DBG("Nothing to read\n"); - goto wait_for_packet; - } - - if (bytes_read > count) { - mutex_unlock(&smd_pkt_devp->rx_lock); - pr_info("packet size %d > buffer size %d", bytes_read, count); - return -EINVAL; - } - - r = smd_read(smd_pkt_devp->ch, smd_pkt_devp->rx_buf, bytes_read); - if (r != bytes_read) { - mutex_unlock(&smd_pkt_devp->rx_lock); - pr_err("smd_read failed to read %d bytes: %d\n", bytes_read, r); - return -EIO; - } - - D_DUMP_BUFFER("read: ", bytes_read, smd_pkt_devp->rx_buf); - r = copy_to_user(buf, smd_pkt_devp->rx_buf, bytes_read); - mutex_unlock(&smd_pkt_devp->rx_lock); - if (r) { - pr_err("copy_to_user failed %d\n", r); - return -EFAULT; - } - - DBG("read complete %d bytes\n", bytes_read); - check_and_wakeup_reader(smd_pkt_devp); - - return bytes_read; -} - -static int smd_pkt_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - int r; - struct smd_pkt_dev *smd_pkt_devp; - - if (count > MAX_BUF_SIZE) - return -EINVAL; - - DBG("writing %d bytes\n", count); - - smd_pkt_devp = file->private_data; - if (!smd_pkt_devp || !smd_pkt_devp->ch) - return -EINVAL; - - mutex_lock(&smd_pkt_devp->tx_lock); - if (smd_write_avail(smd_pkt_devp->ch) < count) { - mutex_unlock(&smd_pkt_devp->tx_lock); - DBG("Not enough space to write\n"); - return -ENOMEM; - } - - D_DUMP_BUFFER("write: ", count, buf); - r = copy_from_user(smd_pkt_devp->tx_buf, buf, count); - if (r) { - mutex_unlock(&smd_pkt_devp->tx_lock); - pr_err("copy_from_user failed %d\n", r); - return -EFAULT; - } - - r = smd_write(smd_pkt_devp->ch, smd_pkt_devp->tx_buf, count); - if (r != count) { - mutex_unlock(&smd_pkt_devp->tx_lock); - pr_err("smd_write failed to write %d bytes: %d.\n", count, r); - return -EIO; - } - mutex_unlock(&smd_pkt_devp->tx_lock); - - DBG("wrote %d bytes\n", count); - return count; -} - -static unsigned int smd_pkt_poll(struct file *file, poll_table *wait) -{ - struct smd_pkt_dev *smd_pkt_devp; - unsigned int mask = 0; - - smd_pkt_devp = file->private_data; - if (!smd_pkt_devp) - return POLLERR; - - DBG("poll waiting\n"); - poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait); - if (smd_read_avail(smd_pkt_devp->ch)) - mask |= POLLIN | POLLRDNORM; - - DBG("poll return\n"); - return mask; -} - -static void smd_pkt_ch_notify(void *priv, unsigned event) -{ - struct smd_pkt_dev *smd_pkt_devp = priv; - - if (smd_pkt_devp->ch == 0) - return; - - switch (event) { - case SMD_EVENT_DATA: - DBG("data\n"); - check_and_wakeup_reader(smd_pkt_devp); - break; - - case SMD_EVENT_OPEN: - DBG("remote open\n"); - smd_pkt_devp->remote_open = 1; - wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue); - break; - - case SMD_EVENT_CLOSE: - smd_pkt_devp->remote_open = 0; - pr_info("remote closed\n"); - break; - - default: - pr_err("unknown event %d\n", event); - break; - } -} - -static char *smd_pkt_dev_name[] = { - "smdcntl0", - "smdcntl1", - "smdcntl2", - "smdcntl3", - "smdcntl4", - "smdcntl5", - "smdcntl6", - "smdcntl7", - "smd22", -}; - -static char *smd_ch_name[] = { - "DATA5_CNTL", - "DATA6_CNTL", - "DATA7_CNTL", - "DATA8_CNTL", - "DATA9_CNTL", - "DATA12_CNTL", - "DATA13_CNTL", - "DATA14_CNTL", - "DATA22", -}; - -static int smd_pkt_open(struct inode *inode, struct file *file) -{ - int r = 0; - struct smd_pkt_dev *smd_pkt_devp; - - smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev); - if (!smd_pkt_devp) - return -EINVAL; - - file->private_data = smd_pkt_devp; - - mutex_lock(&smd_pkt_devp->ch_lock); - if (smd_pkt_devp->open_count == 0) { - r = smd_open(smd_ch_name[smd_pkt_devp->i], - &smd_pkt_devp->ch, smd_pkt_devp, - smd_pkt_ch_notify); - if (r < 0) { - pr_err("smd_open failed for %s, %d\n", - smd_ch_name[smd_pkt_devp->i], r); - goto out; - } - - r = wait_event_interruptible_timeout( - smd_pkt_devp->ch_opened_wait_queue, - smd_pkt_devp->remote_open, - msecs_to_jiffies(2 * HZ)); - if (r == 0) - r = -ETIMEDOUT; - - if (r < 0) { - pr_err("wait returned %d\n", r); - smd_close(smd_pkt_devp->ch); - smd_pkt_devp->ch = 0; - } else { - smd_pkt_devp->open_count++; - r = 0; - } - } -out: - mutex_unlock(&smd_pkt_devp->ch_lock); - return r; -} - -static int smd_pkt_release(struct inode *inode, struct file *file) -{ - int r = 0; - struct smd_pkt_dev *smd_pkt_devp = file->private_data; - - if (!smd_pkt_devp) - return -EINVAL; - - mutex_lock(&smd_pkt_devp->ch_lock); - if (--smd_pkt_devp->open_count == 0) { - r = smd_close(smd_pkt_devp->ch); - smd_pkt_devp->ch = 0; - } - mutex_unlock(&smd_pkt_devp->ch_lock); - - return r; -} - -static const struct file_operations smd_pkt_fops = { - .owner = THIS_MODULE, - .open = smd_pkt_open, - .release = smd_pkt_release, - .read = smd_pkt_read, - .write = smd_pkt_write, - .poll = smd_pkt_poll, -}; - -static int __init smd_pkt_init(void) -{ - int i; - int r; - - r = alloc_chrdev_region(&smd_pkt_number, 0, - NUM_SMD_PKT_PORTS, DEVICE_NAME); - if (r) { - pr_err("alloc_chrdev_region() failed %d\n", r); - return r; - } - - smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME); - if (IS_ERR(smd_pkt_classp)) { - r = PTR_ERR(smd_pkt_classp); - pr_err("class_create() failed %d\n", r); - goto unreg_chardev; - } - - for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) { - smd_pkt_devp[i] = kzalloc(sizeof(struct smd_pkt_dev), - GFP_KERNEL); - if (!smd_pkt_devp[i]) { - pr_err("kmalloc() failed\n"); - goto clean_cdevs; - } - - smd_pkt_devp[i]->i = i; - - init_waitqueue_head(&smd_pkt_devp[i]->ch_read_wait_queue); - smd_pkt_devp[i]->remote_open = 0; - init_waitqueue_head(&smd_pkt_devp[i]->ch_opened_wait_queue); - - mutex_init(&smd_pkt_devp[i]->ch_lock); - mutex_init(&smd_pkt_devp[i]->rx_lock); - mutex_init(&smd_pkt_devp[i]->tx_lock); - - cdev_init(&smd_pkt_devp[i]->cdev, &smd_pkt_fops); - smd_pkt_devp[i]->cdev.owner = THIS_MODULE; - - r = cdev_add(&smd_pkt_devp[i]->cdev, - (smd_pkt_number + i), 1); - if (r) { - pr_err("cdev_add() failed %d\n", r); - kfree(smd_pkt_devp[i]); - goto clean_cdevs; - } - - smd_pkt_devp[i]->devicep = - device_create(smd_pkt_classp, NULL, - (smd_pkt_number + i), NULL, - smd_pkt_dev_name[i]); - if (IS_ERR(smd_pkt_devp[i]->devicep)) { - r = PTR_ERR(smd_pkt_devp[i]->devicep); - pr_err("device_create() failed %d\n", r); - cdev_del(&smd_pkt_devp[i]->cdev); - kfree(smd_pkt_devp[i]); - goto clean_cdevs; - } - - } - - pr_info("SMD Packet Port Driver Initialized.\n"); - return 0; - -clean_cdevs: - if (i > 0) { - while (--i >= 0) { - mutex_destroy(&smd_pkt_devp[i]->ch_lock); - mutex_destroy(&smd_pkt_devp[i]->rx_lock); - mutex_destroy(&smd_pkt_devp[i]->tx_lock); - cdev_del(&smd_pkt_devp[i]->cdev); - kfree(smd_pkt_devp[i]); - device_destroy(smd_pkt_classp, - MKDEV(MAJOR(smd_pkt_number), i)); - } - } - - class_destroy(smd_pkt_classp); -unreg_chardev: - unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS); - return r; -} -module_init(smd_pkt_init); - -static void __exit smd_pkt_cleanup(void) -{ - int i; - - for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) { - mutex_destroy(&smd_pkt_devp[i]->ch_lock); - mutex_destroy(&smd_pkt_devp[i]->rx_lock); - mutex_destroy(&smd_pkt_devp[i]->tx_lock); - cdev_del(&smd_pkt_devp[i]->cdev); - kfree(smd_pkt_devp[i]); - device_destroy(smd_pkt_classp, - MKDEV(MAJOR(smd_pkt_number), i)); - } - - class_destroy(smd_pkt_classp); - unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS); -} -module_exit(smd_pkt_cleanup); - -MODULE_DESCRIPTION("MSM Shared Memory Packet Port"); -MODULE_LICENSE("GPL v2"); diff --git a/kernel/drivers/char/nvram.c b/kernel/drivers/char/nvram.c index 9df78e2cc..97c2d8d43 100644 --- a/kernel/drivers/char/nvram.c +++ b/kernel/drivers/char/nvram.c @@ -702,7 +702,7 @@ static void atari_proc_infos(unsigned char *nvram, struct seq_file *seq, seq_printf(seq, "%ds%s\n", nvram[10], nvram[10] < 8 ? ", no memory test" : ""); - vmode = (nvram[14] << 8) || nvram[15]; + vmode = (nvram[14] << 8) | nvram[15]; seq_printf(seq, "Video mode : %s colors, %d columns, %s %s monitor\n", colors[vmode & 7], diff --git a/kernel/drivers/char/pcmcia/cm4040_cs.c b/kernel/drivers/char/pcmcia/cm4040_cs.c index 8dd48a2be..fc061f7c2 100644 --- a/kernel/drivers/char/pcmcia/cm4040_cs.c +++ b/kernel/drivers/char/pcmcia/cm4040_cs.c @@ -532,9 +532,8 @@ static int reader_config(struct pcmcia_device *link, int devno) fail_rc = pcmcia_enable_device(link); if (fail_rc != 0) { - dev_printk(KERN_INFO, &link->dev, - "pcmcia_enable_device failed 0x%x\n", - fail_rc); + dev_info(&link->dev, "pcmcia_enable_device failed 0x%x\n", + fail_rc); goto cs_release; } diff --git a/kernel/drivers/char/pcmcia/synclink_cs.c b/kernel/drivers/char/pcmcia/synclink_cs.c index 0ea998605..45df4bf91 100644 --- a/kernel/drivers/char/pcmcia/synclink_cs.c +++ b/kernel/drivers/char/pcmcia/synclink_cs.c @@ -437,7 +437,7 @@ static int mgslpc_device_count = 0; * .text section address and breakpoint on module load. * This is useful for use with gdb and add-symbol-file command. */ -static bool break_on_load=0; +static bool break_on_load; /* * Driver major number, defaults to zero to get auto @@ -2507,15 +2507,6 @@ static int mgslpc_open(struct tty_struct *tty, struct file * filp) printk("%s(%d):mgslpc_open(%s), old ref count = %d\n", __FILE__, __LINE__, tty->driver->name, port->count); - /* If port is closing, signal caller to try again */ - if (port->flags & ASYNC_CLOSING){ - wait_event_interruptible_tty(tty, port->close_wait, - !(port->flags & ASYNC_CLOSING)); - retval = ((port->flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); - goto cleanup; - } - port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; spin_lock_irqsave(&info->netlock, flags); diff --git a/kernel/drivers/char/random.c b/kernel/drivers/char/random.c index eb47efec2..29abd5fa5 100644 --- a/kernel/drivers/char/random.c +++ b/kernel/drivers/char/random.c @@ -409,6 +409,9 @@ static DECLARE_WAIT_QUEUE_HEAD(random_write_wait); static DECLARE_WAIT_QUEUE_HEAD(urandom_init_wait); static struct fasync_struct *fasync; +static DEFINE_SPINLOCK(random_ready_list_lock); +static LIST_HEAD(random_ready_list); + /********************************************************************** * * OS independent entropy store. Here are the functions which handle @@ -589,6 +592,22 @@ static void fast_mix(struct fast_pool *f) f->count++; } +static void process_random_ready_list(void) +{ + unsigned long flags; + struct random_ready_callback *rdy, *tmp; + + spin_lock_irqsave(&random_ready_list_lock, flags); + list_for_each_entry_safe(rdy, tmp, &random_ready_list, list) { + struct module *owner = rdy->owner; + + list_del_init(&rdy->list); + rdy->func(rdy); + module_put(owner); + } + spin_unlock_irqrestore(&random_ready_list_lock, flags); +} + /* * Credit (or debit) the entropy store with n bits of entropy. * Use credit_entropy_bits_safe() if the value comes from userspace @@ -660,7 +679,8 @@ retry: r->entropy_total = 0; if (r == &nonblocking_pool) { prandom_reseed_late(); - wake_up_interruptible(&urandom_init_wait); + process_random_ready_list(); + wake_up_all(&urandom_init_wait); pr_notice("random: %s pool is initialized\n", r->name); } } @@ -1241,6 +1261,64 @@ void get_random_bytes(void *buf, int nbytes) EXPORT_SYMBOL(get_random_bytes); /* + * Add a callback function that will be invoked when the nonblocking + * pool is initialised. + * + * returns: 0 if callback is successfully added + * -EALREADY if pool is already initialised (callback not called) + * -ENOENT if module for callback is not alive + */ +int add_random_ready_callback(struct random_ready_callback *rdy) +{ + struct module *owner; + unsigned long flags; + int err = -EALREADY; + + if (likely(nonblocking_pool.initialized)) + return err; + + owner = rdy->owner; + if (!try_module_get(owner)) + return -ENOENT; + + spin_lock_irqsave(&random_ready_list_lock, flags); + if (nonblocking_pool.initialized) + goto out; + + owner = NULL; + + list_add(&rdy->list, &random_ready_list); + err = 0; + +out: + spin_unlock_irqrestore(&random_ready_list_lock, flags); + + module_put(owner); + + return err; +} +EXPORT_SYMBOL(add_random_ready_callback); + +/* + * Delete a previously registered readiness callback function. + */ +void del_random_ready_callback(struct random_ready_callback *rdy) +{ + unsigned long flags; + struct module *owner = NULL; + + spin_lock_irqsave(&random_ready_list_lock, flags); + if (!list_empty(&rdy->list)) { + list_del_init(&rdy->list); + owner = rdy->owner; + } + spin_unlock_irqrestore(&random_ready_list_lock, flags); + + module_put(owner); +} +EXPORT_SYMBOL(del_random_ready_callback); + +/* * 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 diff --git a/kernel/drivers/char/raw.c b/kernel/drivers/char/raw.c index 5fc291c61..60316fbaf 100644 --- a/kernel/drivers/char/raw.c +++ b/kernel/drivers/char/raw.c @@ -12,6 +12,7 @@ #include <linux/fs.h> #include <linux/major.h> #include <linux/blkdev.h> +#include <linux/backing-dev.h> #include <linux/module.h> #include <linux/raw.h> #include <linux/capability.h> diff --git a/kernel/drivers/char/snsc.c b/kernel/drivers/char/snsc.c index 8bab59292..94006f9c2 100644 --- a/kernel/drivers/char/snsc.c +++ b/kernel/drivers/char/snsc.c @@ -19,7 +19,7 @@ #include <linux/sched.h> #include <linux/device.h> #include <linux/poll.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/slab.h> #include <linux/mutex.h> #include <asm/sn/io.h> @@ -198,7 +198,7 @@ scdrv_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos) add_wait_queue(&sd->sd_rq, &wait); spin_unlock_irqrestore(&sd->sd_rlock, flags); - schedule_timeout(SCDRV_TIMEOUT); + schedule_timeout(msecs_to_jiffies(SCDRV_TIMEOUT)); remove_wait_queue(&sd->sd_rq, &wait); if (signal_pending(current)) { @@ -294,7 +294,7 @@ scdrv_write(struct file *file, const char __user *buf, add_wait_queue(&sd->sd_wq, &wait); spin_unlock_irqrestore(&sd->sd_wlock, flags); - schedule_timeout(SCDRV_TIMEOUT); + schedule_timeout(msecs_to_jiffies(SCDRV_TIMEOUT)); remove_wait_queue(&sd->sd_wq, &wait); if (signal_pending(current)) { @@ -461,5 +461,4 @@ scdrv_init(void) } return 0; } - -module_init(scdrv_init); +device_initcall(scdrv_init); diff --git a/kernel/drivers/char/toshiba.c b/kernel/drivers/char/toshiba.c index 014c9d90d..f5a45d887 100644 --- a/kernel/drivers/char/toshiba.c +++ b/kernel/drivers/char/toshiba.c @@ -430,7 +430,7 @@ static int tosh_probe(void) int i,major,minor,day,year,month,flag; unsigned char signature[7] = { 0x54,0x4f,0x53,0x48,0x49,0x42,0x41 }; SMMRegisters regs; - void __iomem *bios = ioremap_cache(0xf0000, 0x10000); + void __iomem *bios = ioremap(0xf0000, 0x10000); if (!bios) return -ENOMEM; diff --git a/kernel/drivers/char/tpm/st33zp24/Kconfig b/kernel/drivers/char/tpm/st33zp24/Kconfig index 09cb72786..19c007461 100644 --- a/kernel/drivers/char/tpm/st33zp24/Kconfig +++ b/kernel/drivers/char/tpm/st33zp24/Kconfig @@ -1,6 +1,6 @@ config TCG_TIS_ST33ZP24 tristate "STMicroelectronics TPM Interface Specification 1.2 Interface" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST ---help--- STMicroelectronics ST33ZP24 core driver. It implements the core TPM1.2 logic and hooks into the TPM kernel APIs. Physical layers will diff --git a/kernel/drivers/char/tpm/st33zp24/i2c.c b/kernel/drivers/char/tpm/st33zp24/i2c.c index ad1ee180e..309d2767c 100644 --- a/kernel/drivers/char/tpm/st33zp24/i2c.c +++ b/kernel/drivers/char/tpm/st33zp24/i2c.c @@ -258,7 +258,6 @@ static SIMPLE_DEV_PM_OPS(st33zp24_i2c_ops, st33zp24_pm_suspend, static struct i2c_driver st33zp24_i2c_driver = { .driver = { - .owner = THIS_MODULE, .name = TPM_ST33_I2C, .pm = &st33zp24_i2c_ops, .of_match_table = of_match_ptr(of_st33zp24_i2c_match), diff --git a/kernel/drivers/char/tpm/st33zp24/spi.c b/kernel/drivers/char/tpm/st33zp24/spi.c index f0184a1b0..f974c945c 100644 --- a/kernel/drivers/char/tpm/st33zp24/spi.c +++ b/kernel/drivers/char/tpm/st33zp24/spi.c @@ -381,7 +381,6 @@ static SIMPLE_DEV_PM_OPS(st33zp24_spi_ops, st33zp24_pm_suspend, static struct spi_driver tpm_st33_spi_driver = { .driver = { - .owner = THIS_MODULE, .name = TPM_ST33_SPI, .pm = &st33zp24_spi_ops, .of_match_table = of_match_ptr(of_st33zp24_spi_match), diff --git a/kernel/drivers/char/tpm/tpm-chip.c b/kernel/drivers/char/tpm/tpm-chip.c index 1082d4bb0..45cc39aab 100644 --- a/kernel/drivers/char/tpm/tpm-chip.c +++ b/kernel/drivers/char/tpm/tpm-chip.c @@ -119,6 +119,9 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev, chip->dev.class = tpm_class; chip->dev.release = tpm_dev_release; chip->dev.parent = chip->pdev; +#ifdef CONFIG_ACPI + chip->dev.groups = chip->groups; +#endif if (chip->dev_num == 0) chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR); @@ -182,12 +185,6 @@ static int tpm1_chip_register(struct tpm_chip *chip) if (rc) return rc; - rc = tpm_add_ppi(chip); - if (rc) { - tpm_sysfs_del_device(chip); - return rc; - } - chip->bios_dir = tpm_bios_log_setup(chip->devname); return 0; @@ -201,8 +198,6 @@ static void tpm1_chip_unregister(struct tpm_chip *chip) if (chip->bios_dir) tpm_bios_log_teardown(chip->bios_dir); - tpm_remove_ppi(chip); - tpm_sysfs_del_device(chip); } @@ -225,17 +220,29 @@ int tpm_chip_register(struct tpm_chip *chip) if (rc) return rc; + tpm_add_ppi(chip); + rc = tpm_dev_add_device(chip); if (rc) goto out_err; /* Make the chip available. */ spin_lock(&driver_lock); - list_add_rcu(&chip->list, &tpm_chip_list); + list_add_tail_rcu(&chip->list, &tpm_chip_list); spin_unlock(&driver_lock); chip->flags |= TPM_CHIP_FLAG_REGISTERED; + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { + rc = __compat_only_sysfs_link_entry_to_kobj(&chip->pdev->kobj, + &chip->dev.kobj, + "ppi"); + if (rc && rc != -ENOENT) { + tpm_chip_unregister(chip); + return rc; + } + } + return 0; out_err: tpm1_chip_unregister(chip); @@ -263,6 +270,9 @@ void tpm_chip_unregister(struct tpm_chip *chip) spin_unlock(&driver_lock); synchronize_rcu(); + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) + sysfs_remove_link(&chip->pdev->kobj, "ppi"); + tpm1_chip_unregister(chip); tpm_dev_del_device(chip); } diff --git a/kernel/drivers/char/tpm/tpm-interface.c b/kernel/drivers/char/tpm/tpm-interface.c index e85d3416d..c50637db3 100644 --- a/kernel/drivers/char/tpm/tpm-interface.c +++ b/kernel/drivers/char/tpm/tpm-interface.c @@ -666,6 +666,30 @@ int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) } /** + * tpm_is_tpm2 - is the chip a TPM2 chip? + * @chip_num: tpm idx # or ANY + * + * Returns < 0 on error, and 1 or 0 on success depending whether the chip + * is a TPM2 chip. + */ +int tpm_is_tpm2(u32 chip_num) +{ + struct tpm_chip *chip; + int rc; + + chip = tpm_chip_find_get(chip_num); + if (chip == NULL) + return -ENODEV; + + rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0; + + tpm_chip_put(chip); + + return rc; +} +EXPORT_SYMBOL_GPL(tpm_is_tpm2); + +/** * tpm_pcr_read - read a pcr value * @chip_num: tpm idx # or ANY * @pcr_idx: pcr idx to retrieve @@ -1021,6 +1045,58 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) } EXPORT_SYMBOL_GPL(tpm_get_random); +/** + * tpm_seal_trusted() - seal a trusted key + * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @options: authentication values and other options + * @payload: the key data in clear and encrypted form + * + * Returns < 0 on error and 0 on success. At the moment, only TPM 2.0 chips + * are supported. + */ +int tpm_seal_trusted(u32 chip_num, struct trusted_key_payload *payload, + struct trusted_key_options *options) +{ + struct tpm_chip *chip; + int rc; + + chip = tpm_chip_find_get(chip_num); + if (chip == NULL || !(chip->flags & TPM_CHIP_FLAG_TPM2)) + return -ENODEV; + + rc = tpm2_seal_trusted(chip, payload, options); + + tpm_chip_put(chip); + return rc; +} +EXPORT_SYMBOL_GPL(tpm_seal_trusted); + +/** + * tpm_unseal_trusted() - unseal a trusted key + * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @options: authentication values and other options + * @payload: the key data in clear and encrypted form + * + * Returns < 0 on error and 0 on success. At the moment, only TPM 2.0 chips + * are supported. + */ +int tpm_unseal_trusted(u32 chip_num, struct trusted_key_payload *payload, + struct trusted_key_options *options) +{ + struct tpm_chip *chip; + int rc; + + chip = tpm_chip_find_get(chip_num); + if (chip == NULL || !(chip->flags & TPM_CHIP_FLAG_TPM2)) + return -ENODEV; + + rc = tpm2_unseal_trusted(chip, payload, options); + + tpm_chip_put(chip); + return rc; +} +EXPORT_SYMBOL_GPL(tpm_unseal_trusted); + static int __init tpm_init(void) { int rc; diff --git a/kernel/drivers/char/tpm/tpm.h b/kernel/drivers/char/tpm/tpm.h index f8319a086..a4257a329 100644 --- a/kernel/drivers/char/tpm/tpm.h +++ b/kernel/drivers/char/tpm/tpm.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2004 IBM Corporation + * Copyright (C) 2015 Intel Corporation * * Authors: * Leendert van Doorn <leendert@watson.ibm.com> @@ -28,6 +29,7 @@ #include <linux/tpm.h> #include <linux/acpi.h> #include <linux/cdev.h> +#include <linux/highmem.h> enum tpm_const { TPM_MINOR = 224, /* officially assigned */ @@ -88,6 +90,9 @@ enum tpm2_return_codes { enum tpm2_algorithms { TPM2_ALG_SHA1 = 0x0004, + TPM2_ALG_KEYEDHASH = 0x0008, + TPM2_ALG_SHA256 = 0x000B, + TPM2_ALG_NULL = 0x0010 }; enum tpm2_command_codes { @@ -95,6 +100,10 @@ enum tpm2_command_codes { TPM2_CC_SELF_TEST = 0x0143, TPM2_CC_STARTUP = 0x0144, TPM2_CC_SHUTDOWN = 0x0145, + TPM2_CC_CREATE = 0x0153, + TPM2_CC_LOAD = 0x0157, + TPM2_CC_UNSEAL = 0x015E, + TPM2_CC_FLUSH_CONTEXT = 0x0165, TPM2_CC_GET_CAPABILITY = 0x017A, TPM2_CC_GET_RANDOM = 0x017B, TPM2_CC_PCR_READ = 0x017E, @@ -115,6 +124,13 @@ enum tpm2_startup_types { TPM2_SU_STATE = 0x0001, }; +enum tpm2_start_method { + TPM2_START_ACPI = 2, + TPM2_START_FIFO = 6, + TPM2_START_CRB = 7, + TPM2_START_CRB_WITH_ACPI = 8, +}; + struct tpm_chip; struct tpm_vendor_specific { @@ -151,8 +167,7 @@ struct tpm_vendor_specific { enum tpm_chip_flags { TPM_CHIP_FLAG_REGISTERED = BIT(0), - TPM_CHIP_FLAG_PPI = BIT(1), - TPM_CHIP_FLAG_TPM2 = BIT(2), + TPM_CHIP_FLAG_TPM2 = BIT(1), }; struct tpm_chip { @@ -175,6 +190,8 @@ struct tpm_chip { struct dentry **bios_dir; #ifdef CONFIG_ACPI + const struct attribute_group *groups[2]; + unsigned int groups_cnt; acpi_handle acpi_dev_handle; char ppi_version[TPM_PPI_VERSION_LEN + 1]; #endif /* CONFIG_ACPI */ @@ -182,7 +199,7 @@ struct tpm_chip { struct list_head list; }; -#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor) +#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) static inline void tpm_chip_put(struct tpm_chip *chip) { @@ -382,6 +399,101 @@ struct tpm_cmd_t { tpm_cmd_params params; } __packed; +/* A string buffer type for constructing TPM commands. This is based on the + * ideas of string buffer code in security/keys/trusted.h but is heap based + * in order to keep the stack usage minimal. + */ + +enum tpm_buf_flags { + TPM_BUF_OVERFLOW = BIT(0), +}; + +struct tpm_buf { + struct page *data_page; + unsigned int flags; + u8 *data; +}; + +static inline int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal) +{ + struct tpm_input_header *head; + + buf->data_page = alloc_page(GFP_HIGHUSER); + if (!buf->data_page) + return -ENOMEM; + + buf->flags = 0; + buf->data = kmap(buf->data_page); + + head = (struct tpm_input_header *) buf->data; + + head->tag = cpu_to_be16(tag); + head->length = cpu_to_be32(sizeof(*head)); + head->ordinal = cpu_to_be32(ordinal); + + return 0; +} + +static inline void tpm_buf_destroy(struct tpm_buf *buf) +{ + kunmap(buf->data_page); + __free_page(buf->data_page); +} + +static inline u32 tpm_buf_length(struct tpm_buf *buf) +{ + struct tpm_input_header *head = (struct tpm_input_header *) buf->data; + + return be32_to_cpu(head->length); +} + +static inline u16 tpm_buf_tag(struct tpm_buf *buf) +{ + struct tpm_input_header *head = (struct tpm_input_header *) buf->data; + + return be16_to_cpu(head->tag); +} + +static inline void tpm_buf_append(struct tpm_buf *buf, + const unsigned char *new_data, + unsigned int new_len) +{ + struct tpm_input_header *head = (struct tpm_input_header *) buf->data; + u32 len = tpm_buf_length(buf); + + /* Return silently if overflow has already happened. */ + if (buf->flags & TPM_BUF_OVERFLOW) + return; + + if ((len + new_len) > PAGE_SIZE) { + WARN(1, "tpm_buf: overflow\n"); + buf->flags |= TPM_BUF_OVERFLOW; + return; + } + + memcpy(&buf->data[len], new_data, new_len); + head->length = cpu_to_be32(len + new_len); +} + +static inline void tpm_buf_append_u8(struct tpm_buf *buf, const u8 value) +{ + tpm_buf_append(buf, &value, 1); +} + +static inline void tpm_buf_append_u16(struct tpm_buf *buf, const u16 value) +{ + __be16 value2 = cpu_to_be16(value); + + tpm_buf_append(buf, (u8 *) &value2, 2); +} + +static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value) +{ + __be32 value2 = cpu_to_be32(value); + + tpm_buf_append(buf, (u8 *) &value2, 4); +} + extern struct class *tpm_class; extern dev_t tpm_devt; extern const struct file_operations tpm_fops; @@ -412,15 +524,9 @@ void tpm_sysfs_del_device(struct tpm_chip *chip); int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf); #ifdef CONFIG_ACPI -extern int tpm_add_ppi(struct tpm_chip *chip); -extern void tpm_remove_ppi(struct tpm_chip *chip); +extern void tpm_add_ppi(struct tpm_chip *chip); #else -static inline int tpm_add_ppi(struct tpm_chip *chip) -{ - return 0; -} - -static inline void tpm_remove_ppi(struct tpm_chip *chip) +static inline void tpm_add_ppi(struct tpm_chip *chip) { } #endif @@ -428,6 +534,12 @@ static inline void tpm_remove_ppi(struct tpm_chip *chip) int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf); int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash); int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max); +int tpm2_seal_trusted(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options); +int tpm2_unseal_trusted(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options); ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, const char *desc); diff --git a/kernel/drivers/char/tpm/tpm2-cmd.c b/kernel/drivers/char/tpm/tpm2-cmd.c index 011909a9b..c12130485 100644 --- a/kernel/drivers/char/tpm/tpm2-cmd.c +++ b/kernel/drivers/char/tpm/tpm2-cmd.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Intel Corporation + * Copyright (C) 2014, 2015 Intel Corporation * * Authors: * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> @@ -16,6 +16,11 @@ */ #include "tpm.h" +#include <keys/trusted-type.h> + +enum tpm2_object_attributes { + TPM2_ATTR_USER_WITH_AUTH = BIT(6), +}; struct tpm2_startup_in { __be16 startup_type; @@ -381,6 +386,254 @@ static const struct tpm_input_header tpm2_get_tpm_pt_header = { }; /** + * Append TPMS_AUTH_COMMAND to the buffer. The buffer must be allocated with + * tpm_buf_alloc(). + * + * @param buf: an allocated tpm_buf instance + * @param nonce: the session nonce, may be NULL if not used + * @param nonce_len: the session nonce length, may be 0 if not used + * @param attributes: the session attributes + * @param hmac: the session HMAC or password, may be NULL if not used + * @param hmac_len: the session HMAC or password length, maybe 0 if not used + */ +static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle, + const u8 *nonce, u16 nonce_len, + u8 attributes, + const u8 *hmac, u16 hmac_len) +{ + tpm_buf_append_u32(buf, 9 + nonce_len + hmac_len); + tpm_buf_append_u32(buf, session_handle); + tpm_buf_append_u16(buf, nonce_len); + + if (nonce && nonce_len) + tpm_buf_append(buf, nonce, nonce_len); + + tpm_buf_append_u8(buf, attributes); + tpm_buf_append_u16(buf, hmac_len); + + if (hmac && hmac_len) + tpm_buf_append(buf, hmac, hmac_len); +} + +/** + * tpm2_seal_trusted() - seal a trusted key + * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @options: authentication values and other options + * @payload: the key data in clear and encrypted form + * + * Returns < 0 on error and 0 on success. + */ +int tpm2_seal_trusted(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options) +{ + unsigned int blob_len; + struct tpm_buf buf; + int rc; + + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); + if (rc) + return rc; + + tpm_buf_append_u32(&buf, options->keyhandle); + tpm2_buf_append_auth(&buf, TPM2_RS_PW, + NULL /* nonce */, 0, + 0 /* session_attributes */, + options->keyauth /* hmac */, + TPM_DIGEST_SIZE); + + /* sensitive */ + tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len + 1); + + tpm_buf_append_u16(&buf, TPM_DIGEST_SIZE); + tpm_buf_append(&buf, options->blobauth, TPM_DIGEST_SIZE); + tpm_buf_append_u16(&buf, payload->key_len + 1); + tpm_buf_append(&buf, payload->key, payload->key_len); + tpm_buf_append_u8(&buf, payload->migratable); + + /* public */ + tpm_buf_append_u16(&buf, 14); + + tpm_buf_append_u16(&buf, TPM2_ALG_KEYEDHASH); + tpm_buf_append_u16(&buf, TPM2_ALG_SHA256); + tpm_buf_append_u32(&buf, TPM2_ATTR_USER_WITH_AUTH); + tpm_buf_append_u16(&buf, 0); /* policy digest size */ + tpm_buf_append_u16(&buf, TPM2_ALG_NULL); + tpm_buf_append_u16(&buf, 0); + + /* outside info */ + tpm_buf_append_u16(&buf, 0); + + /* creation PCR */ + tpm_buf_append_u32(&buf, 0); + + if (buf.flags & TPM_BUF_OVERFLOW) { + rc = -E2BIG; + goto out; + } + + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "sealing data"); + if (rc) + goto out; + + blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]); + if (blob_len > MAX_BLOB_SIZE) { + rc = -E2BIG; + goto out; + } + + memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len); + payload->blob_len = blob_len; + +out: + tpm_buf_destroy(&buf); + + if (rc > 0) + rc = -EPERM; + + return rc; +} + +static int tpm2_load(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options, + u32 *blob_handle) +{ + struct tpm_buf buf; + unsigned int private_len; + unsigned int public_len; + unsigned int blob_len; + int rc; + + private_len = be16_to_cpup((__be16 *) &payload->blob[0]); + if (private_len > (payload->blob_len - 2)) + return -E2BIG; + + public_len = be16_to_cpup((__be16 *) &payload->blob[2 + private_len]); + blob_len = private_len + public_len + 4; + if (blob_len > payload->blob_len) + return -E2BIG; + + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); + if (rc) + return rc; + + tpm_buf_append_u32(&buf, options->keyhandle); + tpm2_buf_append_auth(&buf, TPM2_RS_PW, + NULL /* nonce */, 0, + 0 /* session_attributes */, + options->keyauth /* hmac */, + TPM_DIGEST_SIZE); + + tpm_buf_append(&buf, payload->blob, blob_len); + + if (buf.flags & TPM_BUF_OVERFLOW) { + rc = -E2BIG; + goto out; + } + + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "loading blob"); + if (!rc) + *blob_handle = be32_to_cpup( + (__be32 *) &buf.data[TPM_HEADER_SIZE]); + +out: + tpm_buf_destroy(&buf); + + if (rc > 0) + rc = -EPERM; + + return rc; +} + +static void tpm2_flush_context(struct tpm_chip *chip, u32 handle) +{ + struct tpm_buf buf; + int rc; + + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT); + if (rc) { + dev_warn(chip->pdev, "0x%08x was not flushed, out of memory\n", + handle); + return; + } + + tpm_buf_append_u32(&buf, handle); + + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "flushing context"); + if (rc) + dev_warn(chip->pdev, "0x%08x was not flushed, rc=%d\n", handle, + rc); + + tpm_buf_destroy(&buf); +} + +static int tpm2_unseal(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options, + u32 blob_handle) +{ + struct tpm_buf buf; + u16 data_len; + u8 *data; + int rc; + + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL); + if (rc) + return rc; + + tpm_buf_append_u32(&buf, blob_handle); + tpm2_buf_append_auth(&buf, TPM2_RS_PW, + NULL /* nonce */, 0, + 0 /* session_attributes */, + options->blobauth /* hmac */, + TPM_DIGEST_SIZE); + + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "unsealing"); + if (rc > 0) + rc = -EPERM; + + if (!rc) { + data_len = be16_to_cpup( + (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]); + data = &buf.data[TPM_HEADER_SIZE + 6]; + + memcpy(payload->key, data, data_len - 1); + payload->key_len = data_len - 1; + payload->migratable = data[data_len - 1]; + } + + tpm_buf_destroy(&buf); + return rc; +} + +/** + * tpm_unseal_trusted() - unseal a trusted key + * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @options: authentication values and other options + * @payload: the key data in clear and encrypted form + * + * Returns < 0 on error and 0 on success. + */ +int tpm2_unseal_trusted(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options) +{ + u32 blob_handle; + int rc; + + rc = tpm2_load(chip, payload, options, &blob_handle); + if (rc) + return rc; + + rc = tpm2_unseal(chip, payload, options, blob_handle); + + tpm2_flush_context(chip, blob_handle); + + return rc; +} + +/** * tpm2_get_tpm_pt() - get value of a TPM_CAP_TPM_PROPERTIES type property * @chip: TPM chip to use. * @property_id: property ID. diff --git a/kernel/drivers/char/tpm/tpm_crb.c b/kernel/drivers/char/tpm/tpm_crb.c index 126732259..4bb9727c1 100644 --- a/kernel/drivers/char/tpm/tpm_crb.c +++ b/kernel/drivers/char/tpm/tpm_crb.c @@ -34,12 +34,6 @@ enum crb_defaults { CRB_ACPI_START_INDEX = 1, }; -enum crb_start_method { - CRB_SM_ACPI_START = 2, - CRB_SM_CRB = 7, - CRB_SM_CRB_WITH_ACPI_START = 8, -}; - struct acpi_tpm2 { struct acpi_table_header hdr; u16 platform_class; @@ -74,7 +68,8 @@ struct crb_control_area { u32 int_enable; u32 int_sts; u32 cmd_size; - u64 cmd_pa; + u32 cmd_pa_low; + u32 cmd_pa_high; u32 rsp_size; u64 rsp_pa; } __packed; @@ -220,12 +215,6 @@ static int crb_acpi_add(struct acpi_device *device) u64 pa; int rc; - chip = tpmm_chip_alloc(dev, &tpm_crb); - if (IS_ERR(chip)) - return PTR_ERR(chip); - - chip->flags = TPM_CHIP_FLAG_TPM2; - status = acpi_get_table(ACPI_SIG_TPM2, 1, (struct acpi_table_header **) &buf); if (ACPI_FAILURE(status)) { @@ -233,13 +222,15 @@ static int crb_acpi_add(struct acpi_device *device) return -ENODEV; } - /* At least some versions of AMI BIOS have a bug that TPM2 table has - * zero address for the control area and therefore we must fail. - */ - if (!buf->control_area_pa) { - dev_err(dev, "TPM2 ACPI table has a zero address for the control area\n"); - return -EINVAL; - } + /* Should the FIFO driver handle this? */ + if (buf->start_method == TPM2_START_FIFO) + return -ENODEV; + + chip = tpmm_chip_alloc(dev, &tpm_crb); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + chip->flags = TPM_CHIP_FLAG_TPM2; if (buf->hdr.length < sizeof(struct acpi_tpm2)) { dev_err(dev, "TPM2 ACPI table has wrong size"); @@ -259,11 +250,11 @@ static int crb_acpi_add(struct acpi_device *device) * report only ACPI start but in practice seems to require both * ACPI start and CRB start. */ - if (sm == CRB_SM_CRB || sm == CRB_SM_CRB_WITH_ACPI_START || + if (sm == TPM2_START_CRB || sm == TPM2_START_FIFO || !strcmp(acpi_device_hid(device), "MSFT0101")) priv->flags |= CRB_FL_CRB_START; - if (sm == CRB_SM_ACPI_START || sm == CRB_SM_CRB_WITH_ACPI_START) + if (sm == TPM2_START_ACPI || sm == TPM2_START_CRB_WITH_ACPI) priv->flags |= CRB_FL_ACPI_START; priv->cca = (struct crb_control_area __iomem *) @@ -273,8 +264,8 @@ static int crb_acpi_add(struct acpi_device *device) return -ENOMEM; } - memcpy_fromio(&pa, &priv->cca->cmd_pa, 8); - pa = le64_to_cpu(pa); + pa = ((u64) le32_to_cpu(ioread32(&priv->cca->cmd_pa_high)) << 32) | + (u64) le32_to_cpu(ioread32(&priv->cca->cmd_pa_low)); priv->cmd = devm_ioremap_nocache(dev, pa, ioread32(&priv->cca->cmd_size)); if (!priv->cmd) { diff --git a/kernel/drivers/char/tpm/tpm_eventlog.c b/kernel/drivers/char/tpm/tpm_eventlog.c index 3a56a1315..bd72fb042 100644 --- a/kernel/drivers/char/tpm/tpm_eventlog.c +++ b/kernel/drivers/char/tpm/tpm_eventlog.c @@ -76,15 +76,25 @@ static void *tpm_bios_measurements_start(struct seq_file *m, loff_t *pos) void *addr = log->bios_event_log; void *limit = log->bios_event_log_end; struct tcpa_event *event; + u32 converted_event_size; + u32 converted_event_type; + /* read over *pos measurements */ for (i = 0; i < *pos; i++) { event = addr; + converted_event_size = + do_endian_conversion(event->event_size); + converted_event_type = + do_endian_conversion(event->event_type); + if ((addr + sizeof(struct tcpa_event)) < limit) { - if (event->event_type == 0 && event->event_size == 0) + if ((converted_event_type == 0) && + (converted_event_size == 0)) return NULL; - addr += sizeof(struct tcpa_event) + event->event_size; + addr += (sizeof(struct tcpa_event) + + converted_event_size); } } @@ -94,8 +104,12 @@ static void *tpm_bios_measurements_start(struct seq_file *m, loff_t *pos) event = addr; - if ((event->event_type == 0 && event->event_size == 0) || - ((addr + sizeof(struct tcpa_event) + event->event_size) >= limit)) + converted_event_size = do_endian_conversion(event->event_size); + converted_event_type = do_endian_conversion(event->event_type); + + if (((converted_event_type == 0) && (converted_event_size == 0)) + || ((addr + sizeof(struct tcpa_event) + converted_event_size) + >= limit)) return NULL; return addr; @@ -107,8 +121,12 @@ static void *tpm_bios_measurements_next(struct seq_file *m, void *v, struct tcpa_event *event = v; struct tpm_bios_log *log = m->private; void *limit = log->bios_event_log_end; + u32 converted_event_size; + u32 converted_event_type; - v += sizeof(struct tcpa_event) + event->event_size; + converted_event_size = do_endian_conversion(event->event_size); + + v += sizeof(struct tcpa_event) + converted_event_size; /* now check if current entry is valid */ if ((v + sizeof(struct tcpa_event)) >= limit) @@ -116,11 +134,11 @@ static void *tpm_bios_measurements_next(struct seq_file *m, void *v, event = v; - if (event->event_type == 0 && event->event_size == 0) - return NULL; + converted_event_size = do_endian_conversion(event->event_size); + converted_event_type = do_endian_conversion(event->event_type); - if ((event->event_type == 0 && event->event_size == 0) || - ((v + sizeof(struct tcpa_event) + event->event_size) >= limit)) + if (((converted_event_type == 0) && (converted_event_size == 0)) || + ((v + sizeof(struct tcpa_event) + converted_event_size) >= limit)) return NULL; (*pos)++; @@ -140,7 +158,7 @@ static int get_event_name(char *dest, struct tcpa_event *event, int i, n_len = 0, d_len = 0; struct tcpa_pc_event *pc_event; - switch(event->event_type) { + switch (do_endian_conversion(event->event_type)) { case PREBOOT: case POST_CODE: case UNUSED: @@ -156,14 +174,16 @@ static int get_event_name(char *dest, struct tcpa_event *event, case NONHOST_CODE: case NONHOST_CONFIG: case NONHOST_INFO: - name = tcpa_event_type_strings[event->event_type]; + name = tcpa_event_type_strings[do_endian_conversion + (event->event_type)]; n_len = strlen(name); break; case SEPARATOR: case ACTION: - if (MAX_TEXT_EVENT > event->event_size) { + if (MAX_TEXT_EVENT > + do_endian_conversion(event->event_size)) { name = event_entry; - n_len = event->event_size; + n_len = do_endian_conversion(event->event_size); } break; case EVENT_TAG: @@ -171,7 +191,7 @@ static int get_event_name(char *dest, struct tcpa_event *event, /* ToDo Row data -> Base64 */ - switch (pc_event->event_id) { + switch (do_endian_conversion(pc_event->event_id)) { case SMBIOS: case BIS_CERT: case CMOS: @@ -179,7 +199,8 @@ static int get_event_name(char *dest, struct tcpa_event *event, case OPTION_ROM_EXEC: case OPTION_ROM_CONFIG: case S_CRTM_VERSION: - name = tcpa_pc_event_id_strings[pc_event->event_id]; + name = tcpa_pc_event_id_strings[do_endian_conversion + (pc_event->event_id)]; n_len = strlen(name); break; /* hash data */ @@ -188,7 +209,8 @@ static int get_event_name(char *dest, struct tcpa_event *event, case OPTION_ROM_MICROCODE: case S_CRTM_CONTENTS: case POST_CONTENTS: - name = tcpa_pc_event_id_strings[pc_event->event_id]; + name = tcpa_pc_event_id_strings[do_endian_conversion + (pc_event->event_id)]; n_len = strlen(name); for (i = 0; i < 20; i++) d_len += sprintf(&data[2*i], "%02x", @@ -209,13 +231,24 @@ static int get_event_name(char *dest, struct tcpa_event *event, static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v) { struct tcpa_event *event = v; - char *data = v; + struct tcpa_event temp_event; + char *tempPtr; int i; - for (i = 0; i < sizeof(struct tcpa_event) + event->event_size; i++) - seq_putc(m, data[i]); + memcpy(&temp_event, event, sizeof(struct tcpa_event)); + + /* convert raw integers for endianness */ + temp_event.pcr_index = do_endian_conversion(event->pcr_index); + temp_event.event_type = do_endian_conversion(event->event_type); + temp_event.event_size = do_endian_conversion(event->event_size); + + tempPtr = (char *)&temp_event; + + for (i = 0; i < sizeof(struct tcpa_event) + temp_event.event_size; i++) + seq_putc(m, tempPtr[i]); return 0; + } static int tpm_bios_measurements_release(struct inode *inode, @@ -238,7 +271,7 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) char *eventname; struct tcpa_event *event = v; unsigned char *event_entry = - (unsigned char *) (v + sizeof(struct tcpa_event)); + (unsigned char *)(v + sizeof(struct tcpa_event)); eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL); if (!eventname) { @@ -247,13 +280,14 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) return -EFAULT; } - seq_printf(m, "%2d ", event->pcr_index); + /* 1st: PCR */ + seq_printf(m, "%2d ", do_endian_conversion(event->pcr_index)); /* 2nd: SHA1 */ seq_printf(m, "%20phN", event->pcr_value); /* 3rd: event type identifier */ - seq_printf(m, " %02x", event->event_type); + seq_printf(m, " %02x", do_endian_conversion(event->event_type)); len += get_event_name(eventname, event, event_entry); diff --git a/kernel/drivers/char/tpm/tpm_eventlog.h b/kernel/drivers/char/tpm/tpm_eventlog.h index e7da086d6..267bfbd1b 100644 --- a/kernel/drivers/char/tpm/tpm_eventlog.h +++ b/kernel/drivers/char/tpm/tpm_eventlog.h @@ -6,6 +6,12 @@ #define MAX_TEXT_EVENT 1000 /* Max event string length */ #define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */ +#ifdef CONFIG_PPC64 +#define do_endian_conversion(x) be32_to_cpu(x) +#else +#define do_endian_conversion(x) x +#endif + enum bios_platform_class { BIOS_CLIENT = 0x00, BIOS_SERVER = 0x01, diff --git a/kernel/drivers/char/tpm/tpm_i2c_atmel.c b/kernel/drivers/char/tpm/tpm_i2c_atmel.c index 7a0ca78ad..8dfb88b97 100644 --- a/kernel/drivers/char/tpm/tpm_i2c_atmel.c +++ b/kernel/drivers/char/tpm/tpm_i2c_atmel.c @@ -217,7 +217,6 @@ static struct i2c_driver i2c_atmel_driver = { .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), }, diff --git a/kernel/drivers/char/tpm/tpm_i2c_infineon.c b/kernel/drivers/char/tpm/tpm_i2c_infineon.c index 33c5f360a..63d5d22e9 100644 --- a/kernel/drivers/char/tpm/tpm_i2c_infineon.c +++ b/kernel/drivers/char/tpm/tpm_i2c_infineon.c @@ -711,7 +711,6 @@ static struct i2c_driver tpm_tis_i2c_driver = { .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), }, diff --git a/kernel/drivers/char/tpm/tpm_i2c_nuvoton.c b/kernel/drivers/char/tpm/tpm_i2c_nuvoton.c index 9d42b7d78..847f1597f 100644 --- a/kernel/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/kernel/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -641,7 +641,6 @@ static struct i2c_driver i2c_nuvoton_driver = { .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), }, diff --git a/kernel/drivers/char/tpm/tpm_ibmvtpm.c b/kernel/drivers/char/tpm/tpm_ibmvtpm.c index 27ebf9511..3e6a22658 100644 --- a/kernel/drivers/char/tpm/tpm_ibmvtpm.c +++ b/kernel/drivers/char/tpm/tpm_ibmvtpm.c @@ -491,7 +491,7 @@ static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq, } ibmvtpm->rtce_size = be16_to_cpu(crq->len); ibmvtpm->rtce_buf = kmalloc(ibmvtpm->rtce_size, - GFP_KERNEL); + GFP_ATOMIC); if (!ibmvtpm->rtce_buf) { dev_err(ibmvtpm->dev, "Failed to allocate memory for rtce buffer\n"); return; diff --git a/kernel/drivers/char/tpm/tpm_of.c b/kernel/drivers/char/tpm/tpm_of.c index c002d1bd9..570f30c5c 100644 --- a/kernel/drivers/char/tpm/tpm_of.c +++ b/kernel/drivers/char/tpm/tpm_of.c @@ -24,14 +24,14 @@ int read_log(struct tpm_bios_log *log) { struct device_node *np; const u32 *sizep; - const __be64 *basep; + const u64 *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"); + np = of_find_node_by_name(NULL, "vtpm"); if (!np) { pr_err("%s: ERROR - IBMVTPM not supported\n", __func__); return -ENODEV; @@ -49,21 +49,22 @@ int read_log(struct tpm_bios_log *log) basep = of_get_property(np, "linux,sml-base", NULL); if (basep == NULL) { - pr_err(KERN_ERR "%s: ERROR - SML not found\n", __func__); + pr_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__); + of_node_put(np); return -ENOMEM; } log->bios_event_log_end = log->bios_event_log + *sizep; - memcpy(log->bios_event_log, __va(be64_to_cpup(basep)), *sizep); + memcpy(log->bios_event_log, __va(*basep), *sizep); + of_node_put(np); return 0; diff --git a/kernel/drivers/char/tpm/tpm_ppi.c b/kernel/drivers/char/tpm/tpm_ppi.c index 6ca9b5d78..692a2c6ae 100644 --- a/kernel/drivers/char/tpm/tpm_ppi.c +++ b/kernel/drivers/char/tpm/tpm_ppi.c @@ -53,7 +53,7 @@ tpm_eval_dsm(acpi_handle ppi_handle, int func, acpi_object_type type, static ssize_t tpm_show_ppi_version(struct device *dev, struct device_attribute *attr, char *buf) { - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); return scnprintf(buf, PAGE_SIZE, "%s\n", chip->ppi_version); } @@ -63,7 +63,7 @@ static ssize_t tpm_show_ppi_request(struct device *dev, { ssize_t size = -EINVAL; union acpi_object *obj; - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETREQ, ACPI_TYPE_PACKAGE, NULL); @@ -100,7 +100,7 @@ static ssize_t tpm_store_ppi_request(struct device *dev, int func = TPM_PPI_FN_SUBREQ; union acpi_object *obj, tmp; union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp); - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); /* * the function to submit TPM operation request to pre-os environment @@ -156,7 +156,7 @@ static ssize_t tpm_show_ppi_transition_action(struct device *dev, .buffer.length = 0, .buffer.pointer = NULL }; - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); static char *info[] = { "None", @@ -197,7 +197,7 @@ static ssize_t tpm_show_ppi_response(struct device *dev, acpi_status status = -EINVAL; union acpi_object *obj, *ret_obj; u64 req, res; - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETRSP, ACPI_TYPE_PACKAGE, NULL); @@ -296,7 +296,7 @@ static ssize_t tpm_show_ppi_tcg_operations(struct device *dev, struct device_attribute *attr, char *buf) { - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); return show_ppi_operations(chip->acpi_dev_handle, buf, 0, PPI_TPM_REQ_MAX); @@ -306,7 +306,7 @@ static ssize_t tpm_show_ppi_vs_operations(struct device *dev, struct device_attribute *attr, char *buf) { - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); return show_ppi_operations(chip->acpi_dev_handle, buf, PPI_VS_REQ_START, PPI_VS_REQ_END); @@ -334,17 +334,16 @@ static struct attribute_group ppi_attr_grp = { .attrs = ppi_attrs }; -int tpm_add_ppi(struct tpm_chip *chip) +void tpm_add_ppi(struct tpm_chip *chip) { union acpi_object *obj; - int rc; if (!chip->acpi_dev_handle) - return 0; + return; if (!acpi_check_dsm(chip->acpi_dev_handle, tpm_ppi_uuid, TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_VERSION)) - return 0; + return; /* Cache PPI version string. */ obj = acpi_evaluate_dsm_typed(chip->acpi_dev_handle, tpm_ppi_uuid, @@ -356,16 +355,5 @@ int tpm_add_ppi(struct tpm_chip *chip) ACPI_FREE(obj); } - rc = sysfs_create_group(&chip->pdev->kobj, &ppi_attr_grp); - - if (!rc) - chip->flags |= TPM_CHIP_FLAG_PPI; - - return rc; -} - -void tpm_remove_ppi(struct tpm_chip *chip) -{ - if (chip->flags & TPM_CHIP_FLAG_PPI) - sysfs_remove_group(&chip->pdev->kobj, &ppi_attr_grp); + chip->groups[chip->groups_cnt++] = &ppi_attr_grp; } diff --git a/kernel/drivers/char/tpm/tpm_tis.c b/kernel/drivers/char/tpm/tpm_tis.c index f2dffa770..65f7eecc4 100644 --- a/kernel/drivers/char/tpm/tpm_tis.c +++ b/kernel/drivers/char/tpm/tpm_tis.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2005, 2006 IBM Corporation - * Copyright (C) 2014 Intel Corporation + * Copyright (C) 2014, 2015 Intel Corporation * * Authors: * Leendert van Doorn <leendert@watson.ibm.com> @@ -28,6 +28,7 @@ #include <linux/wait.h> #include <linux/acpi.h> #include <linux/freezer.h> +#include <acpi/actbl2.h> #include "tpm.h" enum tis_access { @@ -65,6 +66,17 @@ enum tis_defaults { TIS_LONG_TIMEOUT = 2000, /* 2 sec */ }; +struct tpm_info { + unsigned long start; + unsigned long len; + unsigned int irq; +}; + +static struct tpm_info tis_default_info = { + .start = TIS_MEM_BASE, + .len = TIS_MEM_LEN, + .irq = 0, +}; /* Some timeout values are needed before it is known whether the chip is * TPM 1.0 or TPM 2.0. @@ -91,26 +103,54 @@ struct priv_data { }; #if defined(CONFIG_PNP) && defined(CONFIG_ACPI) -static int is_itpm(struct pnp_dev *dev) +static int has_hid(struct acpi_device *dev, const char *hid) { - 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)) + list_for_each_entry(id, &dev->pnp.ids, list) + if (!strcmp(hid, id->id)) return 1; - } return 0; } + +static inline int is_itpm(struct acpi_device *dev) +{ + return has_hid(dev, "INTC0102"); +} + +static inline int is_fifo(struct acpi_device *dev) +{ + struct acpi_table_tpm2 *tbl; + acpi_status st; + + /* TPM 1.2 FIFO */ + if (!has_hid(dev, "MSFT0101")) + return 1; + + st = acpi_get_table(ACPI_SIG_TPM2, 1, + (struct acpi_table_header **) &tbl); + if (ACPI_FAILURE(st)) { + dev_err(&dev->dev, "failed to get TPM2 ACPI table\n"); + return 0; + } + + if (le32_to_cpu(tbl->start_method) != TPM2_START_FIFO) + return 0; + + /* TPM 2.0 FIFO */ + return 1; +} #else -static inline int is_itpm(struct pnp_dev *dev) +static inline int is_itpm(struct acpi_device *dev) { return 0; } + +static inline int is_fifo(struct acpi_device *dev) +{ + return 1; +} #endif /* Before we attempt to access the TPM we must see that the valid bit is set. @@ -600,12 +640,12 @@ static void tpm_tis_remove(struct tpm_chip *chip) release_locality(chip, chip->vendor.locality, 1); } -static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, - resource_size_t start, resource_size_t len, - unsigned int irq) +static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, + acpi_handle acpi_dev_handle) { u32 vendor, intfcaps, intmask; int rc, i, irq_s, irq_e, probe; + int irq_r = -1; struct tpm_chip *chip; struct priv_data *priv; @@ -622,7 +662,7 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, chip->acpi_dev_handle = acpi_dev_handle; #endif - chip->vendor.iobase = devm_ioremap(dev, start, len); + chip->vendor.iobase = devm_ioremap(dev, tpm_info->start, tpm_info->len); if (!chip->vendor.iobase) return -EIO; @@ -707,11 +747,12 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); if (interrupts) - chip->vendor.irq = irq; + chip->vendor.irq = tpm_info->irq; if (interrupts && !chip->vendor.irq) { irq_s = ioread8(chip->vendor.iobase + TPM_INT_VECTOR(chip->vendor.locality)); + irq_r = irq_s; if (irq_s) { irq_e = irq_s; } else { @@ -766,6 +807,8 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, iowrite32(intmask, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); + + devm_free_irq(dev, i, chip); } } if (chip->vendor.irq) { @@ -792,7 +835,9 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); } - } + } else if (irq_r != -1) + iowrite8(irq_r, chip->vendor.iobase + + TPM_INT_VECTOR(chip->vendor.locality)); if (chip->flags & TPM_CHIP_FLAG_TPM2) { chip->vendor.timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A); @@ -890,27 +935,27 @@ static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume); static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev, const struct pnp_device_id *pnp_id) { - resource_size_t start, len; - unsigned int irq = 0; + struct tpm_info tpm_info = tis_default_info; acpi_handle acpi_dev_handle = NULL; - start = pnp_mem_start(pnp_dev, 0); - len = pnp_mem_len(pnp_dev, 0); + tpm_info.start = pnp_mem_start(pnp_dev, 0); + tpm_info.len = pnp_mem_len(pnp_dev, 0); if (pnp_irq_valid(pnp_dev, 0)) - irq = pnp_irq(pnp_dev, 0); + tpm_info.irq = pnp_irq(pnp_dev, 0); else interrupts = false; - if (is_itpm(pnp_dev)) - itpm = true; - #ifdef CONFIG_ACPI - if (pnp_acpi_device(pnp_dev)) + if (pnp_acpi_device(pnp_dev)) { + if (is_itpm(pnp_acpi_device(pnp_dev))) + itpm = true; + acpi_dev_handle = pnp_acpi_device(pnp_dev)->handle; + } #endif - return tpm_tis_init(&pnp_dev->dev, acpi_dev_handle, start, len, irq); + return tpm_tis_init(&pnp_dev->dev, &tpm_info, acpi_dev_handle); } static struct pnp_device_id tpm_pnp_tbl[] = { @@ -930,6 +975,7 @@ MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl); static void tpm_tis_pnp_remove(struct pnp_dev *dev) { struct tpm_chip *chip = pnp_get_drvdata(dev); + tpm_chip_unregister(chip); tpm_tis_remove(chip); } @@ -950,6 +996,79 @@ module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id, MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe"); #endif +#ifdef CONFIG_ACPI +static int tpm_check_resource(struct acpi_resource *ares, void *data) +{ + struct tpm_info *tpm_info = (struct tpm_info *) data; + struct resource res; + + if (acpi_dev_resource_interrupt(ares, 0, &res)) { + tpm_info->irq = res.start; + } else if (acpi_dev_resource_memory(ares, &res)) { + tpm_info->start = res.start; + tpm_info->len = resource_size(&res); + } + + return 1; +} + +static int tpm_tis_acpi_init(struct acpi_device *acpi_dev) +{ + struct list_head resources; + struct tpm_info tpm_info = tis_default_info; + int ret; + + if (!is_fifo(acpi_dev)) + return -ENODEV; + + INIT_LIST_HEAD(&resources); + ret = acpi_dev_get_resources(acpi_dev, &resources, tpm_check_resource, + &tpm_info); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&resources); + + if (!tpm_info.irq) + interrupts = false; + + if (is_itpm(acpi_dev)) + itpm = true; + + return tpm_tis_init(&acpi_dev->dev, &tpm_info, acpi_dev->handle); +} + +static int tpm_tis_acpi_remove(struct acpi_device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(&dev->dev); + + tpm_chip_unregister(chip); + tpm_tis_remove(chip); + + return 0; +} + +static struct acpi_device_id tpm_acpi_tbl[] = { + {"MSFT0101", 0}, /* TPM 2.0 */ + /* Add new here */ + {"", 0}, /* User Specified */ + {"", 0} /* Terminator */ +}; +MODULE_DEVICE_TABLE(acpi, tpm_acpi_tbl); + +static struct acpi_driver tis_acpi_driver = { + .name = "tpm_tis", + .ids = tpm_acpi_tbl, + .ops = { + .add = tpm_tis_acpi_init, + .remove = tpm_tis_acpi_remove, + }, + .drv = { + .pm = &tpm_tis_pm, + }, +}; +#endif + static struct platform_driver tis_drv = { .driver = { .name = "tpm_tis", @@ -966,9 +1085,25 @@ static int __init init_tis(void) { int rc; #ifdef CONFIG_PNP - if (!force) - return pnp_register_driver(&tis_pnp_driver); + if (!force) { + rc = pnp_register_driver(&tis_pnp_driver); + if (rc) + return rc; + } +#endif +#ifdef CONFIG_ACPI + if (!force) { + rc = acpi_bus_register_driver(&tis_acpi_driver); + if (rc) { +#ifdef CONFIG_PNP + pnp_unregister_driver(&tis_pnp_driver); #endif + return rc; + } + } +#endif + if (!force) + return 0; rc = platform_driver_register(&tis_drv); if (rc < 0) @@ -978,7 +1113,7 @@ static int __init init_tis(void) rc = PTR_ERR(pdev); goto err_dev; } - rc = tpm_tis_init(&pdev->dev, NULL, TIS_MEM_BASE, TIS_MEM_LEN, 0); + rc = tpm_tis_init(&pdev->dev, &tis_default_info, NULL); if (rc) goto err_init; return 0; @@ -992,9 +1127,14 @@ err_dev: static void __exit cleanup_tis(void) { struct tpm_chip *chip; -#ifdef CONFIG_PNP +#if defined(CONFIG_PNP) || defined(CONFIG_ACPI) if (!force) { +#ifdef CONFIG_ACPI + acpi_bus_unregister_driver(&tis_acpi_driver); +#endif +#ifdef CONFIG_PNP pnp_unregister_driver(&tis_pnp_driver); +#endif return; } #endif diff --git a/kernel/drivers/char/virtio_console.c b/kernel/drivers/char/virtio_console.c index 50754d203..d2406fe25 100644 --- a/kernel/drivers/char/virtio_console.c +++ b/kernel/drivers/char/virtio_console.c @@ -1492,8 +1492,8 @@ static int add_port(struct ports_device *portdev, u32 id) * Finally, create the debugfs file that we can use to * inspect a port's state at any time */ - sprintf(debugfs_name, "vport%up%u", - port->portdev->vdev->index, id); + snprintf(debugfs_name, sizeof(debugfs_name), "vport%up%u", + port->portdev->vdev->index, id); port->debugfs_file = debugfs_create_file(debugfs_name, 0444, pdrvdata.debugfs_dir, port, diff --git a/kernel/drivers/char/xilinx_hwicap/buffer_icap.c b/kernel/drivers/char/xilinx_hwicap/buffer_icap.c index 05d897764..53c3882e4 100644 --- a/kernel/drivers/char/xilinx_hwicap/buffer_icap.c +++ b/kernel/drivers/char/xilinx_hwicap/buffer_icap.c @@ -270,7 +270,7 @@ int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data, int status; s32 buffer_count = 0; s32 num_writes = 0; - bool dirty = 0; + bool dirty = false; u32 i; void __iomem *base_address = drvdata->base_address; @@ -279,7 +279,7 @@ int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data, /* Copy data to bram */ buffer_icap_set_bram(base_address, buffer_count, data[i]); - dirty = 1; + dirty = true; if (buffer_count < XHI_MAX_BUFFER_INTS - 1) { buffer_count++; @@ -299,7 +299,7 @@ int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data, buffer_count = 0; num_writes++; - dirty = 0; + dirty = false; } /* Write unwritten data to ICAP */ diff --git a/kernel/drivers/char/xillybus/Kconfig b/kernel/drivers/char/xillybus/Kconfig index b53bdf12d..b302684d8 100644 --- a/kernel/drivers/char/xillybus/Kconfig +++ b/kernel/drivers/char/xillybus/Kconfig @@ -24,7 +24,7 @@ config XILLYBUS_PCIE config XILLYBUS_OF tristate "Xillybus over Device Tree" - depends on OF_ADDRESS && OF_IRQ + depends on OF_ADDRESS && OF_IRQ && HAS_DMA help Set to M if you want Xillybus to find its resources from the Open Firmware Flattened Device Tree. If the target is an embedded diff --git a/kernel/drivers/char/xillybus/xillybus_pcie.c b/kernel/drivers/char/xillybus/xillybus_pcie.c index d8266bc2a..941830021 100644 --- a/kernel/drivers/char/xillybus/xillybus_pcie.c +++ b/kernel/drivers/char/xillybus/xillybus_pcie.c @@ -193,14 +193,16 @@ static int xilly_probe(struct pci_dev *pdev, } /* - * In theory, an attempt to set the DMA mask to 64 and dma_using_dac=1 - * is the right thing. But some unclever PCIe drivers report it's OK - * when the hardware drops those 64-bit PCIe packets. So trust - * nobody and use 32 bits DMA addressing in any case. + * Some (old and buggy?) hardware drops 64-bit addressed PCIe packets, + * even when the PCIe driver claims that a 64-bit mask is OK. On the + * other hand, on some architectures, 64-bit addressing is mandatory. + * So go for the 64-bit mask only when failing is the other option. */ if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { endpoint->dma_using_dac = 0; + } else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) { + endpoint->dma_using_dac = 1; } else { dev_err(endpoint->dev, "Failed to set DMA mask. Aborting.\n"); return -ENODEV; |