aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/Kconfig19
-rw-r--r--drivers/char/Makefile6
-rw-r--r--drivers/char/agp/efficeon-agp.c2
-rw-r--r--drivers/char/applicom.c35
-rw-r--r--drivers/char/efirtc.c23
-rw-r--r--drivers/char/generic_nvram.c159
-rw-r--r--drivers/char/hpet.c9
-rw-r--r--drivers/char/hw_random/Kconfig15
-rw-r--r--drivers/char/hw_random/Makefile1
-rw-r--r--drivers/char/hw_random/bcm2835-rng.c18
-rw-r--r--drivers/char/hw_random/optee-rng.c306
-rw-r--r--drivers/char/hw_random/virtio-rng.c2
-rw-r--r--drivers/char/ipmi/Kconfig5
-rw-r--r--drivers/char/ipmi/Makefile1
-rw-r--r--drivers/char/ipmi/ipmi_devintf.c6
-rw-r--r--drivers/char/ipmi/ipmi_dmi.c139
-rw-r--r--drivers/char/ipmi/ipmi_dmi.h2
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c159
-rw-r--r--drivers/char/ipmi/ipmi_plat_data.c121
-rw-r--r--drivers/char/ipmi/ipmi_plat_data.h22
-rw-r--r--drivers/char/ipmi/ipmi_si.h14
-rw-r--r--drivers/char/ipmi/ipmi_si_hardcode.c149
-rw-r--r--drivers/char/ipmi/ipmi_si_hotmod.c222
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c102
-rw-r--r--drivers/char/ipmi/ipmi_si_mem_io.c5
-rw-r--r--drivers/char/ipmi/ipmi_si_parisc.c2
-rw-r--r--drivers/char/ipmi/ipmi_si_pci.c4
-rw-r--r--drivers/char/ipmi/ipmi_si_platform.c73
-rw-r--r--drivers/char/ipmi/ipmi_si_port_io.c5
-rw-r--r--drivers/char/ipmi/ipmi_si_sm.h14
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c188
-rw-r--r--drivers/char/ipmi/kcs_bmc.c5
-rw-r--r--drivers/char/lp.c4
-rw-r--r--drivers/char/mbcs.c1
-rw-r--r--drivers/char/nvram.c673
35 files changed, 1389 insertions, 1122 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 2e2ffe7010aa..72866a004f07 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -244,26 +244,23 @@ source "drivers/char/hw_random/Kconfig"
config NVRAM
tristate "/dev/nvram support"
- depends on ATARI || X86 || GENERIC_NVRAM
+ depends on X86 || HAVE_ARCH_NVRAM_OPS
+ default M68K || PPC
---help---
If you say Y here and create a character special file /dev/nvram
with major number 10 and minor number 144 using mknod ("man mknod"),
- you get read and write access to the extra bytes of non-volatile
- memory in the real time clock (RTC), which is contained in every PC
- and most Ataris. The actual number of bytes varies, depending on the
- nvram in the system, but is usually 114 (128-14 for the RTC).
-
- This memory is conventionally called "CMOS RAM" on PCs and "NVRAM"
- on Ataris. /dev/nvram may be used to view settings there, or to
- change them (with some utility). It could also be used to frequently
+ you get read and write access to the non-volatile memory.
+
+ /dev/nvram may be used to view settings in NVRAM or to change them
+ (with some utility). It could also be used to frequently
save a few bits of very important data that may not be lost over
power-off and for which writing to disk is too insecure. Note
however that most NVRAM space in a PC belongs to the BIOS and you
should NEVER idly tamper with it. See Ralf Brown's interrupt list
for a guide to the use of CMOS bytes by your BIOS.
- On Atari machines, /dev/nvram is always configured and does not need
- to be selected.
+ This memory is conventionally called "NVRAM" on PowerPC machines,
+ "CMOS RAM" on PCs, "NVRAM" on Ataris and "PRAM" on Macintoshes.
To compile this driver as a module, choose M here: the
module will be called nvram.
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index b8d42b4e979b..fbea7dd12932 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -26,11 +26,7 @@ obj-$(CONFIG_RTC) += rtc.o
obj-$(CONFIG_HPET) += hpet.o
obj-$(CONFIG_EFI_RTC) += efirtc.o
obj-$(CONFIG_XILINX_HWICAP) += xilinx_hwicap/
-ifeq ($(CONFIG_GENERIC_NVRAM),y)
- obj-$(CONFIG_NVRAM) += generic_nvram.o
-else
- obj-$(CONFIG_NVRAM) += nvram.o
-endif
+obj-$(CONFIG_NVRAM) += nvram.o
obj-$(CONFIG_TOSHIBA) += toshiba.o
obj-$(CONFIG_DS1620) += ds1620.o
obj-$(CONFIG_HW_RANDOM) += hw_random/
diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c
index 7f88490b5479..c53f0f9ef5b0 100644
--- a/drivers/char/agp/efficeon-agp.c
+++ b/drivers/char/agp/efficeon-agp.c
@@ -163,7 +163,6 @@ static int efficeon_free_gatt_table(struct agp_bridge_data *bridge)
unsigned long page = efficeon_private.l1_table[index];
if (page) {
efficeon_private.l1_table[index] = 0;
- ClearPageReserved(virt_to_page((char *)page));
free_page(page);
freed++;
}
@@ -219,7 +218,6 @@ static int efficeon_create_gatt_table(struct agp_bridge_data *bridge)
efficeon_free_gatt_table(agp_bridge);
return -ENOMEM;
}
- SetPageReserved(virt_to_page((char *)page));
for (offset = 0; offset < PAGE_SIZE; offset += clflush_chunk)
clflush((char *)page+offset);
diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c
index c0a5b1f3a986..4ccc39e00ced 100644
--- a/drivers/char/applicom.c
+++ b/drivers/char/applicom.c
@@ -32,6 +32,7 @@
#include <linux/wait.h>
#include <linux/init.h>
#include <linux/fs.h>
+#include <linux/nospec.h>
#include <asm/io.h>
#include <linux/uaccess.h>
@@ -386,7 +387,11 @@ static ssize_t ac_write(struct file *file, const char __user *buf, size_t count,
TicCard = st_loc.tic_des_from_pc; /* tic number to send */
IndexCard = NumCard - 1;
- if((NumCard < 1) || (NumCard > MAX_BOARD) || !apbs[IndexCard].RamIO)
+ if (IndexCard >= MAX_BOARD)
+ return -EINVAL;
+ IndexCard = array_index_nospec(IndexCard, MAX_BOARD);
+
+ if (!apbs[IndexCard].RamIO)
return -EINVAL;
#ifdef DEBUG
@@ -697,6 +702,7 @@ static long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
unsigned char IndexCard;
void __iomem *pmem;
int ret = 0;
+ static int warncount = 10;
volatile unsigned char byte_reset_it;
struct st_ram_io *adgl;
void __user *argp = (void __user *)arg;
@@ -711,16 +717,12 @@ static long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
mutex_lock(&ac_mutex);
IndexCard = adgl->num_card-1;
- if(cmd != 6 && ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) {
- static int warncount = 10;
- if (warncount) {
- printk( KERN_WARNING "APPLICOM driver IOCTL, bad board number %d\n",(int)IndexCard+1);
- warncount--;
- }
- kfree(adgl);
- mutex_unlock(&ac_mutex);
- return -EINVAL;
- }
+ if (cmd != 6 && IndexCard >= MAX_BOARD)
+ goto err;
+ IndexCard = array_index_nospec(IndexCard, MAX_BOARD);
+
+ if (cmd != 6 && !apbs[IndexCard].RamIO)
+ goto err;
switch (cmd) {
@@ -838,5 +840,16 @@ static long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
kfree(adgl);
mutex_unlock(&ac_mutex);
return 0;
+
+err:
+ if (warncount) {
+ pr_warn("APPLICOM driver IOCTL, bad board number %d\n",
+ (int)IndexCard + 1);
+ warncount--;
+ }
+ kfree(adgl);
+ mutex_unlock(&ac_mutex);
+ return -EINVAL;
+
}
diff --git a/drivers/char/efirtc.c b/drivers/char/efirtc.c
index d9aab643997e..11781ebffbf7 100644
--- a/drivers/char/efirtc.c
+++ b/drivers/char/efirtc.c
@@ -255,35 +255,12 @@ static long efi_rtc_ioctl(struct file *file, unsigned int cmd,
}
/*
- * We enforce only one user at a time here with the open/close.
- * Also clear the previous interrupt data on an open, and clean
- * up things on a close.
- */
-
-static int efi_rtc_open(struct inode *inode, struct file *file)
-{
- /*
- * nothing special to do here
- * We do accept multiple open files at the same time as we
- * synchronize on the per call operation.
- */
- return 0;
-}
-
-static int efi_rtc_close(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-/*
* The various file operations we support.
*/
static const struct file_operations efi_rtc_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = efi_rtc_ioctl,
- .open = efi_rtc_open,
- .release = efi_rtc_close,
.llseek = no_llseek,
};
diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c
deleted file mode 100644
index ff5394f47587..000000000000
--- a/drivers/char/generic_nvram.c
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Generic /dev/nvram driver for architectures providing some
- * "generic" hooks, that is :
- *
- * nvram_read_byte, nvram_write_byte, nvram_sync, nvram_get_size
- *
- * Note that an additional hook is supported for PowerMac only
- * for getting the nvram "partition" informations
- *
- */
-
-#define NVRAM_VERSION "1.1"
-
-#include <linux/module.h>
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/miscdevice.h>
-#include <linux/fcntl.h>
-#include <linux/init.h>
-#include <linux/mutex.h>
-#include <linux/pagemap.h>
-#include <linux/uaccess.h>
-#include <asm/nvram.h>
-#ifdef CONFIG_PPC_PMAC
-#include <asm/machdep.h>
-#endif
-
-#define NVRAM_SIZE 8192
-
-static DEFINE_MUTEX(nvram_mutex);
-static ssize_t nvram_len;
-
-static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
-{
- return generic_file_llseek_size(file, offset, origin,
- MAX_LFS_FILESIZE, nvram_len);
-}
-
-static ssize_t read_nvram(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- unsigned int i;
- char __user *p = buf;
-
- if (!access_ok(buf, count))
- return -EFAULT;
- if (*ppos >= nvram_len)
- return 0;
- for (i = *ppos; count > 0 && i < nvram_len; ++i, ++p, --count)
- if (__put_user(nvram_read_byte(i), p))
- return -EFAULT;
- *ppos = i;
- return p - buf;
-}
-
-static ssize_t write_nvram(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- unsigned int i;
- const char __user *p = buf;
- char c;
-
- if (!access_ok(buf, count))
- return -EFAULT;
- if (*ppos >= nvram_len)
- return 0;
- for (i = *ppos; count > 0 && i < nvram_len; ++i, ++p, --count) {
- if (__get_user(c, p))
- return -EFAULT;
- nvram_write_byte(c, i);
- }
- *ppos = i;
- return p - buf;
-}
-
-static int nvram_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- switch(cmd) {
-#ifdef CONFIG_PPC_PMAC
- case OBSOLETE_PMAC_NVRAM_GET_OFFSET:
- printk(KERN_WARNING "nvram: Using obsolete PMAC_NVRAM_GET_OFFSET ioctl\n");
- case IOC_NVRAM_GET_OFFSET: {
- int part, offset;
-
- if (!machine_is(powermac))
- return -EINVAL;
- if (copy_from_user(&part, (void __user*)arg, sizeof(part)) != 0)
- return -EFAULT;
- if (part < pmac_nvram_OF || part > pmac_nvram_NR)
- return -EINVAL;
- offset = pmac_get_partition(part);
- if (copy_to_user((void __user*)arg, &offset, sizeof(offset)) != 0)
- return -EFAULT;
- break;
- }
-#endif /* CONFIG_PPC_PMAC */
- case IOC_NVRAM_SYNC:
- nvram_sync();
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static long nvram_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- int ret;
-
- mutex_lock(&nvram_mutex);
- ret = nvram_ioctl(file, cmd, arg);
- mutex_unlock(&nvram_mutex);
-
- return ret;
-}
-
-const struct file_operations nvram_fops = {
- .owner = THIS_MODULE,
- .llseek = nvram_llseek,
- .read = read_nvram,
- .write = write_nvram,
- .unlocked_ioctl = nvram_unlocked_ioctl,
-};
-
-static struct miscdevice nvram_dev = {
- NVRAM_MINOR,
- "nvram",
- &nvram_fops
-};
-
-int __init nvram_init(void)
-{
- int ret = 0;
-
- printk(KERN_INFO "Generic non-volatile memory driver v%s\n",
- NVRAM_VERSION);
- ret = misc_register(&nvram_dev);
- if (ret != 0)
- goto out;
-
- nvram_len = nvram_get_size();
- if (nvram_len < 0)
- nvram_len = NVRAM_SIZE;
-
-out:
- return ret;
-}
-
-void __exit nvram_cleanup(void)
-{
- misc_deregister( &nvram_dev );
-}
-
-module_init(nvram_init);
-module_exit(nvram_cleanup);
-MODULE_LICENSE("GPL");
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index 4a22b4b41aef..d0ad85900b79 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -377,7 +377,7 @@ static __init int hpet_mmap_enable(char *str)
pr_info("HPET mmap %s\n", hpet_mmap_enabled ? "enabled" : "disabled");
return 1;
}
-__setup("hpet_mmap", hpet_mmap_enable);
+__setup("hpet_mmap=", hpet_mmap_enable);
static int hpet_mmap(struct file *file, struct vm_area_struct *vma)
{
@@ -842,7 +842,6 @@ int hpet_alloc(struct hpet_data *hdp)
struct hpet_dev *devp;
u32 i, ntimer;
struct hpets *hpetp;
- size_t siz;
struct hpet __iomem *hpet;
static struct hpets *last;
unsigned long period;
@@ -860,10 +859,8 @@ int hpet_alloc(struct hpet_data *hdp)
return 0;
}
- siz = sizeof(struct hpets) + ((hdp->hd_nirqs - 1) *
- sizeof(struct hpet_dev));
-
- hpetp = kzalloc(siz, GFP_KERNEL);
+ hpetp = kzalloc(struct_size(hpetp, hp_dev, hdp->hd_nirqs - 1),
+ GFP_KERNEL);
if (!hpetp)
return -ENOMEM;
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index dac895dc01b9..25a7d8ffdb5d 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -424,6 +424,21 @@ config HW_RANDOM_EXYNOS
will be called exynos-trng.
If unsure, say Y.
+
+config HW_RANDOM_OPTEE
+ tristate "OP-TEE based Random Number Generator support"
+ depends on OPTEE
+ default HW_RANDOM
+ help
+ This driver provides support for OP-TEE based Random Number
+ Generator on ARM SoCs where hardware entropy sources are not
+ accessible to normal world (Linux).
+
+ To compile this driver as a module, choose M here: the module
+ will be called optee-rng.
+
+ If unsure, say Y.
+
endif # HW_RANDOM
config UML_RANDOM
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index e35ec3ce3a20..7c9ef4a7667f 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -38,3 +38,4 @@ obj-$(CONFIG_HW_RANDOM_CAVIUM) += cavium-rng.o cavium-rng-vf.o
obj-$(CONFIG_HW_RANDOM_MTK) += mtk-rng.o
obj-$(CONFIG_HW_RANDOM_S390) += s390-trng.o
obj-$(CONFIG_HW_RANDOM_KEYSTONE) += ks-sa-rng.o
+obj-$(CONFIG_HW_RANDOM_OPTEE) += optee-rng.o
diff --git a/drivers/char/hw_random/bcm2835-rng.c b/drivers/char/hw_random/bcm2835-rng.c
index 256b0b1d0f26..f759790c3cdb 100644
--- a/drivers/char/hw_random/bcm2835-rng.c
+++ b/drivers/char/hw_random/bcm2835-rng.c
@@ -168,14 +168,16 @@ static int bcm2835_rng_probe(struct platform_device *pdev)
priv->rng.read = bcm2835_rng_read;
priv->rng.cleanup = bcm2835_rng_cleanup;
- rng_id = of_match_node(bcm2835_rng_of_match, np);
- if (!rng_id)
- return -EINVAL;
-
- /* Check for rng init function, execute it */
- of_data = rng_id->data;
- if (of_data)
- priv->mask_interrupts = of_data->mask_interrupts;
+ if (dev_of_node(dev)) {
+ rng_id = of_match_node(bcm2835_rng_of_match, np);
+ if (!rng_id)
+ return -EINVAL;
+
+ /* Check for rng init function, execute it */
+ of_data = rng_id->data;
+ if (of_data)
+ priv->mask_interrupts = of_data->mask_interrupts;
+ }
/* register driver */
err = devm_hwrng_register(dev, &priv->rng);
diff --git a/drivers/char/hw_random/optee-rng.c b/drivers/char/hw_random/optee-rng.c
new file mode 100644
index 000000000000..ddfbabaa5f8f
--- /dev/null
+++ b/drivers/char/hw_random/optee-rng.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2019 Linaro Ltd.
+ */
+
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/hw_random.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tee_drv.h>
+#include <linux/uuid.h>
+
+#define DRIVER_NAME "optee-rng"
+
+#define TEE_ERROR_HEALTH_TEST_FAIL 0x00000001
+
+/*
+ * TA_CMD_GET_ENTROPY - Get Entropy from RNG
+ *
+ * param[0] (inout memref) - Entropy buffer memory reference
+ * param[1] unused
+ * param[2] unused
+ * param[3] unused
+ *
+ * Result:
+ * TEE_SUCCESS - Invoke command success
+ * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
+ * TEE_ERROR_NOT_SUPPORTED - Requested entropy size greater than size of pool
+ * TEE_ERROR_HEALTH_TEST_FAIL - Continuous health testing failed
+ */
+#define TA_CMD_GET_ENTROPY 0x0
+
+/*
+ * TA_CMD_GET_RNG_INFO - Get RNG information
+ *
+ * param[0] (out value) - value.a: RNG data-rate in bytes per second
+ * value.b: Quality/Entropy per 1024 bit of data
+ * param[1] unused
+ * param[2] unused
+ * param[3] unused
+ *
+ * Result:
+ * TEE_SUCCESS - Invoke command success
+ * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
+ */
+#define TA_CMD_GET_RNG_INFO 0x1
+
+#define MAX_ENTROPY_REQ_SZ (4 * 1024)
+
+/**
+ * struct optee_rng_private - OP-TEE Random Number Generator private data
+ * @dev: OP-TEE based RNG device.
+ * @ctx: OP-TEE context handler.
+ * @session_id: RNG TA session identifier.
+ * @data_rate: RNG data rate.
+ * @entropy_shm_pool: Memory pool shared with RNG device.
+ * @optee_rng: OP-TEE RNG driver structure.
+ */
+struct optee_rng_private {
+ struct device *dev;
+ struct tee_context *ctx;
+ u32 session_id;
+ u32 data_rate;
+ struct tee_shm *entropy_shm_pool;
+ struct hwrng optee_rng;
+};
+
+#define to_optee_rng_private(r) \
+ container_of(r, struct optee_rng_private, optee_rng)
+
+static size_t get_optee_rng_data(struct optee_rng_private *pvt_data,
+ void *buf, size_t req_size)
+{
+ int ret = 0;
+ u8 *rng_data = NULL;
+ size_t rng_size = 0;
+ struct tee_ioctl_invoke_arg inv_arg;
+ struct tee_param param[4];
+
+ memset(&inv_arg, 0, sizeof(inv_arg));
+ memset(&param, 0, sizeof(param));
+
+ /* Invoke TA_CMD_GET_ENTROPY function of Trusted App */
+ inv_arg.func = TA_CMD_GET_ENTROPY;
+ inv_arg.session = pvt_data->session_id;
+ inv_arg.num_params = 4;
+
+ /* Fill invoke cmd params */
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
+ param[0].u.memref.shm = pvt_data->entropy_shm_pool;
+ param[0].u.memref.size = req_size;
+ param[0].u.memref.shm_offs = 0;
+
+ ret = tee_client_invoke_func(pvt_data->ctx, &inv_arg, param);
+ if ((ret < 0) || (inv_arg.ret != 0)) {
+ dev_err(pvt_data->dev, "TA_CMD_GET_ENTROPY invoke err: %x\n",
+ inv_arg.ret);
+ return 0;
+ }
+
+ rng_data = tee_shm_get_va(pvt_data->entropy_shm_pool, 0);
+ if (IS_ERR(rng_data)) {
+ dev_err(pvt_data->dev, "tee_shm_get_va failed\n");
+ return 0;
+ }
+
+ rng_size = param[0].u.memref.size;
+ memcpy(buf, rng_data, rng_size);
+
+ return rng_size;
+}
+
+static int optee_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ struct optee_rng_private *pvt_data = to_optee_rng_private(rng);
+ size_t read = 0, rng_size = 0;
+ int timeout = 1;
+ u8 *data = buf;
+
+ if (max > MAX_ENTROPY_REQ_SZ)
+ max = MAX_ENTROPY_REQ_SZ;
+
+ while (read == 0) {
+ rng_size = get_optee_rng_data(pvt_data, data, (max - read));
+
+ data += rng_size;
+ read += rng_size;
+
+ if (wait) {
+ if (timeout-- == 0)
+ return read;
+ msleep((1000 * (max - read)) / pvt_data->data_rate);
+ } else {
+ return read;
+ }
+ }
+
+ return read;
+}
+
+static int optee_rng_init(struct hwrng *rng)
+{
+ struct optee_rng_private *pvt_data = to_optee_rng_private(rng);
+ struct tee_shm *entropy_shm_pool = NULL;
+
+ entropy_shm_pool = tee_shm_alloc(pvt_data->ctx, MAX_ENTROPY_REQ_SZ,
+ TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
+ if (IS_ERR(entropy_shm_pool)) {
+ dev_err(pvt_data->dev, "tee_shm_alloc failed\n");
+ return PTR_ERR(entropy_shm_pool);
+ }
+
+ pvt_data->entropy_shm_pool = entropy_shm_pool;
+
+ return 0;
+}
+
+static void optee_rng_cleanup(struct hwrng *rng)
+{
+ struct optee_rng_private *pvt_data = to_optee_rng_private(rng);
+
+ tee_shm_free(pvt_data->entropy_shm_pool);
+}
+
+static struct optee_rng_private pvt_data = {
+ .optee_rng = {
+ .name = DRIVER_NAME,
+ .init = optee_rng_init,
+ .cleanup = optee_rng_cleanup,
+ .read = optee_rng_read,
+ }
+};
+
+static int get_optee_rng_info(struct device *dev)
+{
+ int ret = 0;
+ struct tee_ioctl_invoke_arg inv_arg;
+ struct tee_param param[4];
+
+ memset(&inv_arg, 0, sizeof(inv_arg));
+ memset(&param, 0, sizeof(param));
+
+ /* Invoke TA_CMD_GET_RNG_INFO function of Trusted App */
+ inv_arg.func = TA_CMD_GET_RNG_INFO;
+ inv_arg.session = pvt_data.session_id;
+ inv_arg.num_params = 4;
+
+ /* Fill invoke cmd params */
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
+
+ ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param);
+ if ((ret < 0) || (inv_arg.ret != 0)) {
+ dev_err(dev, "TA_CMD_GET_RNG_INFO invoke err: %x\n",
+ inv_arg.ret);
+ return -EINVAL;
+ }
+
+ pvt_data.data_rate = param[0].u.value.a;
+ pvt_data.optee_rng.quality = param[0].u.value.b;
+
+ return 0;
+}
+
+static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
+{
+ if (ver->impl_id == TEE_IMPL_ID_OPTEE)
+ return 1;
+ else
+ return 0;
+}
+
+static int optee_rng_probe(struct device *dev)
+{
+ struct tee_client_device *rng_device = to_tee_client_device(dev);
+ int ret = 0, err = -ENODEV;
+ struct tee_ioctl_open_session_arg sess_arg;
+
+ memset(&sess_arg, 0, sizeof(sess_arg));
+
+ /* Open context with TEE driver */
+ pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL,
+ NULL);
+ if (IS_ERR(pvt_data.ctx))
+ return -ENODEV;
+
+ /* Open session with hwrng Trusted App */
+ memcpy(sess_arg.uuid, rng_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
+ sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
+ sess_arg.num_params = 0;
+
+ ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL);
+ if ((ret < 0) || (sess_arg.ret != 0)) {
+ dev_err(dev, "tee_client_open_session failed, err: %x\n",
+ sess_arg.ret);
+ err = -EINVAL;
+ goto out_ctx;
+ }
+ pvt_data.session_id = sess_arg.session;
+
+ err = get_optee_rng_info(dev);
+ if (err)
+ goto out_sess;
+
+ err = hwrng_register(&pvt_data.optee_rng);
+ if (err) {
+ dev_err(dev, "hwrng registration failed (%d)\n", err);
+ goto out_sess;
+ }
+
+ pvt_data.dev = dev;
+
+ return 0;
+
+out_sess:
+ tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
+out_ctx:
+ tee_client_close_context(pvt_data.ctx);
+
+ return err;
+}
+
+static int optee_rng_remove(struct device *dev)
+{
+ hwrng_unregister(&pvt_data.optee_rng);
+ tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
+ tee_client_close_context(pvt_data.ctx);
+
+ return 0;
+}
+
+static const struct tee_client_device_id optee_rng_id_table[] = {
+ {UUID_INIT(0xab7a617c, 0xb8e7, 0x4d8f,
+ 0x83, 0x01, 0xd0, 0x9b, 0x61, 0x03, 0x6b, 0x64)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(tee, optee_rng_id_table);
+
+static struct tee_client_driver optee_rng_driver = {
+ .id_table = optee_rng_id_table,
+ .driver = {
+ .name = DRIVER_NAME,
+ .bus = &tee_bus_type,
+ .probe = optee_rng_probe,
+ .remove = optee_rng_remove,
+ },
+};
+
+static int __init optee_rng_mod_init(void)
+{
+ return driver_register(&optee_rng_driver.driver);
+}
+
+static void __exit optee_rng_mod_exit(void)
+{
+ driver_unregister(&optee_rng_driver.driver);
+}
+
+module_init(optee_rng_mod_init);
+module_exit(optee_rng_mod_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sumit Garg <sumit.garg@linaro.org>");
+MODULE_DESCRIPTION("OP-TEE based random number generator driver");
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c
index b89df66ea1ae..7abd604e938c 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -73,7 +73,7 @@ static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
if (!vi->busy) {
vi->busy = true;
- init_completion(&vi->have_data);
+ reinit_completion(&vi->have_data);
register_buffer(vi, buf, size);
}
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index c108441882cc..94719fc6ff9d 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -18,6 +18,10 @@ menuconfig IPMI_HANDLER
If unsure, say N.
config IPMI_DMI_DECODE
+ select IPMI_PLAT_DATA
+ bool
+
+config IPMI_PLAT_DATA
bool
if IPMI_HANDLER
@@ -56,6 +60,7 @@ config IPMI_DEVICE_INTERFACE
config IPMI_SI
tristate 'IPMI System Interface handler'
+ select IPMI_PLAT_DATA
help
Provides a driver for System Interfaces (KCS, SMIC, BT).
Currently, only KCS and SMIC are supported. If
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index 7a3baf301a8f..3f06b2062475 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o
obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
obj-$(CONFIG_IPMI_SI) += ipmi_si.o
obj-$(CONFIG_IPMI_DMI_DECODE) += ipmi_dmi.o
+obj-$(CONFIG_IPMI_PLAT_DATA) += ipmi_plat_data.o
obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c
index effab11887ca..99c9f581a1f3 100644
--- a/drivers/char/ipmi/ipmi_devintf.c
+++ b/drivers/char/ipmi/ipmi_devintf.c
@@ -207,7 +207,7 @@ static int handle_recv(struct ipmi_file_private *priv,
struct list_head *entry;
struct ipmi_recv_msg *msg;
unsigned long flags;
- int rv = 0;
+ int rv = 0, rv2 = 0;
/* We claim a mutex because we don't want two
users getting something from the queue at a time.
@@ -250,7 +250,7 @@ static int handle_recv(struct ipmi_file_private *priv,
if (msg->msg.data_len > 0) {
if (rsp->msg.data_len < msg->msg.data_len) {
- rv = -EMSGSIZE;
+ rv2 = -EMSGSIZE;
if (trunc)
msg->msg.data_len = rsp->msg.data_len;
else
@@ -274,7 +274,7 @@ static int handle_recv(struct ipmi_file_private *priv,
mutex_unlock(&priv->recv_mutex);
ipmi_free_recv_msg(msg);
- return 0;
+ return rv2;
recv_putback_on_err:
/* If we got an error, put the message back onto
diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c
index 249880457b17..ff0b199be472 100644
--- a/drivers/char/ipmi/ipmi_dmi.c
+++ b/drivers/char/ipmi/ipmi_dmi.c
@@ -14,6 +14,7 @@
#include <linux/property.h>
#include "ipmi_si_sm.h"
#include "ipmi_dmi.h"
+#include "ipmi_plat_data.h"
#define IPMI_DMI_TYPE_KCS 0x01
#define IPMI_DMI_TYPE_SMIC 0x02
@@ -22,7 +23,7 @@
struct ipmi_dmi_info {
enum si_type si_type;
- u32 flags;
+ unsigned int space; /* addr space for si, intf# for ssif */
unsigned long addr;
u8 slave_addr;
struct ipmi_dmi_info *next;
@@ -33,133 +34,60 @@ static struct ipmi_dmi_info *ipmi_dmi_infos;
static int ipmi_dmi_nr __initdata;
static void __init dmi_add_platform_ipmi(unsigned long base_addr,
- u32 flags,
+ unsigned int space,
u8 slave_addr,
int irq,
int offset,
int type)
{
- struct platform_device *pdev;
- struct resource r[4];
- unsigned int num_r = 1, size;
- struct property_entry p[5];
- unsigned int pidx = 0;
- char *name;
- int rv;
- enum si_type si_type;
+ const char *name;
struct ipmi_dmi_info *info;
+ struct ipmi_plat_data p;
- memset(p, 0, sizeof(p));
+ memset(&p, 0, sizeof(p));
name = "dmi-ipmi-si";
switch (type) {
case IPMI_DMI_TYPE_SSIF:
name = "dmi-ipmi-ssif";
- offset = 1;
- size = 1;
- si_type = SI_TYPE_INVALID;
+ p.type = SI_TYPE_INVALID;
break;
case IPMI_DMI_TYPE_BT:
- size = 3;
- si_type = SI_BT;
+ p.type = SI_BT;
break;
case IPMI_DMI_TYPE_KCS:
- size = 2;
- si_type = SI_KCS;
+ p.type = SI_KCS;
break;
case IPMI_DMI_TYPE_SMIC:
- size = 2;
- si_type = SI_SMIC;
+ p.type = SI_SMIC;
break;
default:
pr_err("Invalid IPMI type: %d\n", type);
return;
}
- if (si_type != SI_TYPE_INVALID)
- p[pidx++] = PROPERTY_ENTRY_U8("ipmi-type", si_type);
-
- p[pidx++] = PROPERTY_ENTRY_U8("slave-addr", slave_addr);
- p[pidx++] = PROPERTY_ENTRY_U8("addr-source", SI_SMBIOS);
+ memset(&p, 0, sizeof(p));
+ p.addr = base_addr;
+ p.space = space;
+ p.regspacing = offset;
+ p.irq = irq;
+ p.slave_addr = slave_addr;
+ p.addr_source = SI_SMBIOS;
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
pr_warn("Could not allocate dmi info\n");
} else {
- info->si_type = si_type;
- info->flags = flags;
+ info->si_type = p.type;
+ info->space = space;
info->addr = base_addr;
info->slave_addr = slave_addr;
info->next = ipmi_dmi_infos;
ipmi_dmi_infos = info;
}
- pdev = platform_device_alloc(name, ipmi_dmi_nr);
- if (!pdev) {
- pr_err("Error allocation IPMI platform device\n");
- return;
- }
-
- if (type == IPMI_DMI_TYPE_SSIF) {
- p[pidx++] = PROPERTY_ENTRY_U16("i2c-addr", base_addr);
- goto add_properties;
- }
-
- memset(r, 0, sizeof(r));
-
- r[0].start = base_addr;
- r[0].end = r[0].start + offset - 1;
- r[0].name = "IPMI Address 1";
- r[0].flags = flags;
-
- if (size > 1) {
- r[1].start = r[0].start + offset;
- r[1].end = r[1].start + offset - 1;
- r[1].name = "IPMI Address 2";
- r[1].flags = flags;
- num_r++;
- }
-
- if (size > 2) {
- r[2].start = r[1].start + offset;
- r[2].end = r[2].start + offset - 1;
- r[2].name = "IPMI Address 3";
- r[2].flags = flags;
- num_r++;
- }
-
- if (irq) {
- r[num_r].start = irq;
- r[num_r].end = irq;
- r[num_r].name = "IPMI IRQ";
- r[num_r].flags = IORESOURCE_IRQ;
- num_r++;
- }
-
- rv = platform_device_add_resources(pdev, r, num_r);
- if (rv) {
- dev_err(&pdev->dev, "Unable to add resources: %d\n", rv);
- goto err;
- }
-
-add_properties:
- rv = platform_device_add_properties(pdev, p);
- if (rv) {
- dev_err(&pdev->dev, "Unable to add properties: %d\n", rv);
- goto err;
- }
-
- rv = platform_device_add(pdev);
- if (rv) {
- dev_err(&pdev->dev, "Unable to add device: %d\n", rv);
- goto err;
- }
-
- ipmi_dmi_nr++;
- return;
-
-err:
- platform_device_put(pdev);
+ if (ipmi_platform_add(name, ipmi_dmi_nr, &p))
+ ipmi_dmi_nr++;
}
/*
@@ -169,14 +97,14 @@ err:
* This function allows an ACPI-specified IPMI device to look up the
* slave address from the DMI table.
*/
-int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags,
+int ipmi_dmi_get_slave_addr(enum si_type si_type, unsigned int space,
unsigned long base_addr)
{
struct ipmi_dmi_info *info = ipmi_dmi_infos;
while (info) {
if (info->si_type == si_type &&
- info->flags == flags &&
+ info->space == space &&
info->addr == base_addr)
return info->slave_addr;
info = info->next;
@@ -197,13 +125,13 @@ EXPORT_SYMBOL(ipmi_dmi_get_slave_addr);
static void __init dmi_decode_ipmi(const struct dmi_header *dm)
{
- const u8 *data = (const u8 *) dm;
- u32 flags = IORESOURCE_IO;
- unsigned long base_addr;
- u8 len = dm->length;
- u8 slave_addr;
- int irq = 0, offset;
- int type;
+ const u8 *data = (const u8 *) dm;
+ int space = IPMI_IO_ADDR_SPACE;
+ unsigned long base_addr;
+ u8 len = dm->length;
+ u8 slave_addr;
+ int irq = 0, offset = 0;
+ int type;
if (len < DMI_IPMI_MIN_LENGTH)
return;
@@ -218,8 +146,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
}
if (len >= DMI_IPMI_VER2_LENGTH) {
if (type == IPMI_DMI_TYPE_SSIF) {
- offset = 0;
- flags = 0;
+ space = 0; /* Match I2C interface 0. */
base_addr = data[DMI_IPMI_ADDR] >> 1;
if (base_addr == 0) {
/*
@@ -236,7 +163,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
base_addr &= DMI_IPMI_IO_MASK;
} else {
/* Memory */
- flags = IORESOURCE_MEM;
+ space = IPMI_MEM_ADDR_SPACE;
}
/*
@@ -280,7 +207,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
offset = 1;
}
- dmi_add_platform_ipmi(base_addr, flags, slave_addr, irq,
+ dmi_add_platform_ipmi(base_addr, space, slave_addr, irq,
offset, type);
}
diff --git a/drivers/char/ipmi/ipmi_dmi.h b/drivers/char/ipmi/ipmi_dmi.h
index 8d2b094db8e6..2dbec0461d0c 100644
--- a/drivers/char/ipmi/ipmi_dmi.h
+++ b/drivers/char/ipmi/ipmi_dmi.h
@@ -4,6 +4,6 @@
*/
#ifdef CONFIG_IPMI_DMI_DECODE
-int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags,
+int ipmi_dmi_get_slave_addr(enum si_type si_type, unsigned int space,
unsigned long base_addr);
#endif
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index c518659b4d9f..e8ba67834746 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -529,9 +529,27 @@ struct ipmi_smi {
unsigned int waiting_events_count; /* How many events in queue? */
char delivering_events;
char event_msg_printed;
+
+ /* How many users are waiting for events? */
atomic_t event_waiters;
unsigned int ticks_to_req_ev;
- int last_needs_timer;
+
+ spinlock_t watch_lock; /* For dealing with watch stuff below. */
+
+ /* How many users are waiting for commands? */
+ unsigned int command_waiters;
+
+ /* How many users are waiting for watchdogs? */
+ unsigned int watchdog_waiters;
+
+ /* How many users are waiting for message responses? */
+ unsigned int response_waiters;
+
+ /*
+ * Tells what the lower layer has last been asked to watch for,
+ * messages and/or watchdogs. Protected by watch_lock.
+ */
+ unsigned int last_watch_mask;
/*
* The event receiver for my BMC, only really used at panic
@@ -925,6 +943,64 @@ static void deliver_err_response(struct ipmi_smi *intf,
deliver_local_response(intf, msg);
}
+static void smi_add_watch(struct ipmi_smi *intf, unsigned int flags)
+{
+ unsigned long iflags;
+
+ if (!intf->handlers->set_need_watch)
+ return;
+
+ spin_lock_irqsave(&intf->watch_lock, iflags);
+ if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES)
+ intf->response_waiters++;
+
+ if (flags & IPMI_WATCH_MASK_CHECK_WATCHDOG)
+ intf->watchdog_waiters++;
+
+ if (flags & IPMI_WATCH_MASK_CHECK_COMMANDS)
+ intf->command_waiters++;
+
+ if ((intf->last_watch_mask & flags) != flags) {
+ intf->last_watch_mask |= flags;
+ intf->handlers->set_need_watch(intf->send_info,
+ intf->last_watch_mask);
+ }
+ spin_unlock_irqrestore(&intf->watch_lock, iflags);
+}
+
+static void smi_remove_watch(struct ipmi_smi *intf, unsigned int flags)
+{
+ unsigned long iflags;
+
+ if (!intf->handlers->set_need_watch)
+ return;
+
+ spin_lock_irqsave(&intf->watch_lock, iflags);
+ if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES)
+ intf->response_waiters--;
+
+ if (flags & IPMI_WATCH_MASK_CHECK_WATCHDOG)
+ intf->watchdog_waiters--;
+
+ if (flags & IPMI_WATCH_MASK_CHECK_COMMANDS)
+ intf->command_waiters--;
+
+ flags = 0;
+ if (intf->response_waiters)
+ flags |= IPMI_WATCH_MASK_CHECK_MESSAGES;
+ if (intf->watchdog_waiters)
+ flags |= IPMI_WATCH_MASK_CHECK_WATCHDOG;
+ if (intf->command_waiters)
+ flags |= IPMI_WATCH_MASK_CHECK_COMMANDS;
+
+ if (intf->last_watch_mask != flags) {
+ intf->last_watch_mask = flags;
+ intf->handlers->set_need_watch(intf->send_info,
+ intf->last_watch_mask);
+ }
+ spin_unlock_irqrestore(&intf->watch_lock, iflags);
+}
+
/*
* Find the next sequence number not being used and add the given
* message with the given timeout to the sequence table. This must be
@@ -968,6 +1044,7 @@ static int intf_next_seq(struct ipmi_smi *intf,
*seq = i;
*seqid = intf->seq_table[i].seqid;
intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ;
+ smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
need_waiter(intf);
} else {
rv = -EAGAIN;
@@ -1006,6 +1083,7 @@ static int intf_find_seq(struct ipmi_smi *intf,
&& (ipmi_addr_equal(addr, &msg->addr))) {
*recv_msg = msg;
intf->seq_table[seq].inuse = 0;
+ smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
rv = 0;
}
}
@@ -1067,6 +1145,7 @@ static int intf_err_seq(struct ipmi_smi *intf,
struct seq_table *ent = &intf->seq_table[seq];
ent->inuse = 0;
+ smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
msg = ent->recv_msg;
rv = 0;
}
@@ -1078,7 +1157,6 @@ static int intf_err_seq(struct ipmi_smi *intf,
return rv;
}
-
int ipmi_create_user(unsigned int if_num,
const struct ipmi_user_hndl *handler,
void *handler_data,
@@ -1139,11 +1217,9 @@ int ipmi_create_user(unsigned int if_num,
spin_lock_irqsave(&intf->seq_lock, flags);
list_add_rcu(&new_user->link, &intf->users);
spin_unlock_irqrestore(&intf->seq_lock, flags);
- if (handler->ipmi_watchdog_pretimeout) {
+ if (handler->ipmi_watchdog_pretimeout)
/* User wants pretimeouts, so make sure to watch for them. */
- if (atomic_inc_return(&intf->event_waiters) == 1)
- need_waiter(intf);
- }
+ smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG);
srcu_read_unlock(&ipmi_interfaces_srcu, index);
*user = new_user;
return 0;
@@ -1214,7 +1290,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user)
user->handler->shutdown(user->handler_data);
if (user->handler->ipmi_watchdog_pretimeout)
- atomic_dec(&intf->event_waiters);
+ smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG);
if (user->gets_events)
atomic_dec(&intf->event_waiters);
@@ -1227,6 +1303,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user)
if (intf->seq_table[i].inuse
&& (intf->seq_table[i].recv_msg->user == user)) {
intf->seq_table[i].inuse = 0;
+ smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
ipmi_free_recv_msg(intf->seq_table[i].recv_msg);
}
}
@@ -1569,8 +1646,7 @@ int ipmi_register_for_cmd(struct ipmi_user *user,
goto out_unlock;
}
- if (atomic_inc_return(&intf->event_waiters) == 1)
- need_waiter(intf);
+ smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS);
list_add_rcu(&rcvr->link, &intf->cmd_rcvrs);
@@ -1620,7 +1696,7 @@ int ipmi_unregister_for_cmd(struct ipmi_user *user,
synchronize_rcu();
release_ipmi_user(user, index);
while (rcvrs) {
- atomic_dec(&intf->event_waiters);
+ smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS);
rcvr = rcvrs;
rcvrs = rcvr->next;
kfree(rcvr);
@@ -1737,22 +1813,19 @@ static struct ipmi_smi_msg *smi_add_send_msg(struct ipmi_smi *intf,
return smi_msg;
}
-
static void smi_send(struct ipmi_smi *intf,
const struct ipmi_smi_handlers *handlers,
struct ipmi_smi_msg *smi_msg, int priority)
{
int run_to_completion = intf->run_to_completion;
+ unsigned long flags = 0;
- if (run_to_completion) {
- smi_msg = smi_add_send_msg(intf, smi_msg, priority);
- } else {
- unsigned long flags;
-
+ if (!run_to_completion)
spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
- smi_msg = smi_add_send_msg(intf, smi_msg, priority);
+ smi_msg = smi_add_send_msg(intf, smi_msg, priority);
+
+ if (!run_to_completion)
spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
- }
if (smi_msg)
handlers->sender(intf->send_info, smi_msg);
@@ -2676,7 +2749,7 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
if (!guid_set)
return -ENOENT;
- return snprintf(buf, 38, "%pUl\n", guid.b);
+ return snprintf(buf, UUID_STRING_LEN + 1 + 1, "%pUl\n", &guid);
}
static DEVICE_ATTR_RO(guid);
@@ -3075,15 +3148,15 @@ static void guid_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
goto out;
}
- if (msg->msg.data_len < 17) {
+ if (msg->msg.data_len < UUID_SIZE + 1) {
bmc->dyn_guid_set = 0;
dev_warn(intf->si_dev,
- "The GUID response from the BMC was too short, it was %d but should have been 17. Assuming GUID is not available.\n",
- msg->msg.data_len);
+ "The GUID response from the BMC was too short, it was %d but should have been %d. Assuming GUID is not available.\n",
+ msg->msg.data_len, UUID_SIZE + 1);
goto out;
}
- memcpy(bmc->fetch_guid.b, msg->msg.data + 1, 16);
+ guid_copy(&bmc->fetch_guid, (guid_t *)(msg->msg.data + 1));
/*
* Make sure the guid data is available before setting
* dyn_guid_set.
@@ -3350,6 +3423,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
INIT_LIST_HEAD(&intf->xmit_msgs);
INIT_LIST_HEAD(&intf->hp_xmit_msgs);
spin_lock_init(&intf->events_lock);
+ spin_lock_init(&intf->watch_lock);
atomic_set(&intf->event_waiters, 0);
intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
INIT_LIST_HEAD(&intf->waiting_events);
@@ -4365,6 +4439,7 @@ static void smi_recv_tasklet(unsigned long val)
intf->curr_msg = newmsg;
}
}
+
if (!run_to_completion)
spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
if (newmsg)
@@ -4492,7 +4567,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
struct list_head *timeouts,
unsigned long timeout_period,
int slot, unsigned long *flags,
- unsigned int *waiting_msgs)
+ bool *need_timer)
{
struct ipmi_recv_msg *msg;
@@ -4504,13 +4579,14 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
if (timeout_period < ent->timeout) {
ent->timeout -= timeout_period;
- (*waiting_msgs)++;
+ *need_timer = true;
return;
}
if (ent->retries_left == 0) {
/* The message has used all its retries. */
ent->inuse = 0;
+ smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
msg = ent->recv_msg;
list_add_tail(&msg->link, timeouts);
if (ent->broadcast)
@@ -4523,7 +4599,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
struct ipmi_smi_msg *smi_msg;
/* More retries, send again. */
- (*waiting_msgs)++;
+ *need_timer = true;
/*
* Start with the max timer, set to normal timer after
@@ -4568,20 +4644,20 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
}
}
-static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf,
- unsigned long timeout_period)
+static bool ipmi_timeout_handler(struct ipmi_smi *intf,
+ unsigned long timeout_period)
{
struct list_head timeouts;
struct ipmi_recv_msg *msg, *msg2;
unsigned long flags;
int i;
- unsigned int waiting_msgs = 0;
+ bool need_timer = false;
if (!intf->bmc_registered) {
kref_get(&intf->refcount);
if (!schedule_work(&intf->bmc_reg_work)) {
kref_put(&intf->refcount, intf_free);
- waiting_msgs++;
+ need_timer = true;
}
}
@@ -4601,7 +4677,7 @@ static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf,
for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++)
check_msg_timeout(intf, &intf->seq_table[i],
&timeouts, timeout_period, i,
- &flags, &waiting_msgs);
+ &flags, &need_timer);
spin_unlock_irqrestore(&intf->seq_lock, flags);
list_for_each_entry_safe(msg, msg2, &timeouts, link)
@@ -4632,7 +4708,7 @@ static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf,
tasklet_schedule(&intf->recv_tasklet);
- return waiting_msgs;
+ return need_timer;
}
static void ipmi_request_event(struct ipmi_smi *intf)
@@ -4652,37 +4728,28 @@ static atomic_t stop_operation;
static void ipmi_timeout(struct timer_list *unused)
{
struct ipmi_smi *intf;
- int nt = 0, index;
+ bool need_timer = false;
+ int index;
if (atomic_read(&stop_operation))
return;
index = srcu_read_lock(&ipmi_interfaces_srcu);
list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
- int lnt = 0;
-
if (atomic_read(&intf->event_waiters)) {
intf->ticks_to_req_ev--;
if (intf->ticks_to_req_ev == 0) {
ipmi_request_event(intf);
intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
}
- lnt++;
+ need_timer = true;
}
- lnt += ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME);
-
- lnt = !!lnt;
- if (lnt != intf->last_needs_timer &&
- intf->handlers->set_need_watch)
- intf->handlers->set_need_watch(intf->send_info, lnt);
- intf->last_needs_timer = lnt;
-
- nt += lnt;
+ need_timer |= ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME);
}
srcu_read_unlock(&ipmi_interfaces_srcu, index);
- if (nt)
+ if (need_timer)
mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
}
diff --git a/drivers/char/ipmi/ipmi_plat_data.c b/drivers/char/ipmi/ipmi_plat_data.c
new file mode 100644
index 000000000000..8f0ca2a848eb
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_plat_data.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Add an IPMI platform device.
+ */
+
+#include <linux/platform_device.h>
+#include "ipmi_plat_data.h"
+#include "ipmi_si.h"
+
+struct platform_device *ipmi_platform_add(const char *name, unsigned int inst,
+ struct ipmi_plat_data *p)
+{
+ struct platform_device *pdev;
+ unsigned int num_r = 1, size, pidx = 0;
+ struct resource r[4];
+ struct property_entry pr[6];
+ u32 flags;
+ int rv;
+
+ memset(pr, 0, sizeof(pr));
+ memset(r, 0, sizeof(r));
+
+ if (p->type == SI_BT)
+ size = 3;
+ else if (p->type == SI_TYPE_INVALID)
+ size = 0;
+ else
+ size = 2;
+
+ if (p->regsize == 0)
+ p->regsize = DEFAULT_REGSIZE;
+ if (p->regspacing == 0)
+ p->regspacing = p->regsize;
+
+ pr[pidx++] = PROPERTY_ENTRY_U8("ipmi-type", p->type);
+ if (p->slave_addr)
+ pr[pidx++] = PROPERTY_ENTRY_U8("slave-addr", p->slave_addr);
+ pr[pidx++] = PROPERTY_ENTRY_U8("addr-source", p->addr_source);
+ if (p->regshift)
+ pr[pidx++] = PROPERTY_ENTRY_U8("reg-shift", p->regshift);
+ pr[pidx++] = PROPERTY_ENTRY_U8("reg-size", p->regsize);
+ /* Last entry must be left NULL to terminate it. */
+
+ pdev = platform_device_alloc(name, inst);
+ if (!pdev) {
+ pr_err("Error allocating IPMI platform device %s.%d\n",
+ name, inst);
+ return NULL;
+ }
+
+ if (size == 0)
+ /* An invalid or SSIF interface, no resources. */
+ goto add_properties;
+
+ /*
+ * Register spacing is derived from the resources in
+ * the IPMI platform code.
+ */
+
+ if (p->space == IPMI_IO_ADDR_SPACE)
+ flags = IORESOURCE_IO;
+ else
+ flags = IORESOURCE_MEM;
+
+ r[0].start = p->addr;
+ r[0].end = r[0].start + p->regsize - 1;
+ r[0].name = "IPMI Address 1";
+ r[0].flags = flags;
+
+ if (size > 1) {
+ r[1].start = r[0].start + p->regspacing;
+ r[1].end = r[1].start + p->regsize - 1;
+ r[1].name = "IPMI Address 2";
+ r[1].flags = flags;
+ num_r++;
+ }
+
+ if (size > 2) {
+ r[2].start = r[1].start + p->regspacing;
+ r[2].end = r[2].start + p->regsize - 1;
+ r[2].name = "IPMI Address 3";
+ r[2].flags = flags;
+ num_r++;
+ }
+
+ if (p->irq) {
+ r[num_r].start = p->irq;
+ r[num_r].end = p->irq;
+ r[num_r].name = "IPMI IRQ";
+ r[num_r].flags = IORESOURCE_IRQ;
+ num_r++;
+ }
+
+ rv = platform_device_add_resources(pdev, r, num_r);
+ if (rv) {
+ dev_err(&pdev->dev,
+ "Unable to add hard-code resources: %d\n", rv);
+ goto err;
+ }
+ add_properties:
+ rv = platform_device_add_properties(pdev, pr);
+ if (rv) {
+ dev_err(&pdev->dev,
+ "Unable to add hard-code properties: %d\n", rv);
+ goto err;
+ }
+
+ rv = platform_device_add(pdev);
+ if (rv) {
+ dev_err(&pdev->dev,
+ "Unable to add hard-code device: %d\n", rv);
+ goto err;
+ }
+ return pdev;
+
+err:
+ platform_device_put(pdev);
+ return NULL;
+}
+EXPORT_SYMBOL(ipmi_platform_add);
diff --git a/drivers/char/ipmi/ipmi_plat_data.h b/drivers/char/ipmi/ipmi_plat_data.h
new file mode 100644
index 000000000000..567cfcec8ada
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_plat_data.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+/*
+ * Generic code to add IPMI platform devices.
+ */
+
+#include <linux/ipmi.h>
+
+struct ipmi_plat_data {
+ unsigned int type; /* si_type for si, SI_INVALID for others */
+ unsigned int space; /* addr_space for si, intf# for ssif. */
+ unsigned long addr;
+ unsigned int regspacing;
+ unsigned int regsize;
+ unsigned int regshift;
+ unsigned int irq;
+ unsigned int slave_addr;
+ enum ipmi_addr_src addr_source;
+};
+
+struct platform_device *ipmi_platform_add(const char *name, unsigned int inst,
+ struct ipmi_plat_data *p);
diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h
index 52f6152d1fcb..357a229c9012 100644
--- a/drivers/char/ipmi/ipmi_si.h
+++ b/drivers/char/ipmi/ipmi_si.h
@@ -7,11 +7,9 @@
*/
#include <linux/interrupt.h>
+#include <linux/platform_device.h>
#include "ipmi_si_sm.h"
-#define IPMI_IO_ADDR_SPACE 0
-#define IPMI_MEM_ADDR_SPACE 1
-
#define DEFAULT_REGSPACING 1
#define DEFAULT_REGSIZE 1
@@ -23,11 +21,15 @@ void ipmi_irq_start_cleanup(struct si_sm_io *io);
int ipmi_std_irq_setup(struct si_sm_io *io);
void ipmi_irq_finish_setup(struct si_sm_io *io);
int ipmi_si_remove_by_dev(struct device *dev);
-void ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
- unsigned long addr);
-int ipmi_si_hardcode_find_bmc(void);
+struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
+ unsigned long addr);
+void ipmi_hardcode_init(void);
+void ipmi_si_hardcode_exit(void);
+void ipmi_si_hotmod_exit(void);
+int ipmi_si_hardcode_match(int addr_space, unsigned long addr);
void ipmi_si_platform_init(void);
void ipmi_si_platform_shutdown(void);
+void ipmi_remove_platform_device_by_name(char *name);
extern struct platform_driver ipmi_platform_driver;
diff --git a/drivers/char/ipmi/ipmi_si_hardcode.c b/drivers/char/ipmi/ipmi_si_hardcode.c
index 487642809c58..01946cad3d13 100644
--- a/drivers/char/ipmi/ipmi_si_hardcode.c
+++ b/drivers/char/ipmi/ipmi_si_hardcode.c
@@ -3,7 +3,9 @@
#define pr_fmt(fmt) "ipmi_hardcode: " fmt
#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
#include "ipmi_si.h"
+#include "ipmi_plat_data.h"
/*
* There can be 4 IO ports passed in (with or without IRQs), 4 addresses,
@@ -12,23 +14,22 @@
#define SI_MAX_PARMS 4
-static char *si_type[SI_MAX_PARMS];
#define MAX_SI_TYPE_STR 30
-static char si_type_str[MAX_SI_TYPE_STR];
+static char si_type_str[MAX_SI_TYPE_STR] __initdata;
static unsigned long addrs[SI_MAX_PARMS];
static unsigned int num_addrs;
static unsigned int ports[SI_MAX_PARMS];
static unsigned int num_ports;
-static int irqs[SI_MAX_PARMS];
-static unsigned int num_irqs;
-static int regspacings[SI_MAX_PARMS];
-static unsigned int num_regspacings;
-static int regsizes[SI_MAX_PARMS];
-static unsigned int num_regsizes;
-static int regshifts[SI_MAX_PARMS];
-static unsigned int num_regshifts;
-static int slave_addrs[SI_MAX_PARMS]; /* Leaving 0 chooses the default value */
-static unsigned int num_slave_addrs;
+static int irqs[SI_MAX_PARMS] __initdata;
+static unsigned int num_irqs __initdata;
+static int regspacings[SI_MAX_PARMS] __initdata;
+static unsigned int num_regspacings __initdata;
+static int regsizes[SI_MAX_PARMS] __initdata;
+static unsigned int num_regsizes __initdata;
+static int regshifts[SI_MAX_PARMS] __initdata;
+static unsigned int num_regshifts __initdata;
+static int slave_addrs[SI_MAX_PARMS] __initdata;
+static unsigned int num_slave_addrs __initdata;
module_param_string(type, si_type_str, MAX_SI_TYPE_STR, 0);
MODULE_PARM_DESC(type, "Defines the type of each interface, each"
@@ -73,12 +74,49 @@ MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for"
" overridden by this parm. This is an array indexed"
" by interface number.");
-int ipmi_si_hardcode_find_bmc(void)
+static void __init ipmi_hardcode_init_one(const char *si_type_str,
+ unsigned int i,
+ unsigned long addr,
+ enum ipmi_addr_space addr_space)
{
- int ret = -ENODEV;
- int i;
- struct si_sm_io io;
+ struct ipmi_plat_data p;
+
+ memset(&p, 0, sizeof(p));
+
+ if (!si_type_str || !*si_type_str || strcmp(si_type_str, "kcs") == 0) {
+ p.type = SI_KCS;
+ } else if (strcmp(si_type_str, "smic") == 0) {
+ p.type = SI_SMIC;
+ } else if (strcmp(si_type_str, "bt") == 0) {
+ p.type = SI_BT;
+ } else if (strcmp(si_type_str, "invalid") == 0) {
+ /*
+ * Allow a firmware-specified interface to be
+ * disabled.
+ */
+ p.type = SI_TYPE_INVALID;
+ } else {
+ pr_warn("Interface type specified for interface %d, was invalid: %s\n",
+ i, si_type_str);
+ return;
+ }
+
+ p.regsize = regsizes[i];
+ p.slave_addr = slave_addrs[i];
+ p.addr_source = SI_HARDCODED;
+ p.regshift = regshifts[i];
+ p.regsize = regsizes[i];
+ p.addr = addr;
+ p.space = addr_space;
+
+ ipmi_platform_add("hardcode-ipmi-si", i, &p);
+}
+
+void __init ipmi_hardcode_init(void)
+{
+ unsigned int i;
char *str;
+ char *si_type[SI_MAX_PARMS];
/* Parse out the si_type string into its components. */
str = si_type_str;
@@ -95,54 +133,41 @@ int ipmi_si_hardcode_find_bmc(void)
}
}
- memset(&io, 0, sizeof(io));
for (i = 0; i < SI_MAX_PARMS; i++) {
- if (!ports[i] && !addrs[i])
- continue;
-
- io.addr_source = SI_HARDCODED;
- pr_info("probing via hardcoded address\n");
-
- if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) {
- io.si_type = SI_KCS;
- } else if (strcmp(si_type[i], "smic") == 0) {
- io.si_type = SI_SMIC;
- } else if (strcmp(si_type[i], "bt") == 0) {
- io.si_type = SI_BT;
- } else {
- pr_warn("Interface type specified for interface %d, was invalid: %s\n",
- i, si_type[i]);
- continue;
- }
+ if (i < num_ports && ports[i])
+ ipmi_hardcode_init_one(si_type[i], i, ports[i],
+ IPMI_IO_ADDR_SPACE);
+ if (i < num_addrs && addrs[i])
+ ipmi_hardcode_init_one(si_type[i], i, addrs[i],
+ IPMI_MEM_ADDR_SPACE);
+ }
+}
- if (ports[i]) {
- /* An I/O port */
- io.addr_data = ports[i];
- io.addr_type = IPMI_IO_ADDR_SPACE;
- } else if (addrs[i]) {
- /* A memory port */
- io.addr_data = addrs[i];
- io.addr_type = IPMI_MEM_ADDR_SPACE;
- } else {
- pr_warn("Interface type specified for interface %d, but port and address were not set or set to zero\n",
- i);
- continue;
- }
- io.addr = NULL;
- io.regspacing = regspacings[i];
- if (!io.regspacing)
- io.regspacing = DEFAULT_REGSPACING;
- io.regsize = regsizes[i];
- if (!io.regsize)
- io.regsize = DEFAULT_REGSIZE;
- io.regshift = regshifts[i];
- io.irq = irqs[i];
- if (io.irq)
- io.irq_setup = ipmi_std_irq_setup;
- io.slave_addr = slave_addrs[i];
-
- ret = ipmi_si_add_smi(&io);
+void ipmi_si_hardcode_exit(void)
+{
+ ipmi_remove_platform_device_by_name("hardcode-ipmi-si");
+}
+
+/*
+ * Returns true of the given address exists as a hardcoded address,
+ * false if not.
+ */
+int ipmi_si_hardcode_match(int addr_space, unsigned long addr)
+{
+ unsigned int i;
+
+ if (addr_space == IPMI_IO_ADDR_SPACE) {
+ for (i = 0; i < num_ports; i++) {
+ if (ports[i] == addr)
+ return 1;
+ }
+ } else {
+ for (i = 0; i < num_addrs; i++) {
+ if (addrs[i] == addr)
+ return 1;
+ }
}
- return ret;
+
+ return 0;
}
diff --git a/drivers/char/ipmi/ipmi_si_hotmod.c b/drivers/char/ipmi/ipmi_si_hotmod.c
index c0067fd0480d..03140f6cdf6f 100644
--- a/drivers/char/ipmi/ipmi_si_hotmod.c
+++ b/drivers/char/ipmi/ipmi_si_hotmod.c
@@ -10,7 +10,9 @@
#include <linux/moduleparam.h>
#include <linux/ipmi.h>
+#include <linux/atomic.h>
#include "ipmi_si.h"
+#include "ipmi_plat_data.h"
static int hotmod_handler(const char *val, const struct kernel_param *kp);
@@ -54,8 +56,8 @@ static const struct hotmod_vals hotmod_as[] = {
{ NULL }
};
-static int parse_str(const struct hotmod_vals *v, int *val, char *name,
- char **curr)
+static int parse_str(const struct hotmod_vals *v, unsigned int *val, char *name,
+ const char **curr)
{
char *s;
int i;
@@ -80,7 +82,7 @@ static int parse_str(const struct hotmod_vals *v, int *val, char *name,
}
static int check_hotmod_int_op(const char *curr, const char *option,
- const char *name, int *val)
+ const char *name, unsigned int *val)
{
char *n;
@@ -99,22 +101,94 @@ static int check_hotmod_int_op(const char *curr, const char *option,
return 0;
}
+static int parse_hotmod_str(const char *curr, enum hotmod_op *op,
+ struct ipmi_plat_data *h)
+{
+ char *s, *o;
+ int rv;
+ unsigned int ival;
+
+ rv = parse_str(hotmod_ops, &ival, "operation", &curr);
+ if (rv)
+ return rv;
+ *op = ival;
+
+ rv = parse_str(hotmod_si, &ival, "interface type", &curr);
+ if (rv)
+ return rv;
+ h->type = ival;
+
+ rv = parse_str(hotmod_as, &ival, "address space", &curr);
+ if (rv)
+ return rv;
+ h->space = ival;
+
+ s = strchr(curr, ',');
+ if (s) {
+ *s = '\0';
+ s++;
+ }
+ rv = kstrtoul(curr, 0, &h->addr);
+ if (rv) {
+ pr_warn("Invalid hotmod address '%s': %d\n", curr, rv);
+ return rv;
+ }
+
+ while (s) {
+ curr = s;
+ s = strchr(curr, ',');
+ if (s) {
+ *s = '\0';
+ s++;
+ }
+ o = strchr(curr, '=');
+ if (o) {
+ *o = '\0';
+ o++;
+ }
+ rv = check_hotmod_int_op(curr, o, "rsp", &h->regspacing);
+ if (rv < 0)
+ return rv;
+ else if (rv)
+ continue;
+ rv = check_hotmod_int_op(curr, o, "rsi", &h->regsize);
+ if (rv < 0)
+ return rv;
+ else if (rv)
+ continue;
+ rv = check_hotmod_int_op(curr, o, "rsh", &h->regshift);
+ if (rv < 0)
+ return rv;
+ else if (rv)
+ continue;
+ rv = check_hotmod_int_op(curr, o, "irq", &h->irq);
+ if (rv < 0)
+ return rv;
+ else if (rv)
+ continue;
+ rv = check_hotmod_int_op(curr, o, "ipmb", &h->slave_addr);
+ if (rv < 0)
+ return rv;
+ else if (rv)
+ continue;
+
+ pr_warn("Invalid hotmod option '%s'\n", curr);
+ return -EINVAL;
+ }
+
+ h->addr_source = SI_HOTMOD;
+ return 0;
+}
+
+static atomic_t hotmod_nr;
+
static int hotmod_handler(const char *val, const struct kernel_param *kp)
{
- char *str = kstrdup(val, GFP_KERNEL);
+ char *str = kstrdup(val, GFP_KERNEL), *curr, *next;
int rv;
- char *next, *curr, *s, *n, *o;
- enum hotmod_op op;
- enum si_type si_type;
- int addr_space;
- unsigned long addr;
- int regspacing;
- int regsize;
- int regshift;
- int irq;
- int ipmb;
+ struct ipmi_plat_data h;
+ unsigned int len;
int ival;
- int len;
if (!str)
return -ENOMEM;
@@ -128,11 +202,7 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp)
}
for (curr = str; curr; curr = next) {
- regspacing = 1;
- regsize = 1;
- regshift = 0;
- irq = 0;
- ipmb = 0; /* Choose the default if not specified */
+ enum hotmod_op op;
next = strchr(curr, ':');
if (next) {
@@ -140,101 +210,28 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp)
next++;
}
- rv = parse_str(hotmod_ops, &ival, "operation", &curr);
- if (rv)
- break;
- op = ival;
-
- rv = parse_str(hotmod_si, &ival, "interface type", &curr);
+ memset(&h, 0, sizeof(h));
+ rv = parse_hotmod_str(curr, &op, &h);
if (rv)
- break;
- si_type = ival;
-
- rv = parse_str(hotmod_as, &addr_space, "address space", &curr);
- if (rv)
- break;
-
- s = strchr(curr, ',');
- if (s) {
- *s = '\0';
- s++;
- }
- addr = simple_strtoul(curr, &n, 0);
- if ((*n != '\0') || (*curr == '\0')) {
- pr_warn("Invalid hotmod address '%s'\n", curr);
- break;
- }
-
- while (s) {
- curr = s;
- s = strchr(curr, ',');
- if (s) {
- *s = '\0';
- s++;
- }
- o = strchr(curr, '=');
- if (o) {
- *o = '\0';
- o++;
- }
- rv = check_hotmod_int_op(curr, o, "rsp", &regspacing);
- if (rv < 0)
- goto out;
- else if (rv)
- continue;
- rv = check_hotmod_int_op(curr, o, "rsi", &regsize);
- if (rv < 0)
- goto out;
- else if (rv)
- continue;
- rv = check_hotmod_int_op(curr, o, "rsh", &regshift);
- if (rv < 0)
- goto out;
- else if (rv)
- continue;
- rv = check_hotmod_int_op(curr, o, "irq", &irq);
- if (rv < 0)
- goto out;
- else if (rv)
- continue;
- rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb);
- if (rv < 0)
- goto out;
- else if (rv)
- continue;
-
- rv = -EINVAL;
- pr_warn("Invalid hotmod option '%s'\n", curr);
goto out;
- }
if (op == HM_ADD) {
- struct si_sm_io io;
-
- memset(&io, 0, sizeof(io));
- io.addr_source = SI_HOTMOD;
- io.si_type = si_type;
- io.addr_data = addr;
- io.addr_type = addr_space;
-
- io.addr = NULL;
- io.regspacing = regspacing;
- if (!io.regspacing)
- io.regspacing = DEFAULT_REGSPACING;
- io.regsize = regsize;
- if (!io.regsize)
- io.regsize = DEFAULT_REGSIZE;
- io.regshift = regshift;
- io.irq = irq;
- if (io.irq)
- io.irq_setup = ipmi_std_irq_setup;
- io.slave_addr = ipmb;
-
- rv = ipmi_si_add_smi(&io);
- if (rv)
- goto out;
+ ipmi_platform_add("hotmod-ipmi-si",
+ atomic_inc_return(&hotmod_nr),
+ &h);
} else {
- ipmi_si_remove_by_data(addr_space, si_type, addr);
+ struct device *dev;
+
+ dev = ipmi_si_remove_by_data(h.space, h.type, h.addr);
+ if (dev && dev_is_platform(dev)) {
+ struct platform_device *pdev;
+
+ pdev = to_platform_device(dev);
+ if (strcmp(pdev->name, "hotmod-ipmi-si") == 0)
+ platform_device_unregister(pdev);
+ }
+ if (dev)
+ put_device(dev);
}
}
rv = len;
@@ -242,3 +239,8 @@ out:
kfree(str);
return rv;
}
+
+void ipmi_si_hotmod_exit(void)
+{
+ ipmi_remove_platform_device_by_name("hotmod-ipmi-si");
+}
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index dc8603d34320..b1732882b97e 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -229,15 +229,9 @@ struct smi_info {
/* From the get device id response... */
struct ipmi_device_id device_id;
- /* Default driver model device. */
- struct platform_device *pdev;
-
/* Have we added the device group to the device? */
bool dev_group_added;
- /* Have we added the platform device? */
- bool pdev_registered;
-
/* Counters and things for the proc filesystem. */
atomic_t stats[SI_NUM_STATS];
@@ -1060,10 +1054,13 @@ static void request_events(void *send_info)
atomic_set(&smi_info->req_events, 1);
}
-static void set_need_watch(void *send_info, bool enable)
+static void set_need_watch(void *send_info, unsigned int watch_mask)
{
struct smi_info *smi_info = send_info;
unsigned long flags;
+ int enable;
+
+ enable = !!watch_mask;
atomic_set(&smi_info->need_watch, enable);
spin_lock_irqsave(&smi_info->si_lock, flags);
@@ -1642,7 +1639,7 @@ static ssize_t ipmi_params_show(struct device *dev,
return snprintf(buf, 200,
"%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n",
si_to_str[smi_info->io.si_type],
- addr_space_to_str[smi_info->io.addr_type],
+ addr_space_to_str[smi_info->io.addr_space],
smi_info->io.addr_data,
smi_info->io.regspacing,
smi_info->io.regsize,
@@ -1840,7 +1837,7 @@ static struct smi_info *find_dup_si(struct smi_info *info)
struct smi_info *e;
list_for_each_entry(e, &smi_infos, link) {
- if (e->io.addr_type != info->io.addr_type)
+ if (e->io.addr_space != info->io.addr_space)
continue;
if (e->io.addr_data == info->io.addr_data) {
/*
@@ -1862,10 +1859,22 @@ int ipmi_si_add_smi(struct si_sm_io *io)
int rv = 0;
struct smi_info *new_smi, *dup;
+ /*
+ * If the user gave us a hard-coded device at the same
+ * address, they presumably want us to use it and not what is
+ * in the firmware.
+ */
+ if (io->addr_source != SI_HARDCODED && io->addr_source != SI_HOTMOD &&
+ ipmi_si_hardcode_match(io->addr_space, io->addr_data)) {
+ dev_info(io->dev,
+ "Hard-coded device at this address already exists");
+ return -ENODEV;
+ }
+
if (!io->io_setup) {
- if (io->addr_type == IPMI_IO_ADDR_SPACE) {
+ if (io->addr_space == IPMI_IO_ADDR_SPACE) {
io->io_setup = ipmi_si_port_setup;
- } else if (io->addr_type == IPMI_MEM_ADDR_SPACE) {
+ } else if (io->addr_space == IPMI_MEM_ADDR_SPACE) {
io->io_setup = ipmi_si_mem_setup;
} else {
return -EINVAL;
@@ -1927,7 +1936,7 @@ static int try_smi_init(struct smi_info *new_smi)
pr_info("Trying %s-specified %s state machine at %s address 0x%lx, slave address 0x%x, irq %d\n",
ipmi_addr_src_to_str(new_smi->io.addr_source),
si_to_str[new_smi->io.si_type],
- addr_space_to_str[new_smi->io.addr_type],
+ addr_space_to_str[new_smi->io.addr_space],
new_smi->io.addr_data,
new_smi->io.slave_addr, new_smi->io.irq);
@@ -1954,24 +1963,9 @@ static int try_smi_init(struct smi_info *new_smi)
/* Do this early so it's available for logs. */
if (!new_smi->io.dev) {
- init_name = kasprintf(GFP_KERNEL, "ipmi_si.%d",
- new_smi->si_num);
-
- /*
- * If we don't already have a device from something
- * else (like PCI), then register a new one.
- */
- new_smi->pdev = platform_device_alloc("ipmi_si",
- new_smi->si_num);
- if (!new_smi->pdev) {
- pr_err("Unable to allocate platform device\n");
- rv = -ENOMEM;
- goto out_err;
- }
- new_smi->io.dev = &new_smi->pdev->dev;
- new_smi->io.dev->driver = &ipmi_platform_driver.driver;
- /* Nulled by device_add() */
- new_smi->io.dev->init_name = init_name;
+ pr_err("IPMI interface added with no device\n");
+ rv = EIO;
+ goto out_err;
}
/* Allocate the state machine's data and initialize it. */
@@ -2044,17 +2038,6 @@ static int try_smi_init(struct smi_info *new_smi)
atomic_set(&new_smi->req_events, 1);
}
- if (new_smi->pdev && !new_smi->pdev_registered) {
- rv = platform_device_add(new_smi->pdev);
- if (rv) {
- dev_err(new_smi->io.dev,
- "Unable to register system interface device: %d\n",
- rv);
- goto out_err;
- }
- new_smi->pdev_registered = true;
- }
-
dev_set_drvdata(new_smi->io.dev, new_smi);
rv = device_add_group(new_smi->io.dev, &ipmi_si_dev_attr_group);
if (rv) {
@@ -2085,11 +2068,16 @@ static int try_smi_init(struct smi_info *new_smi)
WARN_ON(new_smi->io.dev->init_name != NULL);
out_err:
+ if (rv && new_smi->io.io_cleanup) {
+ new_smi->io.io_cleanup(&new_smi->io);
+ new_smi->io.io_cleanup = NULL;
+ }
+
kfree(init_name);
return rv;
}
-static int init_ipmi_si(void)
+static int __init init_ipmi_si(void)
{
struct smi_info *e;
enum ipmi_addr_src type = SI_INVALID;
@@ -2097,11 +2085,9 @@ static int init_ipmi_si(void)
if (initialized)
return 0;
- pr_info("IPMI System Interface driver\n");
+ ipmi_hardcode_init();
- /* If the user gave us a device, they presumably want us to use it */
- if (!ipmi_si_hardcode_find_bmc())
- goto do_scan;
+ pr_info("IPMI System Interface driver\n");
ipmi_si_platform_init();
@@ -2113,7 +2099,6 @@ static int init_ipmi_si(void)
with multiple BMCs we assume that there will be several instances
of a given type so if we succeed in registering a type then also
try to register everything else of the same type */
-do_scan:
mutex_lock(&smi_infos_lock);
list_for_each_entry(e, &smi_infos, link) {
/* Try to register a device if it has an IRQ and we either
@@ -2236,13 +2221,6 @@ static void cleanup_one_si(struct smi_info *smi_info)
if (smi_info->intf)
ipmi_unregister_smi(smi_info->intf);
- if (smi_info->pdev) {
- if (smi_info->pdev_registered)
- platform_device_unregister(smi_info->pdev);
- else
- platform_device_put(smi_info->pdev);
- }
-
kfree(smi_info);
}
@@ -2264,22 +2242,27 @@ int ipmi_si_remove_by_dev(struct device *dev)
return rv;
}
-void ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
- unsigned long addr)
+struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
+ unsigned long addr)
{
/* remove */
struct smi_info *e, *tmp_e;
+ struct device *dev = NULL;
mutex_lock(&smi_infos_lock);
list_for_each_entry_safe(e, tmp_e, &smi_infos, link) {
- if (e->io.addr_type != addr_space)
+ if (e->io.addr_space != addr_space)
continue;
if (e->io.si_type != si_type)
continue;
- if (e->io.addr_data == addr)
+ if (e->io.addr_data == addr) {
+ dev = get_device(e->io.dev);
cleanup_one_si(e);
+ }
}
mutex_unlock(&smi_infos_lock);
+
+ return dev;
}
static void cleanup_ipmi_si(void)
@@ -2299,6 +2282,9 @@ static void cleanup_ipmi_si(void)
list_for_each_entry_safe(e, tmp_e, &smi_infos, link)
cleanup_one_si(e);
mutex_unlock(&smi_infos_lock);
+
+ ipmi_si_hardcode_exit();
+ ipmi_si_hotmod_exit();
}
module_exit(cleanup_ipmi_si);
diff --git a/drivers/char/ipmi/ipmi_si_mem_io.c b/drivers/char/ipmi/ipmi_si_mem_io.c
index fd0ec8d6bf0e..75583612ab10 100644
--- a/drivers/char/ipmi/ipmi_si_mem_io.c
+++ b/drivers/char/ipmi/ipmi_si_mem_io.c
@@ -81,8 +81,6 @@ int ipmi_si_mem_setup(struct si_sm_io *io)
if (!addr)
return -ENODEV;
- io->io_cleanup = mem_cleanup;
-
/*
* Figure out the actual readb/readw/readl/etc routine to use based
* upon the register size.
@@ -141,5 +139,8 @@ int ipmi_si_mem_setup(struct si_sm_io *io)
mem_region_cleanup(io, io->io_size);
return -EIO;
}
+
+ io->io_cleanup = mem_cleanup;
+
return 0;
}
diff --git a/drivers/char/ipmi/ipmi_si_parisc.c b/drivers/char/ipmi/ipmi_si_parisc.c
index f3c99820f564..11c9160275df 100644
--- a/drivers/char/ipmi/ipmi_si_parisc.c
+++ b/drivers/char/ipmi/ipmi_si_parisc.c
@@ -15,7 +15,7 @@ static int __init ipmi_parisc_probe(struct parisc_device *dev)
io.si_type = SI_KCS;
io.addr_source = SI_DEVICETREE;
- io.addr_type = IPMI_MEM_ADDR_SPACE;
+ io.addr_space = IPMI_MEM_ADDR_SPACE;
io.addr_data = dev->hpa.start;
io.regsize = 1;
io.regspacing = 1;
diff --git a/drivers/char/ipmi/ipmi_si_pci.c b/drivers/char/ipmi/ipmi_si_pci.c
index ce00c0da5866..ce93fc7a1e36 100644
--- a/drivers/char/ipmi/ipmi_si_pci.c
+++ b/drivers/char/ipmi/ipmi_si_pci.c
@@ -107,10 +107,10 @@ static int ipmi_pci_probe(struct pci_dev *pdev,
io.addr_source_data = pdev;
if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
- io.addr_type = IPMI_IO_ADDR_SPACE;
+ io.addr_space = IPMI_IO_ADDR_SPACE;
io.io_setup = ipmi_si_port_setup;
} else {
- io.addr_type = IPMI_MEM_ADDR_SPACE;
+ io.addr_space = IPMI_MEM_ADDR_SPACE;
io.io_setup = ipmi_si_mem_setup;
}
io.addr_data = pci_resource_start(pdev, 0);
diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c
index 15cf819f884f..54c7ded2a1ff 100644
--- a/drivers/char/ipmi/ipmi_si_platform.c
+++ b/drivers/char/ipmi/ipmi_si_platform.c
@@ -107,11 +107,11 @@ ipmi_get_info_from_resources(struct platform_device *pdev,
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (res) {
- io->addr_type = IPMI_IO_ADDR_SPACE;
+ io->addr_space = IPMI_IO_ADDR_SPACE;
} else {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res)
- io->addr_type = IPMI_MEM_ADDR_SPACE;
+ io->addr_space = IPMI_MEM_ADDR_SPACE;
}
if (!res) {
dev_err(&pdev->dev, "no I/O or memory address\n");
@@ -121,15 +121,13 @@ ipmi_get_info_from_resources(struct platform_device *pdev,
io->regspacing = DEFAULT_REGSPACING;
res_second = platform_get_resource(pdev,
- (io->addr_type == IPMI_IO_ADDR_SPACE) ?
+ (io->addr_space == IPMI_IO_ADDR_SPACE) ?
IORESOURCE_IO : IORESOURCE_MEM,
1);
if (res_second) {
if (res_second->start > io->addr_data)
io->regspacing = res_second->start - io->addr_data;
}
- io->regsize = DEFAULT_REGSIZE;
- io->regshift = 0;
return res;
}
@@ -137,7 +135,7 @@ ipmi_get_info_from_resources(struct platform_device *pdev,
static int platform_ipmi_probe(struct platform_device *pdev)
{
struct si_sm_io io;
- u8 type, slave_addr, addr_source;
+ u8 type, slave_addr, addr_source, regsize, regshift;
int rv;
rv = device_property_read_u8(&pdev->dev, "addr-source", &addr_source);
@@ -149,7 +147,7 @@ static int platform_ipmi_probe(struct platform_device *pdev)
if (addr_source == SI_SMBIOS) {
if (!si_trydmi)
return -ENODEV;
- } else {
+ } else if (addr_source != SI_HARDCODED) {
if (!si_tryplatform)
return -ENODEV;
}
@@ -169,11 +167,23 @@ static int platform_ipmi_probe(struct platform_device *pdev)
case SI_BT:
io.si_type = type;
break;
+ case SI_TYPE_INVALID: /* User disabled this in hardcode. */
+ return -ENODEV;
default:
dev_err(&pdev->dev, "ipmi-type property is invalid\n");
return -EINVAL;
}
+ io.regsize = DEFAULT_REGSIZE;
+ rv = device_property_read_u8(&pdev->dev, "reg-size", &regsize);
+ if (!rv)
+ io.regsize = regsize;
+
+ io.regshift = 0;
+ rv = device_property_read_u8(&pdev->dev, "reg-shift", &regshift);
+ if (!rv)
+ io.regshift = regshift;
+
if (!ipmi_get_info_from_resources(pdev, &io))
return -EINVAL;
@@ -193,8 +203,9 @@ static int platform_ipmi_probe(struct platform_device *pdev)
io.dev = &pdev->dev;
- pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n",
- (io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
+ pr_info("ipmi_si: %s: %s %#lx regsize %d spacing %d irq %d\n",
+ ipmi_addr_src_to_str(addr_source),
+ (io.addr_space == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
io.addr_data, io.regsize, io.regspacing, io.irq);
ipmi_si_add_smi(&io);
@@ -266,9 +277,9 @@ static int of_ipmi_probe(struct platform_device *pdev)
io.irq_setup = ipmi_std_irq_setup;
if (resource.flags & IORESOURCE_IO)
- io.addr_type = IPMI_IO_ADDR_SPACE;
+ io.addr_space = IPMI_IO_ADDR_SPACE;
else
- io.addr_type = IPMI_MEM_ADDR_SPACE;
+ io.addr_space = IPMI_MEM_ADDR_SPACE;
io.addr_data = resource.start;
@@ -296,15 +307,10 @@ static int of_ipmi_probe(struct platform_device *dev)
static int find_slave_address(struct si_sm_io *io, int slave_addr)
{
#ifdef CONFIG_IPMI_DMI_DECODE
- if (!slave_addr) {
- u32 flags = IORESOURCE_IO;
-
- if (io->addr_type == IPMI_MEM_ADDR_SPACE)
- flags = IORESOURCE_MEM;
-
- slave_addr = ipmi_dmi_get_slave_addr(io->si_type, flags,
+ if (!slave_addr)
+ slave_addr = ipmi_dmi_get_slave_addr(io->si_type,
+ io->addr_space,
io->addr_data);
- }
#endif
return slave_addr;
@@ -358,6 +364,9 @@ static int acpi_ipmi_probe(struct platform_device *pdev)
goto err_free;
}
+ io.regsize = DEFAULT_REGSIZE;
+ io.regshift = 0;
+
res = ipmi_get_info_from_resources(pdev, &io);
if (!res) {
rv = -EINVAL;
@@ -419,9 +428,31 @@ static int ipmi_remove(struct platform_device *pdev)
return ipmi_si_remove_by_dev(&pdev->dev);
}
+static int pdev_match_name(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ const char *name = data;
+
+ return strcmp(pdev->name, name) == 0;
+}
+
+void ipmi_remove_platform_device_by_name(char *name)
+{
+ struct device *dev;
+
+ while ((dev = bus_find_device(&platform_bus_type, NULL, name,
+ pdev_match_name))) {
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+ }
+}
+
static const struct platform_device_id si_plat_ids[] = {
- { "dmi-ipmi-si", 0 },
- { }
+ { "dmi-ipmi-si", 0 },
+ { "hardcode-ipmi-si", 0 },
+ { "hotmod-ipmi-si", 0 },
+ { }
};
struct platform_driver ipmi_platform_driver = {
diff --git a/drivers/char/ipmi/ipmi_si_port_io.c b/drivers/char/ipmi/ipmi_si_port_io.c
index ef6dffcea9fa..03924c32b6e9 100644
--- a/drivers/char/ipmi/ipmi_si_port_io.c
+++ b/drivers/char/ipmi/ipmi_si_port_io.c
@@ -68,8 +68,6 @@ int ipmi_si_port_setup(struct si_sm_io *io)
if (!addr)
return -ENODEV;
- io->io_cleanup = port_cleanup;
-
/*
* Figure out the actual inb/inw/inl/etc routine to use based
* upon the register size.
@@ -109,5 +107,8 @@ int ipmi_si_port_setup(struct si_sm_io *io)
return -EIO;
}
}
+
+ io->io_cleanup = port_cleanup;
+
return 0;
}
diff --git a/drivers/char/ipmi/ipmi_si_sm.h b/drivers/char/ipmi/ipmi_si_sm.h
index aaddf047d923..499db820fadb 100644
--- a/drivers/char/ipmi/ipmi_si_sm.h
+++ b/drivers/char/ipmi/ipmi_si_sm.h
@@ -26,6 +26,10 @@ enum si_type {
SI_TYPE_INVALID, SI_KCS, SI_SMIC, SI_BT
};
+enum ipmi_addr_space {
+ IPMI_IO_ADDR_SPACE, IPMI_MEM_ADDR_SPACE
+};
+
/*
* The structure for doing I/O in the state machine. The state
* machine doesn't have the actual I/O routines, they are done through
@@ -42,11 +46,11 @@ struct si_sm_io {
* state machine shouldn't touch these.
*/
void __iomem *addr;
- int regspacing;
- int regsize;
- int regshift;
- int addr_type;
- long addr_data;
+ unsigned int regspacing;
+ unsigned int regsize;
+ unsigned int regshift;
+ enum ipmi_addr_space addr_space;
+ unsigned long addr_data;
enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */
void (*addr_source_cleanup)(struct si_sm_io *io);
void *addr_source_data;
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index b7a1ae2afaea..8b5aec5430f1 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -28,6 +28,7 @@
*/
#define pr_fmt(fmt) "ipmi_ssif: " fmt
+#define dev_fmt(fmt) "ipmi_ssif: " fmt
#if defined(MODVERSIONS)
#include <linux/modversions.h>
@@ -90,6 +91,12 @@
#define SSIF_MSG_JIFFIES ((SSIF_MSG_USEC * 1000) / TICK_NSEC)
#define SSIF_MSG_PART_JIFFIES ((SSIF_MSG_PART_USEC * 1000) / TICK_NSEC)
+/*
+ * Timeout for the watch, only used for get flag timer.
+ */
+#define SSIF_WATCH_MSG_TIMEOUT msecs_to_jiffies(10)
+#define SSIF_WATCH_WATCHDOG_TIMEOUT msecs_to_jiffies(250)
+
enum ssif_intf_state {
SSIF_NORMAL,
SSIF_GETTING_FLAGS,
@@ -270,6 +277,9 @@ struct ssif_info {
struct timer_list retry_timer;
int retries_left;
+ long watch_timeout; /* Timeout for flags check, 0 if off. */
+ struct timer_list watch_timer; /* Flag fetch timer. */
+
/* Info from SSIF cmd */
unsigned char max_xmit_msg_size;
unsigned char max_recv_msg_size;
@@ -319,7 +329,8 @@ static void deliver_recv_msg(struct ssif_info *ssif_info,
{
if (msg->rsp_size < 0) {
return_hosed_msg(ssif_info, msg);
- pr_err("%s: Malformed message: rsp_size = %d\n",
+ dev_err(&ssif_info->client->dev,
+ "%s: Malformed message: rsp_size = %d\n",
__func__, msg->rsp_size);
} else {
ipmi_smi_msg_received(ssif_info->intf, msg);
@@ -536,7 +547,8 @@ static void start_get(struct ssif_info *ssif_info)
if (rv < 0) {
/* request failed, just return the error. */
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Error from i2c_non_blocking_op(5)\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Error from i2c_non_blocking_op(5)\n");
msg_done_handler(ssif_info, -EIO, NULL, 0);
}
@@ -560,6 +572,26 @@ static void retry_timeout(struct timer_list *t)
start_get(ssif_info);
}
+static void watch_timeout(struct timer_list *t)
+{
+ struct ssif_info *ssif_info = from_timer(ssif_info, t, watch_timer);
+ unsigned long oflags, *flags;
+
+ if (ssif_info->stopping)
+ return;
+
+ flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
+ if (ssif_info->watch_timeout) {
+ mod_timer(&ssif_info->watch_timer,
+ jiffies + ssif_info->watch_timeout);
+ if (SSIF_IDLE(ssif_info)) {
+ start_flag_fetch(ssif_info, flags); /* Releases lock */
+ return;
+ }
+ ssif_info->req_flags = true;
+ }
+ ipmi_ssif_unlock_cond(ssif_info, flags);
+}
static void ssif_alert(struct i2c_client *client, enum i2c_alert_protocol type,
unsigned int data)
@@ -618,7 +650,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
ssif_inc_stat(ssif_info, receive_errors);
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Error in msg_done_handler: %d\n", result);
+ dev_dbg(&ssif_info->client->dev,
+ "%s: Error %d\n", __func__, result);
len = 0;
goto continue_op;
}
@@ -643,7 +676,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
ssif_info->recv, I2C_SMBUS_BLOCK_DATA);
if (rv < 0) {
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Error from i2c_non_blocking_op(1)\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Error from i2c_non_blocking_op(1)\n");
result = -EIO;
} else
@@ -656,7 +690,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
if (len == 0) {
result = -EIO;
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Middle message with no data\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Middle message with no data\n");
goto continue_op;
}
@@ -669,7 +704,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
/* All blocks but the last must have 31 data bytes. */
result = -EIO;
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Received middle message <31\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Received middle message <31\n");
goto continue_op;
}
@@ -678,7 +714,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
/* Received message too big, abort the operation. */
result = -E2BIG;
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Received message too big\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Received message too big\n");
goto continue_op;
}
@@ -709,7 +746,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
I2C_SMBUS_BLOCK_DATA);
if (rv < 0) {
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Error from ssif_i2c_send\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Error from ssif_i2c_send\n");
result = -EIO;
} else
@@ -726,7 +764,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
}
if (ssif_info->ssif_debug & SSIF_DEBUG_STATE)
- pr_info("DONE 1: state = %d, result=%d\n",
+ dev_dbg(&ssif_info->client->dev,
+ "DONE 1: state = %d, result=%d\n",
ssif_info->ssif_state, result);
flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
@@ -760,8 +799,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
*/
ssif_info->ssif_state = SSIF_NORMAL;
ipmi_ssif_unlock_cond(ssif_info, flags);
- pr_warn("Error getting flags: %d %d, %x\n",
- result, len, (len >= 3) ? data[2] : 0);
+ dev_warn(&ssif_info->client->dev,
+ "Error getting flags: %d %d, %x\n",
+ result, len, (len >= 3) ? data[2] : 0);
} else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| data[1] != IPMI_GET_MSG_FLAGS_CMD) {
/*
@@ -769,8 +809,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
* response to a previous command.
*/
ipmi_ssif_unlock_cond(ssif_info, flags);
- pr_warn("Invalid response getting flags: %x %x\n",
- data[0], data[1]);
+ dev_warn(&ssif_info->client->dev,
+ "Invalid response getting flags: %x %x\n",
+ data[0], data[1]);
} else {
ssif_inc_stat(ssif_info, flag_fetches);
ssif_info->msg_flags = data[3];
@@ -782,12 +823,14 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
/* We cleared the flags. */
if ((result < 0) || (len < 3) || (data[2] != 0)) {
/* Error clearing flags */
- pr_warn("Error clearing flags: %d %d, %x\n",
- result, len, (len >= 3) ? data[2] : 0);
+ dev_warn(&ssif_info->client->dev,
+ "Error clearing flags: %d %d, %x\n",
+ result, len, (len >= 3) ? data[2] : 0);
} else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| data[1] != IPMI_CLEAR_MSG_FLAGS_CMD) {
- pr_warn("Invalid response clearing flags: %x %x\n",
- data[0], data[1]);
+ dev_warn(&ssif_info->client->dev,
+ "Invalid response clearing flags: %x %x\n",
+ data[0], data[1]);
}
ssif_info->ssif_state = SSIF_NORMAL;
ipmi_ssif_unlock_cond(ssif_info, flags);
@@ -803,8 +846,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
handle_flags(ssif_info, flags);
} else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| msg->rsp[1] != IPMI_READ_EVENT_MSG_BUFFER_CMD) {
- pr_warn("Invalid response getting events: %x %x\n",
- msg->rsp[0], msg->rsp[1]);
+ dev_warn(&ssif_info->client->dev,
+ "Invalid response getting events: %x %x\n",
+ msg->rsp[0], msg->rsp[1]);
msg->done(msg);
/* Take off the event flag. */
ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL;
@@ -826,8 +870,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
handle_flags(ssif_info, flags);
} else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| msg->rsp[1] != IPMI_GET_MSG_CMD) {
- pr_warn("Invalid response clearing flags: %x %x\n",
- msg->rsp[0], msg->rsp[1]);
+ dev_warn(&ssif_info->client->dev,
+ "Invalid response clearing flags: %x %x\n",
+ msg->rsp[0], msg->rsp[1]);
msg->done(msg);
/* Take off the msg flag. */
@@ -853,7 +898,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
ipmi_ssif_unlock_cond(ssif_info, flags);
if (ssif_info->ssif_debug & SSIF_DEBUG_STATE)
- pr_info("DONE 2: state = %d.\n", ssif_info->ssif_state);
+ dev_dbg(&ssif_info->client->dev,
+ "DONE 2: state = %d.\n", ssif_info->ssif_state);
}
static void msg_written_handler(struct ssif_info *ssif_info, int result,
@@ -873,7 +919,8 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
ssif_inc_stat(ssif_info, send_errors);
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("%s: Out of retries\n", __func__);
+ dev_dbg(&ssif_info->client->dev,
+ "%s: Out of retries\n", __func__);
msg_done_handler(ssif_info, -EIO, NULL, 0);
return;
}
@@ -885,7 +932,8 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
* handle it.
*/
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Error in msg_written_handler: %d\n", result);
+ dev_dbg(&ssif_info->client->dev,
+ "%s: Error %d\n", __func__, result);
msg_done_handler(ssif_info, result, NULL, 0);
return;
@@ -929,7 +977,8 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
ssif_inc_stat(ssif_info, send_errors);
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
- pr_info("Error from i2c_non_blocking_op(3)\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Error from i2c_non_blocking_op(3)\n");
msg_done_handler(ssif_info, -EIO, NULL, 0);
}
} else {
@@ -985,7 +1034,8 @@ static int start_resend(struct ssif_info *ssif_info)
rv = ssif_i2c_send(ssif_info, msg_written_handler, I2C_SMBUS_WRITE,
command, ssif_info->data, I2C_SMBUS_BLOCK_DATA);
if (rv && (ssif_info->ssif_debug & SSIF_DEBUG_MSG))
- pr_info("Error from i2c_non_blocking_op(4)\n");
+ dev_dbg(&ssif_info->client->dev,
+ "Error from i2c_non_blocking_op(4)\n");
return rv;
}
@@ -1054,7 +1104,8 @@ static void sender(void *send_info,
struct timespec64 t;
ktime_get_real_ts64(&t);
- pr_info("**Enqueue %02x %02x: %lld.%6.6ld\n",
+ dev_dbg(&ssif_info->client->dev,
+ "**Enqueue %02x %02x: %lld.%6.6ld\n",
msg->data[0], msg->data[1],
(long long)t.tv_sec, (long)t.tv_nsec / NSEC_PER_USEC);
}
@@ -1073,8 +1124,7 @@ static int get_smi_info(void *send_info, struct ipmi_smi_info *data)
}
/*
- * Instead of having our own timer to periodically check the message
- * flags, we let the message handler drive us.
+ * Upper layer wants us to request events.
*/
static void request_events(void *send_info)
{
@@ -1085,18 +1135,33 @@ static void request_events(void *send_info)
return;
flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
- /*
- * Request flags first, not events, because the lower layer
- * doesn't have a way to send an attention. But make sure
- * event checking still happens.
- */
ssif_info->req_events = true;
- if (SSIF_IDLE(ssif_info))
- start_flag_fetch(ssif_info, flags);
- else {
- ssif_info->req_flags = true;
- ipmi_ssif_unlock_cond(ssif_info, flags);
+ ipmi_ssif_unlock_cond(ssif_info, flags);
+}
+
+/*
+ * Upper layer is changing the flag saying whether we need to request
+ * flags periodically or not.
+ */
+static void ssif_set_need_watch(void *send_info, unsigned int watch_mask)
+{
+ struct ssif_info *ssif_info = (struct ssif_info *) send_info;
+ unsigned long oflags, *flags;
+ long timeout = 0;
+
+ if (watch_mask & IPMI_WATCH_MASK_CHECK_MESSAGES)
+ timeout = SSIF_WATCH_MSG_TIMEOUT;
+ else if (watch_mask)
+ timeout = SSIF_WATCH_WATCHDOG_TIMEOUT;
+
+ flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
+ if (timeout != ssif_info->watch_timeout) {
+ ssif_info->watch_timeout = timeout;
+ if (ssif_info->watch_timeout)
+ mod_timer(&ssif_info->watch_timer,
+ jiffies + ssif_info->watch_timeout);
}
+ ipmi_ssif_unlock_cond(ssif_info, flags);
}
static int ssif_start_processing(void *send_info,
@@ -1223,6 +1288,7 @@ static void shutdown_ssif(void *send_info)
schedule_timeout(1);
ssif_info->stopping = true;
+ del_timer_sync(&ssif_info->watch_timer);
del_timer_sync(&ssif_info->retry_timer);
if (ssif_info->thread) {
complete(&ssif_info->wake_thread);
@@ -1570,7 +1636,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
slave_addr = find_slave_address(client, slave_addr);
- pr_info("Trying %s-specified SSIF interface at i2c address 0x%x, adapter %s, slave address 0x%x\n",
+ dev_info(&client->dev,
+ "Trying %s-specified SSIF interface at i2c address 0x%x, adapter %s, slave address 0x%x\n",
ipmi_addr_src_to_str(ssif_info->addr_source),
client->addr, client->adapter->name, slave_addr);
@@ -1585,7 +1652,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (!rv && (len >= 3) && (resp[2] == 0)) {
if (len < 7) {
if (ssif_dbg_probe)
- pr_info("SSIF info too short: %d\n", len);
+ dev_dbg(&ssif_info->client->dev,
+ "SSIF info too short: %d\n", len);
goto no_support;
}
@@ -1622,7 +1690,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
} else {
no_support:
/* Assume no multi-part or PEC support */
- pr_info("Error fetching SSIF: %d %d %2.2x, your system probably doesn't support this command so using defaults\n",
+ dev_info(&ssif_info->client->dev,
+ "Error fetching SSIF: %d %d %2.2x, your system probably doesn't support this command so using defaults\n",
rv, len, resp[2]);
ssif_info->max_xmit_msg_size = 32;
@@ -1639,16 +1708,18 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
msg[2] = WDT_PRE_TIMEOUT_INT;
rv = do_cmd(client, 3, msg, &len, resp);
if (rv || (len < 3) || (resp[2] != 0))
- pr_warn("Unable to clear message flags: %d %d %2.2x\n",
- rv, len, resp[2]);
+ dev_warn(&ssif_info->client->dev,
+ "Unable to clear message flags: %d %d %2.2x\n",
+ rv, len, resp[2]);
/* Attempt to enable the event buffer. */
msg[0] = IPMI_NETFN_APP_REQUEST << 2;
msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD;
rv = do_cmd(client, 2, msg, &len, resp);
if (rv || (len < 4) || (resp[2] != 0)) {
- pr_warn("Error getting global enables: %d %d %2.2x\n",
- rv, len, resp[2]);
+ dev_warn(&ssif_info->client->dev,
+ "Error getting global enables: %d %d %2.2x\n",
+ rv, len, resp[2]);
rv = 0; /* Not fatal */
goto found;
}
@@ -1666,8 +1737,9 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
msg[2] = ssif_info->global_enables | IPMI_BMC_EVT_MSG_BUFF;
rv = do_cmd(client, 3, msg, &len, resp);
if (rv || (len < 2)) {
- pr_warn("Error setting global enables: %d %d %2.2x\n",
- rv, len, resp[2]);
+ dev_warn(&ssif_info->client->dev,
+ "Error setting global enables: %d %d %2.2x\n",
+ rv, len, resp[2]);
rv = 0; /* Not fatal */
goto found;
}
@@ -1687,8 +1759,9 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
msg[2] = ssif_info->global_enables | IPMI_BMC_RCV_MSG_INTR;
rv = do_cmd(client, 3, msg, &len, resp);
if (rv || (len < 2)) {
- pr_warn("Error setting global enables: %d %d %2.2x\n",
- rv, len, resp[2]);
+ dev_warn(&ssif_info->client->dev,
+ "Error setting global enables: %d %d %2.2x\n",
+ rv, len, resp[2]);
rv = 0; /* Not fatal */
goto found;
}
@@ -1701,13 +1774,15 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
found:
if (ssif_dbg_probe) {
- pr_info("ssif_probe: i2c_probe found device at i2c address %x\n",
- client->addr);
+ dev_dbg(&ssif_info->client->dev,
+ "%s: i2c_probe found device at i2c address %x\n",
+ __func__, client->addr);
}
spin_lock_init(&ssif_info->lock);
ssif_info->ssif_state = SSIF_NORMAL;
timer_setup(&ssif_info->retry_timer, retry_timeout, 0);
+ timer_setup(&ssif_info->watch_timer, watch_timeout, 0);
for (i = 0; i < SSIF_NUM_STATS; i++)
atomic_set(&ssif_info->stats[i], 0);
@@ -1721,6 +1796,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
ssif_info->handlers.get_smi_info = get_smi_info;
ssif_info->handlers.sender = sender;
ssif_info->handlers.request_events = request_events;
+ ssif_info->handlers.set_need_watch = ssif_set_need_watch;
{
unsigned int thread_num;
@@ -1754,8 +1830,9 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
ssif_info,
&ssif_info->client->dev,
slave_addr);
- if (rv) {
- pr_err("Unable to register device: error %d\n", rv);
+ if (rv) {
+ dev_err(&ssif_info->client->dev,
+ "Unable to register device: error %d\n", rv);
goto out_remove_attr;
}
@@ -1764,7 +1841,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (addr_info)
addr_info->client = NULL;
- dev_err(&client->dev, "Unable to start IPMI SSIF: %d\n", rv);
+ dev_err(&ssif_info->client->dev,
+ "Unable to start IPMI SSIF: %d\n", rv);
kfree(ssif_info);
}
kfree(resp);
diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c
index e6124bd548df..ed4dc3b1843e 100644
--- a/drivers/char/ipmi/kcs_bmc.c
+++ b/drivers/char/ipmi/kcs_bmc.c
@@ -440,12 +440,13 @@ struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel)
kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
- if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer)
- return NULL;
kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u",
DEVICE_NAME, channel);
+ if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
+ !kcs_bmc->miscdev.name)
+ return NULL;
kcs_bmc->miscdev.fops = &kcs_bmc_fops;
return kcs_bmc;
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index 5c8d780637bd..3406852f67ff 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -729,7 +729,7 @@ static long lp_ioctl(struct file *file, unsigned int cmd,
ret = lp_set_timeout32(minor, (void __user *)arg);
break;
}
- /* fallthrough for 64-bit */
+ /* fall through - for 64-bit */
case LPSETTIMEOUT_NEW:
ret = lp_set_timeout64(minor, (void __user *)arg);
break;
@@ -757,7 +757,7 @@ static long lp_compat_ioctl(struct file *file, unsigned int cmd,
ret = lp_set_timeout32(minor, (void __user *)arg);
break;
}
- /* fallthrough for x32 mode */
+ /* fall through - for x32 mode */
case LPSETTIMEOUT_NEW:
ret = lp_set_timeout64(minor, (void __user *)arg);
break;
diff --git a/drivers/char/mbcs.c b/drivers/char/mbcs.c
index 8c9216a0f62e..0a31b60bee7b 100644
--- a/drivers/char/mbcs.c
+++ b/drivers/char/mbcs.c
@@ -50,6 +50,7 @@ static LIST_HEAD(soft_list);
* file operations
*/
static const struct file_operations mbcs_ops = {
+ .owner = THIS_MODULE,
.open = mbcs_open,
.llseek = mbcs_sram_llseek,
.read = mbcs_sram_read,
diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c
index 25264d65e716..eff1e3f1b3a2 100644
--- a/drivers/char/nvram.c
+++ b/drivers/char/nvram.c
@@ -21,13 +21,6 @@
* ioctl(NVRAM_SETCKS) (doesn't change contents, just makes checksum valid
* again; use with care!)
*
- * This file also provides some functions for other parts of the kernel that
- * want to access the NVRAM: nvram_{read,write,check_checksum,set_checksum}.
- * Obviously this can be used only if this driver is always configured into
- * the kernel and is not a module. Since the functions are used by some Atari
- * drivers, this is the case on the Atari.
- *
- *
* 1.1 Cesar Barros: SMP locking fixes
* added changelog
* 1.2 Erik Gilling: Cobalt Networks support
@@ -39,64 +32,6 @@
#include <linux/module.h>
#include <linux/nvram.h>
-
-#define PC 1
-#define ATARI 2
-
-/* select machine configuration */
-#if defined(CONFIG_ATARI)
-# define MACH ATARI
-#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) /* and ?? */
-# define MACH PC
-#else
-# error Cannot build nvram driver for this machine configuration.
-#endif
-
-#if MACH == PC
-
-/* RTC in a PC */
-#define CHECK_DRIVER_INIT() 1
-
-/* On PCs, the checksum is built only over bytes 2..31 */
-#define PC_CKS_RANGE_START 2
-#define PC_CKS_RANGE_END 31
-#define PC_CKS_LOC 32
-#define NVRAM_BYTES (128-NVRAM_FIRST_BYTE)
-
-#define mach_check_checksum pc_check_checksum
-#define mach_set_checksum pc_set_checksum
-#define mach_proc_infos pc_proc_infos
-
-#endif
-
-#if MACH == ATARI
-
-/* Special parameters for RTC in Atari machines */
-#include <asm/atarihw.h>
-#include <asm/atariints.h>
-#define RTC_PORT(x) (TT_RTC_BAS + 2*(x))
-#define CHECK_DRIVER_INIT() (MACH_IS_ATARI && ATARIHW_PRESENT(TT_CLK))
-
-#define NVRAM_BYTES 50
-
-/* On Ataris, the checksum is over all bytes except the checksum bytes
- * themselves; these are at the very end */
-#define ATARI_CKS_RANGE_START 0
-#define ATARI_CKS_RANGE_END 47
-#define ATARI_CKS_LOC 48
-
-#define mach_check_checksum atari_check_checksum
-#define mach_set_checksum atari_set_checksum
-#define mach_proc_infos atari_proc_infos
-
-#endif
-
-/* Note that *all* calls to CMOS_READ and CMOS_WRITE must be done with
- * rtc_lock held. Due to the index-port/data-port design of the RTC, we
- * don't want two different things trying to get to it at once. (e.g. the
- * periodic 11 min sync from kernel/time/ntp.c vs. this driver.)
- */
-
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
@@ -106,28 +41,26 @@
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
+#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#include <linux/pagemap.h>
+#ifdef CONFIG_PPC
+#include <asm/nvram.h>
+#endif
static DEFINE_MUTEX(nvram_mutex);
static DEFINE_SPINLOCK(nvram_state_lock);
static int nvram_open_cnt; /* #times opened */
static int nvram_open_mode; /* special open modes */
+static ssize_t nvram_size;
#define NVRAM_WRITE 1 /* opened for writing (exclusive) */
#define NVRAM_EXCL 2 /* opened with O_EXCL */
-static int mach_check_checksum(void);
-static void mach_set_checksum(void);
-
-#ifdef CONFIG_PROC_FS
-static void mach_proc_infos(unsigned char *contents, struct seq_file *seq,
- void *offset);
-#endif
-
+#ifdef CONFIG_X86
/*
* These functions are provided to be called internally or by other parts of
* the kernel. It's up to the caller to ensure correct checksum before reading
@@ -139,13 +72,20 @@ static void mach_proc_infos(unsigned char *contents, struct seq_file *seq,
* know about the RTC cruft.
*/
-unsigned char __nvram_read_byte(int i)
+#define NVRAM_BYTES (128 - NVRAM_FIRST_BYTE)
+
+/* Note that *all* calls to CMOS_READ and CMOS_WRITE must be done with
+ * rtc_lock held. Due to the index-port/data-port design of the RTC, we
+ * don't want two different things trying to get to it at once. (e.g. the
+ * periodic 11 min sync from kernel/time/ntp.c vs. this driver.)
+ */
+
+static unsigned char __nvram_read_byte(int i)
{
return CMOS_READ(NVRAM_FIRST_BYTE + i);
}
-EXPORT_SYMBOL(__nvram_read_byte);
-unsigned char nvram_read_byte(int i)
+static unsigned char pc_nvram_read_byte(int i)
{
unsigned long flags;
unsigned char c;
@@ -155,16 +95,14 @@ unsigned char nvram_read_byte(int i)
spin_unlock_irqrestore(&rtc_lock, flags);
return c;
}
-EXPORT_SYMBOL(nvram_read_byte);
/* This races nicely with trying to read with checksum checking (nvram_read) */
-void __nvram_write_byte(unsigned char c, int i)
+static void __nvram_write_byte(unsigned char c, int i)
{
CMOS_WRITE(c, NVRAM_FIRST_BYTE + i);
}
-EXPORT_SYMBOL(__nvram_write_byte);
-void nvram_write_byte(unsigned char c, int i)
+static void pc_nvram_write_byte(unsigned char c, int i)
{
unsigned long flags;
@@ -172,172 +110,266 @@ void nvram_write_byte(unsigned char c, int i)
__nvram_write_byte(c, i);
spin_unlock_irqrestore(&rtc_lock, flags);
}
-EXPORT_SYMBOL(nvram_write_byte);
-int __nvram_check_checksum(void)
+/* On PCs, the checksum is built only over bytes 2..31 */
+#define PC_CKS_RANGE_START 2
+#define PC_CKS_RANGE_END 31
+#define PC_CKS_LOC 32
+
+static int __nvram_check_checksum(void)
{
- return mach_check_checksum();
+ int i;
+ unsigned short sum = 0;
+ unsigned short expect;
+
+ for (i = PC_CKS_RANGE_START; i <= PC_CKS_RANGE_END; ++i)
+ sum += __nvram_read_byte(i);
+ expect = __nvram_read_byte(PC_CKS_LOC)<<8 |
+ __nvram_read_byte(PC_CKS_LOC+1);
+ return (sum & 0xffff) == expect;
}
-EXPORT_SYMBOL(__nvram_check_checksum);
-int nvram_check_checksum(void)
+static void __nvram_set_checksum(void)
{
- unsigned long flags;
- int rv;
+ int i;
+ unsigned short sum = 0;
- spin_lock_irqsave(&rtc_lock, flags);
- rv = __nvram_check_checksum();
- spin_unlock_irqrestore(&rtc_lock, flags);
- return rv;
+ for (i = PC_CKS_RANGE_START; i <= PC_CKS_RANGE_END; ++i)
+ sum += __nvram_read_byte(i);
+ __nvram_write_byte(sum >> 8, PC_CKS_LOC);
+ __nvram_write_byte(sum & 0xff, PC_CKS_LOC + 1);
}
-EXPORT_SYMBOL(nvram_check_checksum);
-static void __nvram_set_checksum(void)
+static long pc_nvram_set_checksum(void)
{
- mach_set_checksum();
+ spin_lock_irq(&rtc_lock);
+ __nvram_set_checksum();
+ spin_unlock_irq(&rtc_lock);
+ return 0;
}
-#if 0
-void nvram_set_checksum(void)
+static long pc_nvram_initialize(void)
{
- unsigned long flags;
+ ssize_t i;
- spin_lock_irqsave(&rtc_lock, flags);
+ spin_lock_irq(&rtc_lock);
+ for (i = 0; i < NVRAM_BYTES; ++i)
+ __nvram_write_byte(0, i);
__nvram_set_checksum();
- spin_unlock_irqrestore(&rtc_lock, flags);
+ spin_unlock_irq(&rtc_lock);
+ return 0;
}
-#endif /* 0 */
-
-/*
- * The are the file operation function for user access to /dev/nvram
- */
-static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
+static ssize_t pc_nvram_get_size(void)
{
- return generic_file_llseek_size(file, offset, origin, MAX_LFS_FILESIZE,
- NVRAM_BYTES);
+ return NVRAM_BYTES;
}
-static ssize_t nvram_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
+static ssize_t pc_nvram_read(char *buf, size_t count, loff_t *ppos)
{
- unsigned char contents[NVRAM_BYTES];
- unsigned i = *ppos;
- unsigned char *tmp;
+ char *p = buf;
+ loff_t i;
spin_lock_irq(&rtc_lock);
+ if (!__nvram_check_checksum()) {
+ spin_unlock_irq(&rtc_lock);
+ return -EIO;
+ }
+ for (i = *ppos; count > 0 && i < NVRAM_BYTES; --count, ++i, ++p)
+ *p = __nvram_read_byte(i);
+ spin_unlock_irq(&rtc_lock);
- if (!__nvram_check_checksum())
- goto checksum_err;
+ *ppos = i;
+ return p - buf;
+}
- for (tmp = contents; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp)
- *tmp = __nvram_read_byte(i);
+static ssize_t pc_nvram_write(char *buf, size_t count, loff_t *ppos)
+{
+ char *p = buf;
+ loff_t i;
+ spin_lock_irq(&rtc_lock);
+ if (!__nvram_check_checksum()) {
+ spin_unlock_irq(&rtc_lock);
+ return -EIO;
+ }
+ for (i = *ppos; count > 0 && i < NVRAM_BYTES; --count, ++i, ++p)
+ __nvram_write_byte(*p, i);
+ __nvram_set_checksum();
spin_unlock_irq(&rtc_lock);
- if (copy_to_user(buf, contents, tmp - contents))
- return -EFAULT;
-
*ppos = i;
+ return p - buf;
+}
- return tmp - contents;
+const struct nvram_ops arch_nvram_ops = {
+ .read = pc_nvram_read,
+ .write = pc_nvram_write,
+ .read_byte = pc_nvram_read_byte,
+ .write_byte = pc_nvram_write_byte,
+ .get_size = pc_nvram_get_size,
+ .set_checksum = pc_nvram_set_checksum,
+ .initialize = pc_nvram_initialize,
+};
+EXPORT_SYMBOL(arch_nvram_ops);
+#endif /* CONFIG_X86 */
-checksum_err:
- spin_unlock_irq(&rtc_lock);
- return -EIO;
-}
+/*
+ * The are the file operation function for user access to /dev/nvram
+ */
-static ssize_t nvram_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
+static loff_t nvram_misc_llseek(struct file *file, loff_t offset, int origin)
{
- unsigned char contents[NVRAM_BYTES];
- unsigned i = *ppos;
- unsigned char *tmp;
+ return generic_file_llseek_size(file, offset, origin, MAX_LFS_FILESIZE,
+ nvram_size);
+}
- if (i >= NVRAM_BYTES)
- return 0; /* Past EOF */
+static ssize_t nvram_misc_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *tmp;
+ ssize_t ret;
- if (count > NVRAM_BYTES - i)
- count = NVRAM_BYTES - i;
- if (count > NVRAM_BYTES)
- return -EFAULT; /* Can't happen, but prove it to gcc */
- if (copy_from_user(contents, buf, count))
+ if (!access_ok(buf, count))
return -EFAULT;
+ if (*ppos >= nvram_size)
+ return 0;
- spin_lock_irq(&rtc_lock);
+ count = min_t(size_t, count, nvram_size - *ppos);
+ count = min_t(size_t, count, PAGE_SIZE);
- if (!__nvram_check_checksum())
- goto checksum_err;
+ tmp = kmalloc(count, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
- for (tmp = contents; count--; ++i, ++tmp)
- __nvram_write_byte(*tmp, i);
+ ret = nvram_read(tmp, count, ppos);
+ if (ret <= 0)
+ goto out;
- __nvram_set_checksum();
+ if (copy_to_user(buf, tmp, ret)) {
+ *ppos -= ret;
+ ret = -EFAULT;
+ }
- spin_unlock_irq(&rtc_lock);
+out:
+ kfree(tmp);
+ return ret;
+}
- *ppos = i;
+static ssize_t nvram_misc_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *tmp;
+ ssize_t ret;
- return tmp - contents;
+ if (!access_ok(buf, count))
+ return -EFAULT;
+ if (*ppos >= nvram_size)
+ return 0;
-checksum_err:
- spin_unlock_irq(&rtc_lock);
- return -EIO;
+ count = min_t(size_t, count, nvram_size - *ppos);
+ count = min_t(size_t, count, PAGE_SIZE);
+
+ tmp = memdup_user(buf, count);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+
+ ret = nvram_write(tmp, count, ppos);
+ kfree(tmp);
+ return ret;
}
-static long nvram_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static long nvram_misc_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
- int i;
+ long ret = -ENOTTY;
switch (cmd) {
-
+#ifdef CONFIG_PPC
+ case OBSOLETE_PMAC_NVRAM_GET_OFFSET:
+ pr_warn("nvram: Using obsolete PMAC_NVRAM_GET_OFFSET ioctl\n");
+ /* fall through */
+ case IOC_NVRAM_GET_OFFSET:
+ ret = -EINVAL;
+#ifdef CONFIG_PPC_PMAC
+ if (machine_is(powermac)) {
+ int part, offset;
+
+ if (copy_from_user(&part, (void __user *)arg,
+ sizeof(part)) != 0)
+ return -EFAULT;
+ if (part < pmac_nvram_OF || part > pmac_nvram_NR)
+ return -EINVAL;
+ offset = pmac_get_partition(part);
+ if (offset < 0)
+ return -EINVAL;
+ if (copy_to_user((void __user *)arg,
+ &offset, sizeof(offset)) != 0)
+ return -EFAULT;
+ ret = 0;
+ }
+#endif
+ break;
+#ifdef CONFIG_PPC32
+ case IOC_NVRAM_SYNC:
+ if (ppc_md.nvram_sync != NULL) {
+ mutex_lock(&nvram_mutex);
+ ppc_md.nvram_sync();
+ mutex_unlock(&nvram_mutex);
+ }
+ ret = 0;
+ break;
+#endif
+#elif defined(CONFIG_X86) || defined(CONFIG_M68K)
case NVRAM_INIT:
/* initialize NVRAM contents and checksum */
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- mutex_lock(&nvram_mutex);
- spin_lock_irq(&rtc_lock);
-
- for (i = 0; i < NVRAM_BYTES; ++i)
- __nvram_write_byte(0, i);
- __nvram_set_checksum();
-
- spin_unlock_irq(&rtc_lock);
- mutex_unlock(&nvram_mutex);
- return 0;
-
+ if (arch_nvram_ops.initialize != NULL) {
+ mutex_lock(&nvram_mutex);
+ ret = arch_nvram_ops.initialize();
+ mutex_unlock(&nvram_mutex);
+ }
+ break;
case NVRAM_SETCKS:
/* just set checksum, contents unchanged (maybe useful after
* checksum garbaged somehow...) */
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- mutex_lock(&nvram_mutex);
- spin_lock_irq(&rtc_lock);
- __nvram_set_checksum();
- spin_unlock_irq(&rtc_lock);
- mutex_unlock(&nvram_mutex);
- return 0;
-
- default:
- return -ENOTTY;
+ if (arch_nvram_ops.set_checksum != NULL) {
+ mutex_lock(&nvram_mutex);
+ ret = arch_nvram_ops.set_checksum();
+ mutex_unlock(&nvram_mutex);
+ }
+ break;
+#endif /* CONFIG_X86 || CONFIG_M68K */
}
+ return ret;
}
-static int nvram_open(struct inode *inode, struct file *file)
+static int nvram_misc_open(struct inode *inode, struct file *file)
{
spin_lock(&nvram_state_lock);
+ /* Prevent multiple readers/writers if desired. */
if ((nvram_open_cnt && (file->f_flags & O_EXCL)) ||
- (nvram_open_mode & NVRAM_EXCL) ||
- ((file->f_mode & FMODE_WRITE) && (nvram_open_mode & NVRAM_WRITE))) {
+ (nvram_open_mode & NVRAM_EXCL)) {
spin_unlock(&nvram_state_lock);
return -EBUSY;
}
+#if defined(CONFIG_X86) || defined(CONFIG_M68K)
+ /* Prevent multiple writers if the set_checksum ioctl is implemented. */
+ if ((arch_nvram_ops.set_checksum != NULL) &&
+ (file->f_mode & FMODE_WRITE) && (nvram_open_mode & NVRAM_WRITE)) {
+ spin_unlock(&nvram_state_lock);
+ return -EBUSY;
+ }
+#endif
+
if (file->f_flags & O_EXCL)
nvram_open_mode |= NVRAM_EXCL;
if (file->f_mode & FMODE_WRITE)
@@ -349,7 +381,7 @@ static int nvram_open(struct inode *inode, struct file *file)
return 0;
}
-static int nvram_release(struct inode *inode, struct file *file)
+static int nvram_misc_release(struct inode *inode, struct file *file)
{
spin_lock(&nvram_state_lock);
@@ -366,123 +398,7 @@ static int nvram_release(struct inode *inode, struct file *file)
return 0;
}
-#ifndef CONFIG_PROC_FS
-static int nvram_add_proc_fs(void)
-{
- return 0;
-}
-
-#else
-
-static int nvram_proc_read(struct seq_file *seq, void *offset)
-{
- unsigned char contents[NVRAM_BYTES];
- int i = 0;
-
- spin_lock_irq(&rtc_lock);
- for (i = 0; i < NVRAM_BYTES; ++i)
- contents[i] = __nvram_read_byte(i);
- spin_unlock_irq(&rtc_lock);
-
- mach_proc_infos(contents, seq, offset);
-
- return 0;
-}
-
-static int nvram_add_proc_fs(void)
-{
- if (!proc_create_single("driver/nvram", 0, NULL, nvram_proc_read))
- return -ENOMEM;
- return 0;
-}
-
-#endif /* CONFIG_PROC_FS */
-
-static const struct file_operations nvram_fops = {
- .owner = THIS_MODULE,
- .llseek = nvram_llseek,
- .read = nvram_read,
- .write = nvram_write,
- .unlocked_ioctl = nvram_ioctl,
- .open = nvram_open,
- .release = nvram_release,
-};
-
-static struct miscdevice nvram_dev = {
- NVRAM_MINOR,
- "nvram",
- &nvram_fops
-};
-
-static int __init nvram_init(void)
-{
- int ret;
-
- /* First test whether the driver should init at all */
- if (!CHECK_DRIVER_INIT())
- return -ENODEV;
-
- ret = misc_register(&nvram_dev);
- if (ret) {
- printk(KERN_ERR "nvram: can't misc_register on minor=%d\n",
- NVRAM_MINOR);
- goto out;
- }
- ret = nvram_add_proc_fs();
- if (ret) {
- printk(KERN_ERR "nvram: can't create /proc/driver/nvram\n");
- goto outmisc;
- }
- ret = 0;
- printk(KERN_INFO "Non-volatile memory driver v" NVRAM_VERSION "\n");
-out:
- return ret;
-outmisc:
- misc_deregister(&nvram_dev);
- goto out;
-}
-
-static void __exit nvram_cleanup_module(void)
-{
- remove_proc_entry("driver/nvram", NULL);
- misc_deregister(&nvram_dev);
-}
-
-module_init(nvram_init);
-module_exit(nvram_cleanup_module);
-
-/*
- * Machine specific functions
- */
-
-#if MACH == PC
-
-static int pc_check_checksum(void)
-{
- int i;
- unsigned short sum = 0;
- unsigned short expect;
-
- for (i = PC_CKS_RANGE_START; i <= PC_CKS_RANGE_END; ++i)
- sum += __nvram_read_byte(i);
- expect = __nvram_read_byte(PC_CKS_LOC)<<8 |
- __nvram_read_byte(PC_CKS_LOC+1);
- return (sum & 0xffff) == expect;
-}
-
-static void pc_set_checksum(void)
-{
- int i;
- unsigned short sum = 0;
-
- for (i = PC_CKS_RANGE_START; i <= PC_CKS_RANGE_END; ++i)
- sum += __nvram_read_byte(i);
- __nvram_write_byte(sum >> 8, PC_CKS_LOC);
- __nvram_write_byte(sum & 0xff, PC_CKS_LOC + 1);
-}
-
-#ifdef CONFIG_PROC_FS
-
+#if defined(CONFIG_X86) && defined(CONFIG_PROC_FS)
static const char * const floppy_types[] = {
"none", "5.25'' 360k", "5.25'' 1.2M", "3.5'' 720k", "3.5'' 1.44M",
"3.5'' 2.88M", "3.5'' 2.88M"
@@ -495,8 +411,8 @@ static const char * const gfx_types[] = {
"monochrome",
};
-static void pc_proc_infos(unsigned char *nvram, struct seq_file *seq,
- void *offset)
+static void pc_nvram_proc_read(unsigned char *nvram, struct seq_file *seq,
+ void *offset)
{
int checksum;
int type;
@@ -557,143 +473,76 @@ static void pc_proc_infos(unsigned char *nvram, struct seq_file *seq,
return;
}
-#endif
-#endif /* MACH == PC */
-
-#if MACH == ATARI
-
-static int atari_check_checksum(void)
+static int nvram_proc_read(struct seq_file *seq, void *offset)
{
- int i;
- unsigned char sum = 0;
+ unsigned char contents[NVRAM_BYTES];
+ int i = 0;
- for (i = ATARI_CKS_RANGE_START; i <= ATARI_CKS_RANGE_END; ++i)
- sum += __nvram_read_byte(i);
- return (__nvram_read_byte(ATARI_CKS_LOC) == (~sum & 0xff)) &&
- (__nvram_read_byte(ATARI_CKS_LOC + 1) == (sum & 0xff));
-}
+ spin_lock_irq(&rtc_lock);
+ for (i = 0; i < NVRAM_BYTES; ++i)
+ contents[i] = __nvram_read_byte(i);
+ spin_unlock_irq(&rtc_lock);
-static void atari_set_checksum(void)
-{
- int i;
- unsigned char sum = 0;
+ pc_nvram_proc_read(contents, seq, offset);
- for (i = ATARI_CKS_RANGE_START; i <= ATARI_CKS_RANGE_END; ++i)
- sum += __nvram_read_byte(i);
- __nvram_write_byte(~sum, ATARI_CKS_LOC);
- __nvram_write_byte(sum, ATARI_CKS_LOC + 1);
+ return 0;
}
+#endif /* CONFIG_X86 && CONFIG_PROC_FS */
-#ifdef CONFIG_PROC_FS
-
-static struct {
- unsigned char val;
- const char *name;
-} boot_prefs[] = {
- { 0x80, "TOS" },
- { 0x40, "ASV" },
- { 0x20, "NetBSD (?)" },
- { 0x10, "Linux" },
- { 0x00, "unspecified" }
-};
-
-static const char * const languages[] = {
- "English (US)",
- "German",
- "French",
- "English (UK)",
- "Spanish",
- "Italian",
- "6 (undefined)",
- "Swiss (French)",
- "Swiss (German)"
-};
-
-static const char * const dateformat[] = {
- "MM%cDD%cYY",
- "DD%cMM%cYY",
- "YY%cMM%cDD",
- "YY%cDD%cMM",
- "4 (undefined)",
- "5 (undefined)",
- "6 (undefined)",
- "7 (undefined)"
+static const struct file_operations nvram_misc_fops = {
+ .owner = THIS_MODULE,
+ .llseek = nvram_misc_llseek,
+ .read = nvram_misc_read,
+ .write = nvram_misc_write,
+ .unlocked_ioctl = nvram_misc_ioctl,
+ .open = nvram_misc_open,
+ .release = nvram_misc_release,
};
-static const char * const colors[] = {
- "2", "4", "16", "256", "65536", "??", "??", "??"
+static struct miscdevice nvram_misc = {
+ NVRAM_MINOR,
+ "nvram",
+ &nvram_misc_fops,
};
-static void atari_proc_infos(unsigned char *nvram, struct seq_file *seq,
- void *offset)
+static int __init nvram_module_init(void)
{
- int checksum = nvram_check_checksum();
- int i;
- unsigned vmode;
+ int ret;
- seq_printf(seq, "Checksum status : %svalid\n", checksum ? "" : "not ");
+ nvram_size = nvram_get_size();
+ if (nvram_size < 0)
+ return nvram_size;
- seq_printf(seq, "Boot preference : ");
- for (i = ARRAY_SIZE(boot_prefs) - 1; i >= 0; --i) {
- if (nvram[1] == boot_prefs[i].val) {
- seq_printf(seq, "%s\n", boot_prefs[i].name);
- break;
- }
+ ret = misc_register(&nvram_misc);
+ if (ret) {
+ pr_err("nvram: can't misc_register on minor=%d\n", NVRAM_MINOR);
+ return ret;
}
- if (i < 0)
- seq_printf(seq, "0x%02x (undefined)\n", nvram[1]);
-
- seq_printf(seq, "SCSI arbitration : %s\n",
- (nvram[16] & 0x80) ? "on" : "off");
- seq_printf(seq, "SCSI host ID : ");
- if (nvram[16] & 0x80)
- seq_printf(seq, "%d\n", nvram[16] & 7);
- else
- seq_printf(seq, "n/a\n");
-
- /* the following entries are defined only for the Falcon */
- if ((atari_mch_cookie >> 16) != ATARI_MCH_FALCON)
- return;
- seq_printf(seq, "OS language : ");
- if (nvram[6] < ARRAY_SIZE(languages))
- seq_printf(seq, "%s\n", languages[nvram[6]]);
- else
- seq_printf(seq, "%u (undefined)\n", nvram[6]);
- seq_printf(seq, "Keyboard language: ");
- if (nvram[7] < ARRAY_SIZE(languages))
- seq_printf(seq, "%s\n", languages[nvram[7]]);
- else
- seq_printf(seq, "%u (undefined)\n", nvram[7]);
- seq_printf(seq, "Date format : ");
- seq_printf(seq, dateformat[nvram[8] & 7],
- nvram[9] ? nvram[9] : '/', nvram[9] ? nvram[9] : '/');
- seq_printf(seq, ", %dh clock\n", nvram[8] & 16 ? 24 : 12);
- seq_printf(seq, "Boot delay : ");
- if (nvram[10] == 0)
- seq_printf(seq, "default");
- else
- seq_printf(seq, "%ds%s\n", nvram[10],
- nvram[10] < 8 ? ", no memory test" : "");
-
- vmode = (nvram[14] << 8) | nvram[15];
- seq_printf(seq,
- "Video mode : %s colors, %d columns, %s %s monitor\n",
- colors[vmode & 7],
- vmode & 8 ? 80 : 40,
- vmode & 16 ? "VGA" : "TV", vmode & 32 ? "PAL" : "NTSC");
- seq_printf(seq, " %soverscan, compat. mode %s%s\n",
- vmode & 64 ? "" : "no ",
- vmode & 128 ? "on" : "off",
- vmode & 256 ?
- (vmode & 16 ? ", line doubling" : ", half screen") : "");
+#if defined(CONFIG_X86) && defined(CONFIG_PROC_FS)
+ if (!proc_create_single("driver/nvram", 0, NULL, nvram_proc_read)) {
+ pr_err("nvram: can't create /proc/driver/nvram\n");
+ misc_deregister(&nvram_misc);
+ return -ENOMEM;
+ }
+#endif
- return;
+ pr_info("Non-volatile memory driver v" NVRAM_VERSION "\n");
+ return 0;
}
+
+static void __exit nvram_module_exit(void)
+{
+#if defined(CONFIG_X86) && defined(CONFIG_PROC_FS)
+ remove_proc_entry("driver/nvram", NULL);
#endif
+ misc_deregister(&nvram_misc);
+}
-#endif /* MACH == ATARI */
+module_init(nvram_module_init);
+module_exit(nvram_module_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(NVRAM_MINOR);
+MODULE_ALIAS("devname:nvram");