summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/char/Kconfig2
-rw-r--r--drivers/char/xillybus/xillybus_pcie.c1
-rw-r--r--drivers/fpga/altera-cvp.c2
-rw-r--r--drivers/fsi/Kconfig1
-rw-r--r--drivers/fsi/fsi-core.c129
-rw-r--r--drivers/fsi/fsi-master-gpio.c89
-rw-r--r--drivers/fsi/fsi-master-hub.c27
-rw-r--r--drivers/fsi/fsi-master.h17
-rw-r--r--drivers/hv/Makefile1
-rw-r--r--drivers/hv/channel_mgmt.c6
-rw-r--r--drivers/hv/hv.c63
-rw-r--r--drivers/hv/hv_balloon.c121
-rw-r--r--drivers/hv/hv_trace_balloon.h48
-rw-r--r--drivers/hv/hyperv_vmbus.h4
-rw-r--r--drivers/hwtracing/coresight/coresight-cpu-debug.c2
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-sysfs.c4
-rw-r--r--drivers/mcb/mcb-pci.c1
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/Makefile20
-rw-r--r--drivers/misc/aspeed-lpc-ctrl.c44
-rw-r--r--drivers/misc/cardreader/rts5260.c12
-rw-r--r--drivers/misc/lkdtm/Makefile20
-rw-r--r--drivers/misc/lkdtm/bugs.c (renamed from drivers/misc/lkdtm_bugs.c)0
-rw-r--r--drivers/misc/lkdtm/core.c (renamed from drivers/misc/lkdtm_core.c)0
-rw-r--r--drivers/misc/lkdtm/heap.c (renamed from drivers/misc/lkdtm_heap.c)0
-rw-r--r--drivers/misc/lkdtm/lkdtm.h (renamed from drivers/misc/lkdtm.h)0
-rw-r--r--drivers/misc/lkdtm/perms.c (renamed from drivers/misc/lkdtm_perms.c)0
-rw-r--r--drivers/misc/lkdtm/refcount.c (renamed from drivers/misc/lkdtm_refcount.c)1
-rw-r--r--drivers/misc/lkdtm/rodata.c (renamed from drivers/misc/lkdtm_rodata.c)0
-rw-r--r--drivers/misc/lkdtm/usercopy.c (renamed from drivers/misc/lkdtm_usercopy.c)0
-rw-r--r--drivers/misc/mei/bus.c83
-rw-r--r--drivers/misc/mei/client.c87
-rw-r--r--drivers/misc/mei/debugfs.c7
-rw-r--r--drivers/misc/mei/init.c1
-rw-r--r--drivers/misc/mei/main.c66
-rw-r--r--drivers/misc/mei/mei_dev.h10
-rw-r--r--drivers/misc/mic/bus/vop_bus.c6
-rw-r--r--drivers/misc/ocxl/pci.c2
-rw-r--r--drivers/nvmem/Kconfig4
-rw-r--r--drivers/nvmem/bcm-ocotp.c15
-rw-r--r--drivers/nvmem/core.c70
-rw-r--r--drivers/nvmem/imx-iim.c18
-rw-r--r--drivers/nvmem/imx-ocotp.c18
-rw-r--r--drivers/nvmem/lpc18xx_otp.c16
-rw-r--r--drivers/nvmem/meson-efuse.c16
-rw-r--r--drivers/nvmem/meson-mx-efuse.c16
-rw-r--r--drivers/nvmem/mtk-efuse.c16
-rw-r--r--drivers/nvmem/qfprom.c16
-rw-r--r--drivers/nvmem/rockchip-efuse.c32
-rw-r--r--drivers/nvmem/snvs_lpgpr.c53
-rw-r--r--drivers/nvmem/sunxi_sid.c71
-rw-r--r--drivers/nvmem/uniphier-efuse.c16
-rw-r--r--drivers/nvmem/vf610-ocotp.c24
-rw-r--r--drivers/parport/parport_ax88796.c8
-rw-r--r--drivers/parport/parport_pc.c4
-rw-r--r--drivers/parport/parport_serial.c122
-rw-r--r--drivers/pps/clients/pps_parport.c21
-rw-r--r--drivers/pps/generators/pps_gen_parport.c12
-rw-r--r--drivers/siox/siox-core.c2
-rw-r--r--drivers/slimbus/core.c2
-rw-r--r--drivers/uio/uio_hv_generic.c89
-rw-r--r--drivers/w1/w1.c1
62 files changed, 1052 insertions, 488 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index c28dca0c613d..9bcac927cfd0 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -264,7 +264,7 @@ source "drivers/char/hw_random/Kconfig"
config NVRAM
tristate "/dev/nvram support"
- depends on ATARI || X86 || (ARM && RTC_DRV_CMOS) || GENERIC_NVRAM
+ depends on ATARI || X86 || GENERIC_NVRAM
---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"),
diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c
index dff2d1538164..05e5324f60bd 100644
--- a/drivers/char/xillybus/xillybus_pcie.c
+++ b/drivers/char/xillybus/xillybus_pcie.c
@@ -24,7 +24,6 @@ MODULE_LICENSE("GPL v2");
#define PCI_DEVICE_ID_XILLYBUS 0xebeb
-#define PCI_VENDOR_ID_ALTERA 0x1172
#define PCI_VENDOR_ID_ACTEL 0x11aa
#define PCI_VENDOR_ID_LATTICE 0x1204
diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
index 00e73d28077c..77b04e4b3254 100644
--- a/drivers/fpga/altera-cvp.c
+++ b/drivers/fpga/altera-cvp.c
@@ -384,8 +384,6 @@ static int altera_cvp_probe(struct pci_dev *pdev,
const struct pci_device_id *dev_id);
static void altera_cvp_remove(struct pci_dev *pdev);
-#define PCI_VENDOR_ID_ALTERA 0x1172
-
static struct pci_device_id altera_cvp_id_tbl[] = {
{ PCI_VDEVICE(ALTERA, PCI_ANY_ID) },
{ }
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index 513e35173aaa..a326ed663d3c 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -4,6 +4,7 @@
menuconfig FSI
tristate "FSI support"
+ depends on OF
select CRC4
---help---
FSI - the FRU Support Interface - is a simple bus for low-level
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index e318bf8c623c..4c03d6933646 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -18,6 +18,7 @@
#include <linux/fsi.h>
#include <linux/idr.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/slab.h>
#include <linux/bitops.h>
@@ -142,6 +143,7 @@ static void fsi_device_release(struct device *_device)
{
struct fsi_device *device = to_fsi_dev(_device);
+ of_node_put(device->dev.of_node);
kfree(device);
}
@@ -205,7 +207,7 @@ static int fsi_slave_report_and_clear_errors(struct fsi_slave *slave)
if (rc)
return rc;
- dev_info(&slave->dev, "status: 0x%08x, sisc: 0x%08x\n",
+ dev_dbg(&slave->dev, "status: 0x%08x, sisc: 0x%08x\n",
be32_to_cpu(stat), be32_to_cpu(irq));
/* clear interrupts */
@@ -334,6 +336,57 @@ extern void fsi_slave_release_range(struct fsi_slave *slave,
}
EXPORT_SYMBOL_GPL(fsi_slave_release_range);
+static bool fsi_device_node_matches(struct device *dev, struct device_node *np,
+ uint32_t addr, uint32_t size)
+{
+ unsigned int len, na, ns;
+ const __be32 *prop;
+ uint32_t psize;
+
+ na = of_n_addr_cells(np);
+ ns = of_n_size_cells(np);
+
+ if (na != 1 || ns != 1)
+ return false;
+
+ prop = of_get_property(np, "reg", &len);
+ if (!prop || len != 8)
+ return false;
+
+ if (of_read_number(prop, 1) != addr)
+ return false;
+
+ psize = of_read_number(prop + 1, 1);
+ if (psize != size) {
+ dev_warn(dev,
+ "node %s matches probed address, but not size (got 0x%x, expected 0x%x)",
+ of_node_full_name(np), psize, size);
+ }
+
+ return true;
+}
+
+/* Find a matching node for the slave engine at @address, using @size bytes
+ * of space. Returns NULL if not found, or a matching node with refcount
+ * already incremented.
+ */
+static struct device_node *fsi_device_find_of_node(struct fsi_device *dev)
+{
+ struct device_node *parent, *np;
+
+ parent = dev_of_node(&dev->slave->dev);
+ if (!parent)
+ return NULL;
+
+ for_each_child_of_node(parent, np) {
+ if (fsi_device_node_matches(&dev->dev, np,
+ dev->addr, dev->size))
+ return np;
+ }
+
+ return NULL;
+}
+
static int fsi_slave_scan(struct fsi_slave *slave)
{
uint32_t engine_addr;
@@ -402,6 +455,7 @@ static int fsi_slave_scan(struct fsi_slave *slave)
dev_set_name(&dev->dev, "%02x:%02x:%02x:%02x",
slave->master->idx, slave->link,
slave->id, i - 2);
+ dev->dev.of_node = fsi_device_find_of_node(dev);
rc = device_register(&dev->dev);
if (rc) {
@@ -558,9 +612,53 @@ static void fsi_slave_release(struct device *dev)
{
struct fsi_slave *slave = to_fsi_slave(dev);
+ of_node_put(dev->of_node);
kfree(slave);
}
+static bool fsi_slave_node_matches(struct device_node *np,
+ int link, uint8_t id)
+{
+ unsigned int len, na, ns;
+ const __be32 *prop;
+
+ na = of_n_addr_cells(np);
+ ns = of_n_size_cells(np);
+
+ /* Ensure we have the correct format for addresses and sizes in
+ * reg properties
+ */
+ if (na != 2 || ns != 0)
+ return false;
+
+ prop = of_get_property(np, "reg", &len);
+ if (!prop || len != 8)
+ return false;
+
+ return (of_read_number(prop, 1) == link) &&
+ (of_read_number(prop + 1, 1) == id);
+}
+
+/* Find a matching node for the slave at (link, id). Returns NULL if none
+ * found, or a matching node with refcount already incremented.
+ */
+static struct device_node *fsi_slave_find_of_node(struct fsi_master *master,
+ int link, uint8_t id)
+{
+ struct device_node *parent, *np;
+
+ parent = dev_of_node(&master->dev);
+ if (!parent)
+ return NULL;
+
+ for_each_child_of_node(parent, np) {
+ if (fsi_slave_node_matches(np, link, id))
+ return np;
+ }
+
+ return NULL;
+}
+
static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
{
uint32_t chip_id, llmode;
@@ -589,7 +687,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
return -EIO;
}
- dev_info(&master->dev, "fsi: found chip %08x at %02x:%02x:%02x\n",
+ dev_dbg(&master->dev, "fsi: found chip %08x at %02x:%02x:%02x\n",
chip_id, master->idx, link, id);
rc = fsi_slave_set_smode(master, link, id);
@@ -623,6 +721,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
slave->master = master;
slave->dev.parent = &master->dev;
+ slave->dev.of_node = fsi_slave_find_of_node(master, link, id);
slave->dev.release = fsi_slave_release;
slave->link = link;
slave->id = id;
@@ -656,10 +755,13 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
/* FSI master support */
static int fsi_check_access(uint32_t addr, size_t size)
{
- if (size != 1 && size != 2 && size != 4)
- return -EINVAL;
-
- if ((addr & 0x3) != (size & 0x3))
+ if (size == 4) {
+ if (addr & 0x3)
+ return -EINVAL;
+ } else if (size == 2) {
+ if (addr & 0x1)
+ return -EINVAL;
+ } else if (size != 1)
return -EINVAL;
return 0;
@@ -762,14 +864,20 @@ static void fsi_master_unscan(struct fsi_master *master)
device_for_each_child(&master->dev, NULL, fsi_master_remove_slave);
}
+int fsi_master_rescan(struct fsi_master *master)
+{
+ fsi_master_unscan(master);
+ return fsi_master_scan(master);
+}
+EXPORT_SYMBOL_GPL(fsi_master_rescan);
+
static ssize_t master_rescan_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct fsi_master *master = to_fsi_master(dev);
int rc;
- fsi_master_unscan(master);
- rc = fsi_master_scan(master);
+ rc = fsi_master_rescan(master);
if (rc < 0)
return rc;
@@ -793,6 +901,7 @@ static DEVICE_ATTR(break, 0200, NULL, master_break_store);
int fsi_master_register(struct fsi_master *master)
{
int rc;
+ struct device_node *np;
if (!master)
return -EINVAL;
@@ -820,7 +929,9 @@ int fsi_master_register(struct fsi_master *master)
return rc;
}
- fsi_master_scan(master);
+ np = dev_of_node(&master->dev);
+ if (!of_property_read_bool(np, "no-scan-on-init"))
+ fsi_master_scan(master);
return 0;
}
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
index ae2618768508..3f487449a277 100644
--- a/drivers/fsi/fsi-master-gpio.c
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -9,6 +9,7 @@
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -59,6 +60,7 @@ struct fsi_master_gpio {
struct gpio_desc *gpio_trans; /* Voltage translator */
struct gpio_desc *gpio_enable; /* FSI enable */
struct gpio_desc *gpio_mux; /* Mux control */
+ bool external_mode;
};
#define CREATE_TRACE_POINTS
@@ -411,6 +413,12 @@ static int fsi_master_gpio_xfer(struct fsi_master_gpio *master, uint8_t slave,
int rc;
spin_lock_irqsave(&master->cmd_lock, flags);
+
+ if (master->external_mode) {
+ spin_unlock_irqrestore(&master->cmd_lock, flags);
+ return -EBUSY;
+ }
+
serial_out(master, cmd);
echo_delay(master);
rc = poll_for_response(master, slave, resp_len, resp);
@@ -461,12 +469,18 @@ static int fsi_master_gpio_term(struct fsi_master *_master,
static int fsi_master_gpio_break(struct fsi_master *_master, int link)
{
struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+ unsigned long flags;
if (link != 0)
return -ENODEV;
trace_fsi_master_gpio_break(master);
+ spin_lock_irqsave(&master->cmd_lock, flags);
+ if (master->external_mode) {
+ spin_unlock_irqrestore(&master->cmd_lock, flags);
+ return -EBUSY;
+ }
set_sda_output(master, 1);
sda_out(master, 1);
clock_toggle(master, FSI_PRE_BREAK_CLOCKS);
@@ -475,6 +489,7 @@ static int fsi_master_gpio_break(struct fsi_master *_master, int link)
echo_delay(master);
sda_out(master, 1);
clock_toggle(master, FSI_POST_BREAK_CLOCKS);
+ spin_unlock_irqrestore(&master->cmd_lock, flags);
/* Wait for logic reset to take effect */
udelay(200);
@@ -494,21 +509,84 @@ static void fsi_master_gpio_init(struct fsi_master_gpio *master)
clock_zeros(master, FSI_INIT_CLOCKS);
}
+static void fsi_master_gpio_init_external(struct fsi_master_gpio *master)
+{
+ gpiod_direction_output(master->gpio_mux, 0);
+ gpiod_direction_output(master->gpio_trans, 0);
+ gpiod_direction_output(master->gpio_enable, 1);
+ gpiod_direction_input(master->gpio_clk);
+ gpiod_direction_input(master->gpio_data);
+}
+
static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
{
struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+ unsigned long flags;
+ int rc = -EBUSY;
if (link != 0)
return -ENODEV;
- gpiod_set_value(master->gpio_enable, 1);
- return 0;
+ spin_lock_irqsave(&master->cmd_lock, flags);
+ if (!master->external_mode) {
+ gpiod_set_value(master->gpio_enable, 1);
+ rc = 0;
+ }
+ spin_unlock_irqrestore(&master->cmd_lock, flags);
+
+ return rc;
+}
+
+static ssize_t external_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fsi_master_gpio *master = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE - 1, "%u\n",
+ master->external_mode ? 1 : 0);
}
+static ssize_t external_mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fsi_master_gpio *master = dev_get_drvdata(dev);
+ unsigned long flags, val;
+ bool external_mode;
+ int err;
+
+ err = kstrtoul(buf, 0, &val);
+ if (err)
+ return err;
+
+ external_mode = !!val;
+
+ spin_lock_irqsave(&master->cmd_lock, flags);
+
+ if (external_mode == master->external_mode) {
+ spin_unlock_irqrestore(&master->cmd_lock, flags);
+ return count;
+ }
+
+ master->external_mode = external_mode;
+ if (master->external_mode)
+ fsi_master_gpio_init_external(master);
+ else
+ fsi_master_gpio_init(master);
+ spin_unlock_irqrestore(&master->cmd_lock, flags);
+
+ fsi_master_rescan(&master->master);
+
+ return count;
+}
+
+static DEVICE_ATTR(external_mode, 0664,
+ external_mode_show, external_mode_store);
+
static int fsi_master_gpio_probe(struct platform_device *pdev)
{
struct fsi_master_gpio *master;
struct gpio_desc *gpio;
+ int rc;
master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
if (!master)
@@ -516,6 +594,7 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
master->dev = &pdev->dev;
master->master.dev.parent = master->dev;
+ master->master.dev.of_node = of_node_get(dev_of_node(master->dev));
gpio = devm_gpiod_get(&pdev->dev, "clock", 0);
if (IS_ERR(gpio)) {
@@ -565,6 +644,10 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
fsi_master_gpio_init(master);
+ rc = device_create_file(&pdev->dev, &dev_attr_external_mode);
+ if (rc)
+ return rc;
+
return fsi_master_register(&master->master);
}
@@ -583,6 +666,8 @@ static int fsi_master_gpio_remove(struct platform_device *pdev)
devm_gpiod_put(&pdev->dev, master->gpio_mux);
fsi_master_unregister(&master->master);
+ of_node_put(master->master.dev.of_node);
+
return 0;
}
diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c
index 133b9bff1d65..5885fc4a1ef0 100644
--- a/drivers/fsi/fsi-master-hub.c
+++ b/drivers/fsi/fsi-master-hub.c
@@ -16,6 +16,7 @@
#include <linux/delay.h>
#include <linux/fsi.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/slab.h>
#include "fsi-master.h"
@@ -253,7 +254,7 @@ static int hub_master_probe(struct device *dev)
reg = be32_to_cpu(__reg);
links = (reg >> 8) & 0xff;
- dev_info(dev, "hub version %08x (%d links)\n", reg, links);
+ dev_dbg(dev, "hub version %08x (%d links)\n", reg, links);
rc = fsi_slave_claim_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
FSI_HUB_LINK_SIZE * links);
@@ -274,6 +275,7 @@ static int hub_master_probe(struct device *dev)
hub->master.dev.parent = dev;
hub->master.dev.release = hub_master_release;
+ hub->master.dev.of_node = of_node_get(dev_of_node(dev));
hub->master.n_links = links;
hub->master.read = hub_master_read;
@@ -286,10 +288,19 @@ static int hub_master_probe(struct device *dev)
hub_master_init(hub);
rc = fsi_master_register(&hub->master);
- if (!rc)
- return 0;
+ if (rc)
+ goto err_release;
+
+ /* At this point, fsi_master_register performs the device_initialize(),
+ * and holds the sole reference on master.dev. This means the device
+ * will be freed (via ->release) during any subsequent call to
+ * fsi_master_unregister. We add our own reference to it here, so we
+ * can perform cleanup (in _remove()) without it being freed before
+ * we're ready.
+ */
+ get_device(&hub->master.dev);
+ return 0;
- kfree(hub);
err_release:
fsi_slave_release_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
FSI_HUB_LINK_SIZE * links);
@@ -302,6 +313,14 @@ static int hub_master_remove(struct device *dev)
fsi_master_unregister(&hub->master);
fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size);
+ of_node_put(hub->master.dev.of_node);
+
+ /*
+ * master.dev will likely be ->release()ed after this, which free()s
+ * the hub
+ */
+ put_device(&hub->master.dev);
+
return 0;
}
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index 12f7b119567d..ee0b46086026 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -37,7 +37,24 @@ struct fsi_master {
#define dev_to_fsi_master(d) container_of(d, struct fsi_master, dev)
+/**
+ * fsi_master registration & lifetime: the fsi_master_register() and
+ * fsi_master_unregister() functions will take ownership of the master, and
+ * ->dev in particular. The registration path performs a get_device(), which
+ * takes the first reference on the device. Similarly, the unregistration path
+ * performs a put_device(), which may well drop the last reference.
+ *
+ * This means that master implementations *may* need to hold their own
+ * reference (via get_device()) on master->dev. In particular, if the device's
+ * ->release callback frees the fsi_master, then fsi_master_unregister will
+ * invoke this free if no other reference is held.
+ *
+ * The same applies for the error path of fsi_master_register; if the call
+ * fails, dev->release will have been invoked.
+ */
extern int fsi_master_register(struct fsi_master *master);
extern void fsi_master_unregister(struct fsi_master *master);
+extern int fsi_master_rescan(struct fsi_master *master);
+
#endif /* DRIVERS_FSI_MASTER_H */
diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
index 14c22786b519..a1eec7177c2d 100644
--- a/drivers/hv/Makefile
+++ b/drivers/hv/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o
obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o
CFLAGS_hv_trace.o = -I$(src)
+CFLAGS_hv_balloon.o = -I$(src)
hv_vmbus-y := vmbus_drv.o \
hv.o connection.o channel.o \
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index c21020b69114..c6d9d19bc04e 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -596,10 +596,8 @@ static int next_numa_node_id;
/*
* Starting with Win8, we can statically distribute the incoming
* channel interrupt load by binding a channel to VCPU.
- * We do this in a hierarchical fashion:
- * First distribute the primary channels across available NUMA nodes
- * and then distribute the subchannels amongst the CPUs in the NUMA
- * node assigned to the primary channel.
+ * We distribute the interrupt loads to one or more NUMA nodes based on
+ * the channel's affinity_policy.
*
* For pre-win8 hosts or non-performance critical channels we assign the
* first CPU in the first NUMA node.
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index fe96aab9e794..b1f6793acf4c 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -27,7 +27,7 @@
#include <linux/vmalloc.h>
#include <linux/hyperv.h>
#include <linux/version.h>
-#include <linux/interrupt.h>
+#include <linux/random.h>
#include <linux/clockchips.h>
#include <asm/hyperv.h>
#include <asm/mshyperv.h>
@@ -38,6 +38,17 @@ struct hv_context hv_context = {
.synic_initialized = false,
};
+/*
+ * If false, we're using the old mechanism for stimer0 interrupts
+ * where it sends a VMbus message when it expires. The old
+ * mechanism is used when running on older versions of Hyper-V
+ * that don't support Direct Mode. While Hyper-V provides
+ * four stimer's per CPU, Linux uses only stimer0.
+ */
+static bool direct_mode_enabled;
+static int stimer0_irq;
+static int stimer0_vector;
+
#define HV_TIMER_FREQUENCY (10 * 1000 * 1000) /* 100ns period */
#define HV_MAX_MAX_DELTA_TICKS 0xffffffff
#define HV_MIN_DELTA_TICKS 1
@@ -53,6 +64,8 @@ int hv_init(void)
if (!hv_context.cpu_context)
return -ENOMEM;
+ direct_mode_enabled = ms_hyperv.misc_features &
+ HV_X64_STIMER_DIRECT_MODE_AVAILABLE;
return 0;
}
@@ -91,6 +104,21 @@ int hv_post_message(union hv_connection_id connection_id,
return status & 0xFFFF;
}
+/*
+ * ISR for when stimer0 is operating in Direct Mode. Direct Mode
+ * does not use VMbus or any VMbus messages, so process here and not
+ * in the VMbus driver code.
+ */
+
+static void hv_stimer0_isr(void)
+{
+ struct hv_per_cpu_context *hv_cpu;
+
+ hv_cpu = this_cpu_ptr(hv_context.cpu_context);
+ hv_cpu->clk_evt->event_handler(hv_cpu->clk_evt);
+ add_interrupt_randomness(stimer0_vector, 0);
+}
+
static int hv_ce_set_next_event(unsigned long delta,
struct clock_event_device *evt)
{
@@ -108,6 +136,8 @@ static int hv_ce_shutdown(struct clock_event_device *evt)
{
hv_init_timer(HV_X64_MSR_STIMER0_COUNT, 0);
hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, 0);
+ if (direct_mode_enabled)
+ hv_disable_stimer0_percpu_irq(stimer0_irq);
return 0;
}
@@ -116,11 +146,26 @@ static int hv_ce_set_oneshot(struct clock_event_device *evt)
{
union hv_timer_config timer_cfg;
+ timer_cfg.as_uint64 = 0;
timer_cfg.enable = 1;
timer_cfg.auto_enable = 1;
- timer_cfg.sintx = VMBUS_MESSAGE_SINT;
+ if (direct_mode_enabled) {
+ /*
+ * When it expires, the timer will directly interrupt
+ * on the specified hardware vector/IRQ.
+ */
+ timer_cfg.direct_mode = 1;
+ timer_cfg.apic_vector = stimer0_vector;
+ hv_enable_stimer0_percpu_irq(stimer0_irq);
+ } else {
+ /*
+ * When it expires, the timer will generate a VMbus message,
+ * to be handled by the normal VMbus interrupt handler.
+ */
+ timer_cfg.direct_mode = 0;
+ timer_cfg.sintx = VMBUS_MESSAGE_SINT;
+ }
hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64);
-
return 0;
}
@@ -147,7 +192,7 @@ int hv_synic_alloc(void)
int cpu;
hv_context.hv_numa_map = kzalloc(sizeof(struct cpumask) * nr_node_ids,
- GFP_ATOMIC);
+ GFP_KERNEL);
if (hv_context.hv_numa_map == NULL) {
pr_err("Unable to allocate NUMA map\n");
goto err;
@@ -191,6 +236,11 @@ int hv_synic_alloc(void)
INIT_LIST_HEAD(&hv_cpu->chan_list);
}
+ if (direct_mode_enabled &&
+ hv_setup_stimer0_irq(&stimer0_irq, &stimer0_vector,
+ hv_stimer0_isr))
+ goto err;
+
return 0;
err:
return -ENOMEM;
@@ -217,7 +267,7 @@ void hv_synic_free(void)
}
/*
- * hv_synic_init - Initialize the Synthethic Interrupt Controller.
+ * hv_synic_init - Initialize the Synthetic Interrupt Controller.
*
* If it is already initialized by another entity (ie x2v shim), we need to
* retrieve the initialized message and event pages. Otherwise, we create and
@@ -292,6 +342,9 @@ void hv_synic_clockevents_cleanup(void)
if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE))
return;
+ if (direct_mode_enabled)
+ hv_remove_stimer0_irq(stimer0_irq);
+
for_each_present_cpu(cpu) {
struct hv_per_cpu_context *hv_cpu
= per_cpu_ptr(hv_context.cpu_context, cpu);
diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c
index db0e6652d7ef..b3e9f13f8bc3 100644
--- a/drivers/hv/hv_balloon.c
+++ b/drivers/hv/hv_balloon.c
@@ -34,6 +34,9 @@
#include <linux/hyperv.h>
+#define CREATE_TRACE_POINTS
+#include "hv_trace_balloon.h"
+
/*
* We begin with definitions supporting the Dynamic Memory protocol
* with the host.
@@ -576,11 +579,65 @@ static struct hv_dynmem_device dm_device;
static void post_status(struct hv_dynmem_device *dm);
#ifdef CONFIG_MEMORY_HOTPLUG
+static inline bool has_pfn_is_backed(struct hv_hotadd_state *has,
+ unsigned long pfn)
+{
+ struct hv_hotadd_gap *gap;
+
+ /* The page is not backed. */
+ if ((pfn < has->covered_start_pfn) || (pfn >= has->covered_end_pfn))
+ return false;
+
+ /* Check for gaps. */
+ list_for_each_entry(gap, &has->gap_list, list) {
+ if ((pfn >= gap->start_pfn) && (pfn < gap->end_pfn))
+ return false;
+ }
+
+ return true;
+}
+
+static unsigned long hv_page_offline_check(unsigned long start_pfn,
+ unsigned long nr_pages)
+{
+ unsigned long pfn = start_pfn, count = 0;
+ struct hv_hotadd_state *has;
+ bool found;
+
+ while (pfn < start_pfn + nr_pages) {
+ /*
+ * Search for HAS which covers the pfn and when we find one
+ * count how many consequitive PFNs are covered.
+ */
+ found = false;
+ list_for_each_entry(has, &dm_device.ha_region_list, list) {
+ while ((pfn >= has->start_pfn) &&
+ (pfn < has->end_pfn) &&
+ (pfn < start_pfn + nr_pages)) {
+ found = true;
+ if (has_pfn_is_backed(has, pfn))
+ count++;
+ pfn++;
+ }
+ }
+
+ /*
+ * This PFN is not in any HAS (e.g. we're offlining a region
+ * which was present at boot), no need to account for it. Go
+ * to the next one.
+ */
+ if (!found)
+ pfn++;
+ }
+
+ return count;
+}
+
static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
void *v)
{
struct memory_notify *mem = (struct memory_notify *)v;
- unsigned long flags;
+ unsigned long flags, pfn_count;
switch (val) {
case MEM_ONLINE:
@@ -593,7 +650,19 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
case MEM_OFFLINE:
spin_lock_irqsave(&dm_device.ha_lock, flags);
- dm_device.num_pages_onlined -= mem->nr_pages;
+ pfn_count = hv_page_offline_check(mem->start_pfn,
+ mem->nr_pages);
+ if (pfn_count <= dm_device.num_pages_onlined) {
+ dm_device.num_pages_onlined -= pfn_count;
+ } else {
+ /*
+ * We're offlining more pages than we managed to online.
+ * This is unexpected. In any case don't let
+ * num_pages_onlined wrap around zero.
+ */
+ WARN_ON_ONCE(1);
+ dm_device.num_pages_onlined = 0;
+ }
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
break;
case MEM_GOING_ONLINE:
@@ -612,30 +681,9 @@ static struct notifier_block hv_memory_nb = {
/* Check if the particular page is backed and can be onlined and online it. */
static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg)
{
- unsigned long cur_start_pgp;
- unsigned long cur_end_pgp;
- struct hv_hotadd_gap *gap;
-
- cur_start_pgp = (unsigned long)pfn_to_page(has->covered_start_pfn);
- cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
-
- /* The page is not backed. */
- if (((unsigned long)pg < cur_start_pgp) ||
- ((unsigned long)pg >= cur_end_pgp))
+ if (!has_pfn_is_backed(has, page_to_pfn(pg)))
return;
- /* Check for gaps. */
- list_for_each_entry(gap, &has->gap_list, list) {
- cur_start_pgp = (unsigned long)
- pfn_to_page(gap->start_pfn);
- cur_end_pgp = (unsigned long)
- pfn_to_page(gap->end_pfn);
- if (((unsigned long)pg >= cur_start_pgp) &&
- ((unsigned long)pg < cur_end_pgp)) {
- return;
- }
- }
-
/* This frame is currently backed; online the page. */
__online_page_set_limits(pg);
__online_page_increment_counters(pg);
@@ -691,7 +739,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
(HA_CHUNK << PAGE_SHIFT));
if (ret) {
- pr_warn("hot_add memory failed error is %d\n", ret);
+ pr_err("hot_add memory failed error is %d\n", ret);
if (ret == -EEXIST) {
/*
* This error indicates that the error
@@ -726,19 +774,13 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
static void hv_online_page(struct page *pg)
{
struct hv_hotadd_state *has;
- unsigned long cur_start_pgp;
- unsigned long cur_end_pgp;
unsigned long flags;
+ unsigned long pfn = page_to_pfn(pg);
spin_lock_irqsave(&dm_device.ha_lock, flags);
list_for_each_entry(has, &dm_device.ha_region_list, list) {
- cur_start_pgp = (unsigned long)
- pfn_to_page(has->start_pfn);
- cur_end_pgp = (unsigned long)pfn_to_page(has->end_pfn);
-
/* The page belongs to a different HAS. */
- if (((unsigned long)pg < cur_start_pgp) ||
- ((unsigned long)pg >= cur_end_pgp))
+ if ((pfn < has->start_pfn) || (pfn >= has->end_pfn))
continue;
hv_page_online_one(has, pg);
@@ -1014,7 +1056,7 @@ static void hot_add_req(struct work_struct *dummy)
resp.result = 0;
if (!do_hot_add || (resp.page_count == 0))
- pr_info("Memory hot add failed\n");
+ pr_err("Memory hot add failed\n");
dm->state = DM_INITIALIZED;
resp.hdr.trans_id = atomic_inc_return(&trans_id);
@@ -1041,7 +1083,7 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg)
break;
default:
- pr_info("Received Unknown type: %d\n", info_hdr->type);
+ pr_warn("Received Unknown type: %d\n", info_hdr->type);
}
}
@@ -1120,6 +1162,9 @@ static void post_status(struct hv_dynmem_device *dm)
dm->num_pages_added - dm->num_pages_onlined : 0) +
compute_balloon_floor();
+ trace_balloon_status(status.num_avail, status.num_committed,
+ vm_memory_committed(), dm->num_pages_ballooned,
+ dm->num_pages_added, dm->num_pages_onlined);
/*
* If our transaction ID is no longer current, just don't
* send the status. This can happen if we were interrupted
@@ -1290,7 +1335,7 @@ static void balloon_up(struct work_struct *dummy)
/*
* Free up the memory we allocatted.
*/
- pr_info("Balloon response failed\n");
+ pr_err("Balloon response failed\n");
for (i = 0; i < bl_resp->range_count; i++)
free_balloon_pages(&dm_device,
@@ -1421,7 +1466,7 @@ static void cap_resp(struct hv_dynmem_device *dm,
struct dm_capabilities_resp_msg *cap_resp)
{
if (!cap_resp->is_accepted) {
- pr_info("Capabilities not accepted by host\n");
+ pr_err("Capabilities not accepted by host\n");
dm->state = DM_INIT_ERROR;
}
complete(&dm->host_event);
@@ -1508,7 +1553,7 @@ static void balloon_onchannelcallback(void *context)
break;
default:
- pr_err("Unhandled message: type: %d\n", dm_hdr->type);
+ pr_warn("Unhandled message: type: %d\n", dm_hdr->type);
}
}
diff --git a/drivers/hv/hv_trace_balloon.h b/drivers/hv/hv_trace_balloon.h
new file mode 100644
index 000000000000..93082888aec3
--- /dev/null
+++ b/drivers/hv/hv_trace_balloon.h
@@ -0,0 +1,48 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hyperv
+
+#if !defined(_HV_TRACE_BALLOON_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _HV_TRACE_BALLOON_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(balloon_status,
+ TP_PROTO(u64 available, u64 committed,
+ unsigned long vm_memory_committed,
+ unsigned long pages_ballooned,
+ unsigned long pages_added,
+ unsigned long pages_onlined),
+ TP_ARGS(available, committed, vm_memory_committed,
+ pages_ballooned, pages_added, pages_onlined),
+ TP_STRUCT__entry(
+ __field(u64, available)
+ __field(u64, committed)
+ __field(unsigned long, vm_memory_committed)
+ __field(unsigned long, pages_ballooned)
+ __field(unsigned long, pages_added)
+ __field(unsigned long, pages_onlined)
+ ),
+ TP_fast_assign(
+ __entry->available = available;
+ __entry->committed = committed;
+ __entry->vm_memory_committed = vm_memory_committed;
+ __entry->pages_ballooned = pages_ballooned;
+ __entry->pages_added = pages_added;
+ __entry->pages_onlined = pages_onlined;
+ ),
+ TP_printk("available %lld, committed %lld; vm_memory_committed %ld;"
+ " pages_ballooned %ld, pages_added %ld, pages_onlined %ld",
+ __entry->available, __entry->committed,
+ __entry->vm_memory_committed, __entry->pages_ballooned,
+ __entry->pages_added, __entry->pages_onlined
+ )
+ );
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE hv_trace_balloon
+#endif /* _HV_TRACE_BALLOON_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 22300ec7b556..36d34fe3ccb3 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -57,7 +57,9 @@ union hv_timer_config {
u64 periodic:1;
u64 lazy:1;
u64 auto_enable:1;
- u64 reserved_z0:12;
+ u64 apic_vector:8;
+ u64 direct_mode:1;
+ u64 reserved_z0:3;
u64 sintx:4;
u64 reserved_z1:44;
};
diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c
index 6ea62c62ff27..9cdb3fbc8c1f 100644
--- a/drivers/hwtracing/coresight/coresight-cpu-debug.c
+++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c
@@ -315,7 +315,7 @@ static void debug_dump_regs(struct debug_drvdata *drvdata)
}
pc = debug_adjust_pc(drvdata);
- dev_emerg(dev, " EDPCSR: [<%p>] %pS\n", (void *)pc, (void *)pc);
+ dev_emerg(dev, " EDPCSR: [<%px>] %pS\n", (void *)pc, (void *)pc);
if (drvdata->edcidsr_present)
dev_emerg(dev, " EDCIDSR: %08x\n", drvdata->edcidsr);
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
index 4e6eab53e34e..d21961710713 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
@@ -1780,7 +1780,7 @@ static ssize_t ctxid_masks_store(struct device *dev,
*/
for (j = 0; j < 8; j++) {
if (maskbyte & 1)
- config->ctxid_pid[i] &= ~(0xFF << (j * 8));
+ config->ctxid_pid[i] &= ~(0xFFUL << (j * 8));
maskbyte >>= 1;
}
/* Select the next ctxid comparator mask value */
@@ -1963,7 +1963,7 @@ static ssize_t vmid_masks_store(struct device *dev,
*/
for (j = 0; j < 8; j++) {
if (maskbyte & 1)
- config->vmid_val[i] &= ~(0xFF << (j * 8));
+ config->vmid_val[i] &= ~(0xFFUL << (j * 8));
maskbyte >>= 1;
}
/* Select the next vmid comparator mask value */
diff --git a/drivers/mcb/mcb-pci.c b/drivers/mcb/mcb-pci.c
index af4d2f26f1c6..c2d69e33bf2b 100644
--- a/drivers/mcb/mcb-pci.c
+++ b/drivers/mcb/mcb-pci.c
@@ -117,6 +117,7 @@ static void mcb_pci_remove(struct pci_dev *pdev)
static const struct pci_device_id mcb_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_MEN, PCI_DEVICE_ID_MEN_CHAMELEON) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_MEN_CHAMELEON) },
{ 0 },
};
MODULE_DEVICE_TABLE(pci, mcb_pci_tbl);
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 03605f8fc0dc..5d713008749b 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -75,7 +75,6 @@ config ATMEL_TCB_CLKSRC
config ATMEL_TCB_CLKSRC_BLOCK
int
depends on ATMEL_TCB_CLKSRC
- prompt "TC Block" if CPU_AT32AP700X
default 0
range 0 1
help
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index c3c8624f4d95..20be70c3f118 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -12,7 +12,7 @@ obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o
obj-$(CONFIG_ICS932S401) += ics932s401.o
-obj-$(CONFIG_LKDTM) += lkdtm.o
+obj-$(CONFIG_LKDTM) += lkdtm/
obj-$(CONFIG_TIFM_CORE) += tifm_core.o
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
obj-$(CONFIG_PHANTOM) += phantom.o
@@ -57,21 +57,3 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
obj-$(CONFIG_OCXL) += ocxl/
obj-$(CONFIG_MISC_RTSX) += cardreader/
-
-lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
-lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o
-lkdtm-$(CONFIG_LKDTM) += lkdtm_heap.o
-lkdtm-$(CONFIG_LKDTM) += lkdtm_perms.o
-lkdtm-$(CONFIG_LKDTM) += lkdtm_refcount.o
-lkdtm-$(CONFIG_LKDTM) += lkdtm_rodata_objcopy.o
-lkdtm-$(CONFIG_LKDTM) += lkdtm_usercopy.o
-
-KCOV_INSTRUMENT_lkdtm_rodata.o := n
-
-OBJCOPYFLAGS :=
-OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \
- --set-section-flags .text=alloc,readonly \
- --rename-section .text=.rodata
-targets += lkdtm_rodata.o lkdtm_rodata_objcopy.o
-$(obj)/lkdtm_rodata_objcopy.o: $(obj)/lkdtm_rodata.o FORCE
- $(call if_changed,objcopy)
diff --git a/drivers/misc/aspeed-lpc-ctrl.c b/drivers/misc/aspeed-lpc-ctrl.c
index b5439643f54b..a024f8042259 100644
--- a/drivers/misc/aspeed-lpc-ctrl.c
+++ b/drivers/misc/aspeed-lpc-ctrl.c
@@ -7,6 +7,7 @@
* 2 of the License, or (at your option) any later version.
*/
+#include <linux/clk.h>
#include <linux/mfd/syscon.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
@@ -20,12 +21,17 @@
#define DEVICE_NAME "aspeed-lpc-ctrl"
+#define HICR5 0x0
+#define HICR5_ENL2H BIT(8)
+#define HICR5_ENFWH BIT(10)
+
#define HICR7 0x8
#define HICR8 0xc
struct aspeed_lpc_ctrl {
struct miscdevice miscdev;
struct regmap *regmap;
+ struct clk *clk;
phys_addr_t mem_base;
resource_size_t mem_size;
u32 pnor_size;
@@ -153,8 +159,18 @@ static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd,
if (rc)
return rc;
- return regmap_write(lpc_ctrl->regmap, HICR8,
- (~(map.size - 1)) | ((map.size >> 16) - 1));
+ rc = regmap_write(lpc_ctrl->regmap, HICR8,
+ (~(map.size - 1)) | ((map.size >> 16) - 1));
+ if (rc)
+ return rc;
+
+ /*
+ * Enable LPC FHW cycles. This is required for the host to
+ * access the regions specified.
+ */
+ return regmap_update_bits(lpc_ctrl->regmap, HICR5,
+ HICR5_ENFWH | HICR5_ENL2H,
+ HICR5_ENFWH | HICR5_ENL2H);
}
return -EINVAL;
@@ -221,16 +237,33 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
return -ENODEV;
}
+ lpc_ctrl->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(lpc_ctrl->clk)) {
+ dev_err(dev, "couldn't get clock\n");
+ return PTR_ERR(lpc_ctrl->clk);
+ }
+ rc = clk_prepare_enable(lpc_ctrl->clk);
+ if (rc) {
+ dev_err(dev, "couldn't enable clock\n");
+ return rc;
+ }
+
lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
lpc_ctrl->miscdev.name = DEVICE_NAME;
lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops;
lpc_ctrl->miscdev.parent = dev;
rc = misc_register(&lpc_ctrl->miscdev);
- if (rc)
+ if (rc) {
dev_err(dev, "Unable to register device\n");
- else
- dev_info(dev, "Loaded at %pr\n", &resm);
+ goto err;
+ }
+
+ dev_info(dev, "Loaded at %pr\n", &resm);
+
+ return 0;
+err:
+ clk_disable_unprepare(lpc_ctrl->clk);
return rc;
}
@@ -239,6 +272,7 @@ static int aspeed_lpc_ctrl_remove(struct platform_device *pdev)
struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev);
misc_deregister(&lpc_ctrl->miscdev);
+ clk_disable_unprepare(lpc_ctrl->clk);
return 0;
}
diff --git a/drivers/misc/cardreader/rts5260.c b/drivers/misc/cardreader/rts5260.c
index 07cb93abf685..a493b01c5bc6 100644
--- a/drivers/misc/cardreader/rts5260.c
+++ b/drivers/misc/cardreader/rts5260.c
@@ -388,17 +388,17 @@ static void rts5260_disable_ocp(struct rtsx_pcr *pcr)
OC_POWER_DOWN);
}
-int rts5260_get_ocpstat(struct rtsx_pcr *pcr, u8 *val)
+static int rts5260_get_ocpstat(struct rtsx_pcr *pcr, u8 *val)
{
return rtsx_pci_read_register(pcr, REG_OCPSTAT, val);
}
-int rts5260_get_ocpstat2(struct rtsx_pcr *pcr, u8 *val)
+static int rts5260_get_ocpstat2(struct rtsx_pcr *pcr, u8 *val)
{
return rtsx_pci_read_register(pcr, REG_DV3318_OCPSTAT, val);
}
-void rts5260_clear_ocpstat(struct rtsx_pcr *pcr)
+static void rts5260_clear_ocpstat(struct rtsx_pcr *pcr)
{
u8 mask = 0;
u8 val = 0;
@@ -418,7 +418,7 @@ void rts5260_clear_ocpstat(struct rtsx_pcr *pcr)
DV3318_OCP_INT_CLR | DV3318_OCP_CLR, 0);
}
-void rts5260_process_ocp(struct rtsx_pcr *pcr)
+static void rts5260_process_ocp(struct rtsx_pcr *pcr)
{
if (!pcr->option.ocp_en)
return;
@@ -449,7 +449,7 @@ void rts5260_process_ocp(struct rtsx_pcr *pcr)
}
}
-int rts5260_init_hw(struct rtsx_pcr *pcr)
+static int rts5260_init_hw(struct rtsx_pcr *pcr)
{
int err;
@@ -620,7 +620,7 @@ static int rts5260_extra_init_hw(struct rtsx_pcr *pcr)
return 0;
}
-void rts5260_set_aspm(struct rtsx_pcr *pcr, bool enable)
+static void rts5260_set_aspm(struct rtsx_pcr *pcr, bool enable)
{
struct rtsx_cr_option *option = &pcr->option;
u8 val = 0;
diff --git a/drivers/misc/lkdtm/Makefile b/drivers/misc/lkdtm/Makefile
new file mode 100644
index 000000000000..3370a4138e94
--- /dev/null
+++ b/drivers/misc/lkdtm/Makefile
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_LKDTM) += lkdtm.o
+
+lkdtm-$(CONFIG_LKDTM) += core.o
+lkdtm-$(CONFIG_LKDTM) += bugs.o
+lkdtm-$(CONFIG_LKDTM) += heap.o
+lkdtm-$(CONFIG_LKDTM) += perms.o
+lkdtm-$(CONFIG_LKDTM) += refcount.o
+lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o
+lkdtm-$(CONFIG_LKDTM) += usercopy.o
+
+KCOV_INSTRUMENT_rodata.o := n
+
+OBJCOPYFLAGS :=
+OBJCOPYFLAGS_rodata_objcopy.o := \
+ --set-section-flags .text=alloc,readonly \
+ --rename-section .text=.rodata
+targets += rodata.o rodata_objcopy.o
+$(obj)/rodata_objcopy.o: $(obj)/rodata.o FORCE
+ $(call if_changed,objcopy)
diff --git a/drivers/misc/lkdtm_bugs.c b/drivers/misc/lkdtm/bugs.c
index 7eebbdfbcacd..7eebbdfbcacd 100644
--- a/drivers/misc/lkdtm_bugs.c
+++ b/drivers/misc/lkdtm/bugs.c
diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm/core.c
index 2154d1bfd18b..2154d1bfd18b 100644
--- a/drivers/misc/lkdtm_core.c
+++ b/drivers/misc/lkdtm/core.c
diff --git a/drivers/misc/lkdtm_heap.c b/drivers/misc/lkdtm/heap.c
index 65026d7de130..65026d7de130 100644
--- a/drivers/misc/lkdtm_heap.c
+++ b/drivers/misc/lkdtm/heap.c
diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h
index 9e513dcfd809..9e513dcfd809 100644
--- a/drivers/misc/lkdtm.h
+++ b/drivers/misc/lkdtm/lkdtm.h
diff --git a/drivers/misc/lkdtm_perms.c b/drivers/misc/lkdtm/perms.c
index 53b85c9d16b8..53b85c9d16b8 100644
--- a/drivers/misc/lkdtm_perms.c
+++ b/drivers/misc/lkdtm/perms.c
diff --git a/drivers/misc/lkdtm_refcount.c b/drivers/misc/lkdtm/refcount.c
index 2b99d448e7fd..0a146b32da13 100644
--- a/drivers/misc/lkdtm_refcount.c
+++ b/drivers/misc/lkdtm/refcount.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* This is for all the tests related to refcount bugs (e.g. overflow,
* underflow, reaching zero untested, etc).
diff --git a/drivers/misc/lkdtm_rodata.c b/drivers/misc/lkdtm/rodata.c
index 58d180af72cf..58d180af72cf 100644
--- a/drivers/misc/lkdtm_rodata.c
+++ b/drivers/misc/lkdtm/rodata.c
diff --git a/drivers/misc/lkdtm_usercopy.c b/drivers/misc/lkdtm/usercopy.c
index 9725aed305bb..9725aed305bb 100644
--- a/drivers/misc/lkdtm_usercopy.c
+++ b/drivers/misc/lkdtm/usercopy.c
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index 772d02922529..b1133739fb4b 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -74,6 +74,23 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
goto out;
}
+ while (cl->tx_cb_queued >= bus->tx_queue_limit) {
+ mutex_unlock(&bus->device_lock);
+ rets = wait_event_interruptible(cl->tx_wait,
+ cl->writing_state == MEI_WRITE_COMPLETE ||
+ (!mei_cl_is_connected(cl)));
+ mutex_lock(&bus->device_lock);
+ if (rets) {
+ if (signal_pending(current))
+ rets = -EINTR;
+ goto out;
+ }
+ if (!mei_cl_is_connected(cl)) {
+ rets = -ENODEV;
+ goto out;
+ }
+ }
+
cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL);
if (!cb) {
rets = -ENOMEM;
@@ -450,6 +467,29 @@ bool mei_cldev_enabled(struct mei_cl_device *cldev)
EXPORT_SYMBOL_GPL(mei_cldev_enabled);
/**
+ * mei_cl_bus_module_get - acquire module of the underlying
+ * hw driver.
+ *
+ * @cldev: mei client device
+ *
+ * Return: true on success; false if the module was removed.
+ */
+static bool mei_cl_bus_module_get(struct mei_cl_device *cldev)
+{
+ return try_module_get(cldev->bus->dev->driver->owner);
+}
+
+/**
+ * mei_cl_bus_module_put - release the underlying hw module.
+ *
+ * @cldev: mei client device
+ */
+static void mei_cl_bus_module_put(struct mei_cl_device *cldev)
+{
+ module_put(cldev->bus->dev->driver->owner);
+}
+
+/**
* mei_cldev_enable - enable me client device
* create connection with me client
*
@@ -487,9 +527,17 @@ int mei_cldev_enable(struct mei_cl_device *cldev)
goto out;
}
+ if (!mei_cl_bus_module_get(cldev)) {
+ dev_err(&cldev->dev, "get hw module failed");
+ ret = -ENODEV;
+ goto out;
+ }
+
ret = mei_cl_connect(cl, cldev->me_cl, NULL);
- if (ret < 0)
+ if (ret < 0) {
dev_err(&cldev->dev, "cannot connect\n");
+ mei_cl_bus_module_put(cldev);
+ }
out:
mutex_unlock(&bus->device_lock);
@@ -553,6 +601,8 @@ int mei_cldev_disable(struct mei_cl_device *cldev)
dev_err(bus->dev, "Could not disconnect from the ME client\n");
out:
+ mei_cl_bus_module_put(cldev);
+
/* Flush queues and remove any pending read */
mei_cl_flush_queues(cl, NULL);
mei_cl_unlink(cl);
@@ -563,37 +613,6 @@ out:
EXPORT_SYMBOL_GPL(mei_cldev_disable);
/**
- * mei_cl_bus_module_get - acquire module of the underlying
- * hw module.
- *
- * @cl: host client
- *
- * Return: true on success; false if the module was removed.
- */
-bool mei_cl_bus_module_get(struct mei_cl *cl)
-{
- struct mei_cl_device *cldev = cl->cldev;
-
- if (!cldev)
- return true;
-
- return try_module_get(cldev->bus->dev->driver->owner);
-}
-
-/**
- * mei_cl_bus_module_put - release the underlying hw module.
- *
- * @cl: host client
- */
-void mei_cl_bus_module_put(struct mei_cl *cl)
-{
- struct mei_cl_device *cldev = cl->cldev;
-
- if (cldev)
- module_put(cldev->bus->dev->driver->owner);
-}
-
-/**
* mei_cl_device_find - find matching entry in the driver id table
*
* @cldev: me client device
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 7e60c1817c31..8d6197a88b54 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -350,6 +350,36 @@ void mei_io_cb_free(struct mei_cl_cb *cb)
}
/**
+ * mei_tx_cb_queue - queue tx callback
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * @cb: mei callback struct
+ * @head: an instance of list to queue on
+ */
+static inline void mei_tx_cb_enqueue(struct mei_cl_cb *cb,
+ struct list_head *head)
+{
+ list_add_tail(&cb->list, head);
+ cb->cl->tx_cb_queued++;
+}
+
+/**
+ * mei_tx_cb_dequeue - dequeue tx callback
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * @cb: mei callback struct to dequeue and free
+ */
+static inline void mei_tx_cb_dequeue(struct mei_cl_cb *cb)
+{
+ if (!WARN_ON(cb->cl->tx_cb_queued == 0))
+ cb->cl->tx_cb_queued--;
+
+ mei_io_cb_free(cb);
+}
+
+/**
* mei_io_cb_init - allocate and initialize io callback
*
* @cl: mei client
@@ -377,49 +407,37 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl,
}
/**
- * __mei_io_list_flush_cl - removes and frees cbs belonging to cl.
+ * mei_io_list_flush_cl - removes cbs belonging to the cl.
*
* @head: an instance of our list structure
- * @cl: host client, can be NULL for flushing the whole list
- * @free: whether to free the cbs
+ * @cl: host client
*/
-static void __mei_io_list_flush_cl(struct list_head *head,
- const struct mei_cl *cl, bool free)
+static void mei_io_list_flush_cl(struct list_head *head,
+ const struct mei_cl *cl)
{
struct mei_cl_cb *cb, *next;
- /* enable removing everything if no cl is specified */
list_for_each_entry_safe(cb, next, head, list) {
- if (!cl || mei_cl_cmp_id(cl, cb->cl)) {
+ if (mei_cl_cmp_id(cl, cb->cl))
list_del_init(&cb->list);
- if (free)
- mei_io_cb_free(cb);
- }
}
}
/**
- * mei_io_list_flush_cl - removes list entry belonging to cl.
+ * mei_io_tx_list_free_cl - removes cb belonging to the cl and free them
*
* @head: An instance of our list structure
* @cl: host client
*/
-static inline void mei_io_list_flush_cl(struct list_head *head,
- const struct mei_cl *cl)
+static void mei_io_tx_list_free_cl(struct list_head *head,
+ const struct mei_cl *cl)
{
- __mei_io_list_flush_cl(head, cl, false);
-}
+ struct mei_cl_cb *cb, *next;
-/**
- * mei_io_list_free_cl - removes cb belonging to cl and free them
- *
- * @head: An instance of our list structure
- * @cl: host client
- */
-static inline void mei_io_list_free_cl(struct list_head *head,
- const struct mei_cl *cl)
-{
- __mei_io_list_flush_cl(head, cl, true);
+ list_for_each_entry_safe(cb, next, head, list) {
+ if (mei_cl_cmp_id(cl, cb->cl))
+ mei_tx_cb_dequeue(cb);
+ }
}
/**
@@ -538,8 +556,8 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
dev = cl->dev;
cl_dbg(dev, cl, "remove list entry belonging to cl\n");
- mei_io_list_free_cl(&cl->dev->write_list, cl);
- mei_io_list_free_cl(&cl->dev->write_waiting_list, cl);
+ mei_io_tx_list_free_cl(&cl->dev->write_list, cl);
+ mei_io_tx_list_free_cl(&cl->dev->write_waiting_list, cl);
mei_io_list_flush_cl(&cl->dev->ctrl_wr_list, cl);
mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl);
mei_io_list_free_fp(&cl->rd_pending, fp);
@@ -756,8 +774,8 @@ static void mei_cl_set_disconnected(struct mei_cl *cl)
return;
cl->state = MEI_FILE_DISCONNECTED;
- mei_io_list_free_cl(&dev->write_list, cl);
- mei_io_list_free_cl(&dev->write_waiting_list, cl);
+ mei_io_tx_list_free_cl(&dev->write_list, cl);
+ mei_io_tx_list_free_cl(&dev->write_waiting_list, cl);
mei_io_list_flush_cl(&dev->ctrl_rd_list, cl);
mei_io_list_flush_cl(&dev->ctrl_wr_list, cl);
mei_cl_wake_all(cl);
@@ -765,8 +783,6 @@ static void mei_cl_set_disconnected(struct mei_cl *cl)
cl->tx_flow_ctrl_creds = 0;
cl->timer_count = 0;
- mei_cl_bus_module_put(cl);
-
if (!cl->me_cl)
return;
@@ -1076,9 +1092,6 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
dev = cl->dev;
- if (!mei_cl_bus_module_get(cl))
- return -ENODEV;
-
rets = mei_cl_set_connecting(cl, me_cl);
if (rets)
goto nortpm;
@@ -1698,9 +1711,9 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
out:
if (mei_hdr.msg_complete)
- list_add_tail(&cb->list, &dev->write_waiting_list);
+ mei_tx_cb_enqueue(cb, &dev->write_waiting_list);
else
- list_add_tail(&cb->list, &dev->write_list);
+ mei_tx_cb_enqueue(cb, &dev->write_list);
cb = NULL;
if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
@@ -1746,7 +1759,7 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
switch (cb->fop_type) {
case MEI_FOP_WRITE:
- mei_io_cb_free(cb);
+ mei_tx_cb_dequeue(cb);
cl->writing_state = MEI_WRITE_COMPLETE;
if (waitqueue_active(&cl->tx_wait)) {
wake_up_interruptible(&cl->tx_wait);
diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c
index a617aa5a3ad8..c815da91089c 100644
--- a/drivers/misc/mei/debugfs.c
+++ b/drivers/misc/mei/debugfs.c
@@ -97,7 +97,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
int pos = 0;
int ret;
-#define HDR " |me|host|state|rd|wr|\n"
+#define HDR " |me|host|state|rd|wr|wrq\n"
if (!dev)
return -ENODEV;
@@ -130,9 +130,10 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
list_for_each_entry(cl, &dev->file_list, link) {
pos += scnprintf(buf + pos, bufsz - pos,
- "%3d|%2d|%4d|%5d|%2d|%2d|\n",
+ "%3d|%2d|%4d|%5d|%2d|%2d|%3u\n",
i, mei_cl_me_id(cl), cl->host_client_id, cl->state,
- !list_empty(&cl->rd_completed), cl->writing_state);
+ !list_empty(&cl->rd_completed), cl->writing_state,
+ cl->tx_cb_queued);
i++;
}
out:
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index c46f6e99a55e..4888ebc076b7 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -383,6 +383,7 @@ void mei_device_init(struct mei_device *dev,
INIT_LIST_HEAD(&dev->write_waiting_list);
INIT_LIST_HEAD(&dev->ctrl_wr_list);
INIT_LIST_HEAD(&dev->ctrl_rd_list);
+ dev->tx_queue_limit = MEI_TX_QUEUE_LIMIT_DEFAULT;
INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
INIT_WORK(&dev->reset_work, mei_reset_work);
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 758dc73602d5..7465f17e1559 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -291,6 +291,27 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
goto out;
}
+ while (cl->tx_cb_queued >= dev->tx_queue_limit) {
+ if (file->f_flags & O_NONBLOCK) {
+ rets = -EAGAIN;
+ goto out;
+ }
+ mutex_unlock(&dev->device_lock);
+ rets = wait_event_interruptible(cl->tx_wait,
+ cl->writing_state == MEI_WRITE_COMPLETE ||
+ (!mei_cl_is_connected(cl)));
+ mutex_lock(&dev->device_lock);
+ if (rets) {
+ if (signal_pending(current))
+ rets = -EINTR;
+ goto out;
+ }
+ if (!mei_cl_is_connected(cl)) {
+ rets = -ENODEV;
+ goto out;
+ }
+ }
+
*offset = 0;
cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file);
if (!cb) {
@@ -507,7 +528,6 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
break;
default:
- dev_err(dev->dev, ": unsupported ioctl %d.\n", cmd);
rets = -ENOIOCTLCMD;
}
@@ -580,6 +600,12 @@ static __poll_t mei_poll(struct file *file, poll_table *wait)
mei_cl_read_start(cl, mei_cl_mtu(cl), file);
}
+ if (req_events & (POLLOUT | POLLWRNORM)) {
+ poll_wait(file, &cl->tx_wait, wait);
+ if (cl->tx_cb_queued < dev->tx_queue_limit)
+ mask |= POLLOUT | POLLWRNORM;
+ }
+
out:
mutex_unlock(&dev->device_lock);
return mask;
@@ -749,10 +775,48 @@ static ssize_t hbm_ver_drv_show(struct device *device,
}
static DEVICE_ATTR_RO(hbm_ver_drv);
+static ssize_t tx_queue_limit_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct mei_device *dev = dev_get_drvdata(device);
+ u8 size = 0;
+
+ mutex_lock(&dev->device_lock);
+ size = dev->tx_queue_limit;
+ mutex_unlock(&dev->device_lock);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", size);
+}
+
+static ssize_t tx_queue_limit_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mei_device *dev = dev_get_drvdata(device);
+ u8 limit;
+ unsigned int inp;
+ int err;
+
+ err = kstrtouint(buf, 10, &inp);
+ if (err)
+ return err;
+ if (inp > MEI_TX_QUEUE_LIMIT_MAX || inp < MEI_TX_QUEUE_LIMIT_MIN)
+ return -EINVAL;
+ limit = inp;
+
+ mutex_lock(&dev->device_lock);
+ dev->tx_queue_limit = limit;
+ mutex_unlock(&dev->device_lock);
+
+ return count;
+}
+static DEVICE_ATTR_RW(tx_queue_limit);
+
static struct attribute *mei_attrs[] = {
&dev_attr_fw_status.attr,
&dev_attr_hbm_ver.attr,
&dev_attr_hbm_ver_drv.attr,
+ &dev_attr_tx_queue_limit.attr,
NULL
};
ATTRIBUTE_GROUPS(mei);
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index ebcd5132e447..be9c48415da9 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -210,6 +210,7 @@ struct mei_cl_cb {
* @timer_count: watchdog timer for operation completion
* @notify_en: notification - enabled/disabled
* @notify_ev: pending notification event
+ * @tx_cb_queued: number of tx callbacks in queue
* @writing_state: state of the tx
* @rd_pending: pending read credits
* @rd_completed: completed read
@@ -234,6 +235,7 @@ struct mei_cl {
u8 timer_count;
u8 notify_en;
u8 notify_ev;
+ u8 tx_cb_queued;
enum mei_file_transaction_states writing_state;
struct list_head rd_pending;
struct list_head rd_completed;
@@ -241,6 +243,10 @@ struct mei_cl {
struct mei_cl_device *cldev;
};
+#define MEI_TX_QUEUE_LIMIT_DEFAULT 50
+#define MEI_TX_QUEUE_LIMIT_MAX 255
+#define MEI_TX_QUEUE_LIMIT_MIN 30
+
/**
* struct mei_hw_ops - hw specific ops
*
@@ -315,8 +321,6 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length,
bool mei_cl_bus_rx_event(struct mei_cl *cl);
bool mei_cl_bus_notify_event(struct mei_cl *cl);
void mei_cl_bus_remove_devices(struct mei_device *bus);
-bool mei_cl_bus_module_get(struct mei_cl *cl);
-void mei_cl_bus_module_put(struct mei_cl *cl);
int mei_cl_bus_init(void);
void mei_cl_bus_exit(void);
@@ -361,6 +365,7 @@ const char *mei_pg_state_str(enum mei_pg_state state);
* @write_waiting_list : write completion list
* @ctrl_wr_list : pending control write list
* @ctrl_rd_list : pending control read list
+ * @tx_queue_limit: tx queues per client linit
*
* @file_list : list of opened handles
* @open_handle_count: number of opened handles
@@ -425,6 +430,7 @@ struct mei_device {
struct list_head write_waiting_list;
struct list_head ctrl_wr_list;
struct list_head ctrl_rd_list;
+ u8 tx_queue_limit;
struct list_head file_list;
long open_handle_count;
diff --git a/drivers/misc/mic/bus/vop_bus.c b/drivers/misc/mic/bus/vop_bus.c
index fd7f2a6049f8..e5bb9c749b5d 100644
--- a/drivers/misc/mic/bus/vop_bus.c
+++ b/drivers/misc/mic/bus/vop_bus.c
@@ -135,7 +135,9 @@ EXPORT_SYMBOL_GPL(vop_unregister_driver);
static void vop_release_dev(struct device *d)
{
- put_device(d);
+ struct vop_device *dev = dev_to_vop(d);
+
+ kfree(dev);
}
struct vop_device *
@@ -174,7 +176,7 @@ vop_register_device(struct device *pdev, int id,
goto free_vdev;
return vdev;
free_vdev:
- kfree(vdev);
+ put_device(&vdev->dev);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(vop_register_device);
diff --git a/drivers/misc/ocxl/pci.c b/drivers/misc/ocxl/pci.c
index 0051d9ec76cc..21f425472a82 100644
--- a/drivers/misc/ocxl/pci.c
+++ b/drivers/misc/ocxl/pci.c
@@ -519,7 +519,7 @@ static struct ocxl_fn *init_function(struct pci_dev *dev)
rc = device_register(&fn->dev);
if (rc) {
deconfigure_function(fn);
- device_unregister(&fn->dev);
+ put_device(&fn->dev);
return ERR_PTR(rc);
}
return fn;
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index ff505af064ba..5f9bc787d634 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -167,10 +167,10 @@ config MESON_MX_EFUSE
config NVMEM_SNVS_LPGPR
tristate "Support for Low Power General Purpose Register"
- depends on SOC_IMX6 || COMPILE_TEST
+ depends on SOC_IMX6 || SOC_IMX7D || COMPILE_TEST
help
This is a driver for Low Power General Purpose Register (LPGPR) available on
- i.MX6 SoCs in Secure Non-Volatile Storage (SNVS) of this chip.
+ i.MX6 and i.MX7 SoCs in Secure Non-Volatile Storage (SNVS) of this chip.
This driver can also be built as a module. If so, the module
will be called nvmem-snvs-lpgpr.
diff --git a/drivers/nvmem/bcm-ocotp.c b/drivers/nvmem/bcm-ocotp.c
index 5e9e324427f9..4159b3f41d79 100644
--- a/drivers/nvmem/bcm-ocotp.c
+++ b/drivers/nvmem/bcm-ocotp.c
@@ -262,8 +262,7 @@ static int bcm_otpc_probe(struct platform_device *pdev)
else if (of_device_is_compatible(dev->of_node, "brcm,ocotp-v2"))
priv->map = &otp_map_v2;
else {
- dev_err(&pdev->dev,
- "%s otpc config map not defined\n", __func__);
+ dev_err(dev, "%s otpc config map not defined\n", __func__);
return -EINVAL;
}
@@ -302,27 +301,17 @@ static int bcm_otpc_probe(struct platform_device *pdev)
priv->config = &bcm_otpc_nvmem_config;
- nvmem = nvmem_register(&bcm_otpc_nvmem_config);
+ nvmem = devm_nvmem_register(dev, &bcm_otpc_nvmem_config);
if (IS_ERR(nvmem)) {
dev_err(dev, "error registering nvmem config\n");
return PTR_ERR(nvmem);
}
- platform_set_drvdata(pdev, nvmem);
-
return 0;
}
-static int bcm_otpc_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
-}
-
static struct platform_driver bcm_otpc_driver = {
.probe = bcm_otpc_probe,
- .remove = bcm_otpc_remove,
.driver = {
.name = "brcm-otpc",
.of_match_table = bcm_otpc_dt_ids,
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 35a3dbeea324..b05aa8e81303 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -473,9 +473,14 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
nvmem->reg_read = config->reg_read;
nvmem->reg_write = config->reg_write;
nvmem->dev.of_node = config->dev->of_node;
- dev_set_name(&nvmem->dev, "%s%d",
- config->name ? : "nvmem",
- config->name ? config->id : nvmem->id);
+
+ if (config->id == -1 && config->name) {
+ dev_set_name(&nvmem->dev, "%s", config->name);
+ } else {
+ dev_set_name(&nvmem->dev, "%s%d",
+ config->name ? : "nvmem",
+ config->name ? config->id : nvmem->id);
+ }
nvmem->read_only = device_property_present(config->dev, "read-only") |
config->read_only;
@@ -544,6 +549,65 @@ int nvmem_unregister(struct nvmem_device *nvmem)
}
EXPORT_SYMBOL_GPL(nvmem_unregister);
+static void devm_nvmem_release(struct device *dev, void *res)
+{
+ WARN_ON(nvmem_unregister(*(struct nvmem_device **)res));
+}
+
+/**
+ * devm_nvmem_register() - Register a managed nvmem device for given
+ * nvmem_config.
+ * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
+ *
+ * @config: nvmem device configuration with which nvmem device is created.
+ *
+ * Return: Will be an ERR_PTR() on error or a valid pointer to nvmem_device
+ * on success.
+ */
+struct nvmem_device *devm_nvmem_register(struct device *dev,
+ const struct nvmem_config *config)
+{
+ struct nvmem_device **ptr, *nvmem;
+
+ ptr = devres_alloc(devm_nvmem_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ nvmem = nvmem_register(config);
+
+ if (!IS_ERR(nvmem)) {
+ *ptr = nvmem;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return nvmem;
+}
+EXPORT_SYMBOL_GPL(devm_nvmem_register);
+
+static int devm_nvmem_match(struct device *dev, void *res, void *data)
+{
+ struct nvmem_device **r = res;
+
+ return *r == data;
+}
+
+/**
+ * devm_nvmem_unregister() - Unregister previously registered managed nvmem
+ * device.
+ *
+ * @nvmem: Pointer to previously registered nvmem device.
+ *
+ * Return: Will be an negative on error or a zero on success.
+ */
+int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem)
+{
+ return devres_release(dev, devm_nvmem_release, devm_nvmem_match, nvmem);
+}
+EXPORT_SYMBOL(devm_nvmem_unregister);
+
+
static struct nvmem_device *__nvmem_device_get(struct device_node *np,
struct nvmem_cell **cellp,
const char *cell_id)
diff --git a/drivers/nvmem/imx-iim.c b/drivers/nvmem/imx-iim.c
index 52cfe91d9762..6651e4cdc002 100644
--- a/drivers/nvmem/imx-iim.c
+++ b/drivers/nvmem/imx-iim.c
@@ -125,7 +125,7 @@ static int imx_iim_probe(struct platform_device *pdev)
drvdata = of_id->data;
- iim->clk = devm_clk_get(&pdev->dev, NULL);
+ iim->clk = devm_clk_get(dev, NULL);
if (IS_ERR(iim->clk))
return PTR_ERR(iim->clk);
@@ -138,25 +138,13 @@ static int imx_iim_probe(struct platform_device *pdev)
cfg.size = drvdata->nregs;
cfg.priv = iim;
- nvmem = nvmem_register(&cfg);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
+ nvmem = devm_nvmem_register(dev, &cfg);
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int imx_iim_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static struct platform_driver imx_iim_driver = {
.probe = imx_iim_probe,
- .remove = imx_iim_remove,
.driver = {
.name = "imx-iim",
.of_match_table = imx_iim_dt_ids,
diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c
index d7ba351a70c9..60816c856dd6 100644
--- a/drivers/nvmem/imx-ocotp.c
+++ b/drivers/nvmem/imx-ocotp.c
@@ -439,7 +439,6 @@ MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids);
static int imx_ocotp_probe(struct platform_device *pdev)
{
- const struct of_device_id *of_id;
struct device *dev = &pdev->dev;
struct resource *res;
struct ocotp_priv *priv;
@@ -460,32 +459,19 @@ static int imx_ocotp_probe(struct platform_device *pdev)
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
- of_id = of_match_device(imx_ocotp_dt_ids, dev);
priv->params = of_device_get_match_data(&pdev->dev);
imx_ocotp_nvmem_config.size = 4 * priv->params->nregs;
imx_ocotp_nvmem_config.dev = dev;
imx_ocotp_nvmem_config.priv = priv;
priv->config = &imx_ocotp_nvmem_config;
- nvmem = nvmem_register(&imx_ocotp_nvmem_config);
+ nvmem = devm_nvmem_register(dev, &imx_ocotp_nvmem_config);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int imx_ocotp_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static struct platform_driver imx_ocotp_driver = {
.probe = imx_ocotp_probe,
- .remove = imx_ocotp_remove,
.driver = {
.name = "imx_ocotp",
.of_match_table = imx_ocotp_dt_ids,
diff --git a/drivers/nvmem/lpc18xx_otp.c b/drivers/nvmem/lpc18xx_otp.c
index 95268db155e9..549b5298ac4c 100644
--- a/drivers/nvmem/lpc18xx_otp.c
+++ b/drivers/nvmem/lpc18xx_otp.c
@@ -86,20 +86,9 @@ static int lpc18xx_otp_probe(struct platform_device *pdev)
lpc18xx_otp_nvmem_config.dev = &pdev->dev;
lpc18xx_otp_nvmem_config.priv = otp;
- nvmem = nvmem_register(&lpc18xx_otp_nvmem_config);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
+ nvmem = devm_nvmem_register(&pdev->dev, &lpc18xx_otp_nvmem_config);
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int lpc18xx_otp_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static const struct of_device_id lpc18xx_otp_dt_ids[] = {
@@ -110,7 +99,6 @@ MODULE_DEVICE_TABLE(of, lpc18xx_otp_dt_ids);
static struct platform_driver lpc18xx_otp_driver = {
.probe = lpc18xx_otp_probe,
- .remove = lpc18xx_otp_remove,
.driver = {
.name = "lpc18xx_otp",
.of_match_table = lpc18xx_otp_dt_ids,
diff --git a/drivers/nvmem/meson-efuse.c b/drivers/nvmem/meson-efuse.c
index a43c68f90937..71823d1403c5 100644
--- a/drivers/nvmem/meson-efuse.c
+++ b/drivers/nvmem/meson-efuse.c
@@ -60,25 +60,13 @@ static int meson_efuse_probe(struct platform_device *pdev)
econfig.reg_read = meson_efuse_read;
econfig.size = size;
- nvmem = nvmem_register(&econfig);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
+ nvmem = devm_nvmem_register(&pdev->dev, &econfig);
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int meson_efuse_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static struct platform_driver meson_efuse_driver = {
.probe = meson_efuse_probe,
- .remove = meson_efuse_remove,
.driver = {
.name = "meson-efuse",
.of_match_table = meson_efuse_match,
diff --git a/drivers/nvmem/meson-mx-efuse.c b/drivers/nvmem/meson-mx-efuse.c
index 41d3a3c1104e..a085563e39e3 100644
--- a/drivers/nvmem/meson-mx-efuse.c
+++ b/drivers/nvmem/meson-mx-efuse.c
@@ -233,25 +233,13 @@ static int meson_mx_efuse_probe(struct platform_device *pdev)
return PTR_ERR(efuse->core_clk);
}
- efuse->nvmem = nvmem_register(&efuse->config);
- if (IS_ERR(efuse->nvmem))
- return PTR_ERR(efuse->nvmem);
+ efuse->nvmem = devm_nvmem_register(&pdev->dev, &efuse->config);
- platform_set_drvdata(pdev, efuse);
-
- return 0;
-}
-
-static int meson_mx_efuse_remove(struct platform_device *pdev)
-{
- struct meson_mx_efuse *efuse = platform_get_drvdata(pdev);
-
- return nvmem_unregister(efuse->nvmem);
+ return PTR_ERR_OR_ZERO(efuse->nvmem);
}
static struct platform_driver meson_mx_efuse_driver = {
.probe = meson_mx_efuse_probe,
- .remove = meson_mx_efuse_remove,
.driver = {
.name = "meson-mx-efuse",
.of_match_table = meson_mx_efuse_match,
diff --git a/drivers/nvmem/mtk-efuse.c b/drivers/nvmem/mtk-efuse.c
index 9ee3479cfc7b..e66adf17a747 100644
--- a/drivers/nvmem/mtk-efuse.c
+++ b/drivers/nvmem/mtk-efuse.c
@@ -72,20 +72,9 @@ static int mtk_efuse_probe(struct platform_device *pdev)
econfig.size = resource_size(res);
econfig.priv = priv;
econfig.dev = dev;
- nvmem = nvmem_register(&econfig);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
+ nvmem = devm_nvmem_register(dev, &econfig);
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int mtk_efuse_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static const struct of_device_id mtk_efuse_of_match[] = {
@@ -97,7 +86,6 @@ MODULE_DEVICE_TABLE(of, mtk_efuse_of_match);
static struct platform_driver mtk_efuse_driver = {
.probe = mtk_efuse_probe,
- .remove = mtk_efuse_remove,
.driver = {
.name = "mediatek,efuse",
.of_match_table = mtk_efuse_of_match,
diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c
index cb3b48b47d64..4f650baad983 100644
--- a/drivers/nvmem/qfprom.c
+++ b/drivers/nvmem/qfprom.c
@@ -47,13 +47,6 @@ static int qfprom_reg_write(void *context,
return 0;
}
-static int qfprom_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
-}
-
static struct nvmem_config econfig = {
.name = "qfprom",
.stride = 1,
@@ -82,13 +75,9 @@ static int qfprom_probe(struct platform_device *pdev)
econfig.dev = dev;
econfig.priv = priv;
- nvmem = nvmem_register(&econfig);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
-
- platform_set_drvdata(pdev, nvmem);
+ nvmem = devm_nvmem_register(dev, &econfig);
- return 0;
+ return PTR_ERR_OR_ZERO(nvmem);
}
static const struct of_device_id qfprom_of_match[] = {
@@ -99,7 +88,6 @@ MODULE_DEVICE_TABLE(of, qfprom_of_match);
static struct platform_driver qfprom_driver = {
.probe = qfprom_probe,
- .remove = qfprom_remove,
.driver = {
.name = "qcom,qfprom",
.of_match_table = qfprom_of_match,
diff --git a/drivers/nvmem/rockchip-efuse.c b/drivers/nvmem/rockchip-efuse.c
index f13a8335f364..b3b0b648be62 100644
--- a/drivers/nvmem/rockchip-efuse.c
+++ b/drivers/nvmem/rockchip-efuse.c
@@ -259,55 +259,43 @@ static int rockchip_efuse_probe(struct platform_device *pdev)
struct resource *res;
struct nvmem_device *nvmem;
struct rockchip_efuse_chip *efuse;
- const struct of_device_id *match;
+ const void *data;
struct device *dev = &pdev->dev;
- match = of_match_device(dev->driver->of_match_table, dev);
- if (!match || !match->data) {
+ data = of_device_get_match_data(dev);
+ if (!data) {
dev_err(dev, "failed to get match data\n");
return -EINVAL;
}
- efuse = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_efuse_chip),
+ efuse = devm_kzalloc(dev, sizeof(struct rockchip_efuse_chip),
GFP_KERNEL);
if (!efuse)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- efuse->base = devm_ioremap_resource(&pdev->dev, res);
+ efuse->base = devm_ioremap_resource(dev, res);
if (IS_ERR(efuse->base))
return PTR_ERR(efuse->base);
- efuse->clk = devm_clk_get(&pdev->dev, "pclk_efuse");
+ efuse->clk = devm_clk_get(dev, "pclk_efuse");
if (IS_ERR(efuse->clk))
return PTR_ERR(efuse->clk);
- efuse->dev = &pdev->dev;
+ efuse->dev = dev;
if (of_property_read_u32(dev->of_node, "rockchip,efuse-size",
&econfig.size))
econfig.size = resource_size(res);
- econfig.reg_read = match->data;
+ econfig.reg_read = data;
econfig.priv = efuse;
econfig.dev = efuse->dev;
- nvmem = nvmem_register(&econfig);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
+ nvmem = devm_nvmem_register(dev, &econfig);
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int rockchip_efuse_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static struct platform_driver rockchip_efuse_driver = {
.probe = rockchip_efuse_probe,
- .remove = rockchip_efuse_remove,
.driver = {
.name = "rockchip-efuse",
.of_match_table = rockchip_efuse_match,
diff --git a/drivers/nvmem/snvs_lpgpr.c b/drivers/nvmem/snvs_lpgpr.c
index e5c2a4a17f03..c050a23a9f2b 100644
--- a/drivers/nvmem/snvs_lpgpr.c
+++ b/drivers/nvmem/snvs_lpgpr.c
@@ -14,15 +14,21 @@
#include <linux/regmap.h>
#define IMX6Q_SNVS_HPLR 0x00
-#define IMX6Q_GPR_SL BIT(5)
#define IMX6Q_SNVS_LPLR 0x34
-#define IMX6Q_GPR_HL BIT(5)
#define IMX6Q_SNVS_LPGPR 0x68
+#define IMX7D_SNVS_HPLR 0x00
+#define IMX7D_SNVS_LPLR 0x34
+#define IMX7D_SNVS_LPGPR 0x90
+
+#define IMX_GPR_SL BIT(5)
+#define IMX_GPR_HL BIT(5)
+
struct snvs_lpgpr_cfg {
int offset;
int offset_hplr;
int offset_lplr;
+ int size;
};
struct snvs_lpgpr_priv {
@@ -36,6 +42,14 @@ static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q = {
.offset = IMX6Q_SNVS_LPGPR,
.offset_hplr = IMX6Q_SNVS_HPLR,
.offset_lplr = IMX6Q_SNVS_LPLR,
+ .size = 4,
+};
+
+static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx7d = {
+ .offset = IMX7D_SNVS_LPGPR,
+ .offset_hplr = IMX7D_SNVS_HPLR,
+ .offset_lplr = IMX7D_SNVS_LPLR,
+ .size = 16,
};
static int snvs_lpgpr_write(void *context, unsigned int offset, void *val,
@@ -50,14 +64,14 @@ static int snvs_lpgpr_write(void *context, unsigned int offset, void *val,
if (ret < 0)
return ret;
- if (lock_reg & IMX6Q_GPR_SL)
+ if (lock_reg & IMX_GPR_SL)
return -EPERM;
ret = regmap_read(priv->regmap, dcfg->offset_lplr, &lock_reg);
if (ret < 0)
return ret;
- if (lock_reg & IMX6Q_GPR_HL)
+ if (lock_reg & IMX_GPR_HL)
return -EPERM;
return regmap_bulk_write(priv->regmap, dcfg->offset + offset, val,
@@ -110,40 +124,29 @@ static int snvs_lpgpr_probe(struct platform_device *pdev)
cfg->priv = priv;
cfg->name = dev_name(dev);
cfg->dev = dev;
- cfg->stride = 4,
- cfg->word_size = 4,
- cfg->size = 4,
- cfg->owner = THIS_MODULE,
- cfg->reg_read = snvs_lpgpr_read,
- cfg->reg_write = snvs_lpgpr_write,
-
- nvmem = nvmem_register(cfg);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
+ cfg->stride = 4;
+ cfg->word_size = 4;
+ cfg->size = dcfg->size,
+ cfg->owner = THIS_MODULE;
+ cfg->reg_read = snvs_lpgpr_read;
+ cfg->reg_write = snvs_lpgpr_write;
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int snvs_lpgpr_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
+ nvmem = devm_nvmem_register(dev, cfg);
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static const struct of_device_id snvs_lpgpr_dt_ids[] = {
{ .compatible = "fsl,imx6q-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx6q },
{ .compatible = "fsl,imx6ul-snvs-lpgpr",
.data = &snvs_lpgpr_cfg_imx6q },
+ { .compatible = "fsl,imx7d-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx7d },
{ },
};
MODULE_DEVICE_TABLE(of, snvs_lpgpr_dt_ids);
static struct platform_driver snvs_lpgpr_driver = {
.probe = snvs_lpgpr_probe,
- .remove = snvs_lpgpr_remove,
.driver = {
.name = "snvs_lpgpr",
.of_match_table = snvs_lpgpr_dt_ids,
@@ -152,5 +155,5 @@ static struct platform_driver snvs_lpgpr_driver = {
module_platform_driver(snvs_lpgpr_driver);
MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
-MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 Secure Non-Volatile Storage");
+MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 and i.MX7 Secure Non-Volatile Storage");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c
index 99bd54d85fcb..26bb637afe92 100644
--- a/drivers/nvmem/sunxi_sid.c
+++ b/drivers/nvmem/sunxi_sid.c
@@ -85,13 +85,14 @@ static int sunxi_sid_read(void *context, unsigned int offset,
}
static int sun8i_sid_register_readout(const struct sunxi_sid *sid,
- const unsigned int word)
+ const unsigned int offset,
+ u32 *out)
{
u32 reg_val;
int ret;
/* Set word, lock access, and set read command */
- reg_val = (word & SUN8I_SID_OFFSET_MASK)
+ reg_val = (offset & SUN8I_SID_OFFSET_MASK)
<< SUN8I_SID_OFFSET_SHIFT;
reg_val |= SUN8I_SID_OP_LOCK | SUN8I_SID_READ;
writel(reg_val, sid->base + SUN8I_SID_PRCTL);
@@ -101,7 +102,49 @@ static int sun8i_sid_register_readout(const struct sunxi_sid *sid,
if (ret)
return ret;
+ if (out)
+ *out = readl(sid->base + SUN8I_SID_RDKEY);
+
writel(0, sid->base + SUN8I_SID_PRCTL);
+
+ return 0;
+}
+
+/*
+ * On Allwinner H3, the value on the 0x200 offset of the SID controller seems
+ * to be not reliable at all.
+ * Read by the registers instead.
+ */
+static int sun8i_sid_read_byte_by_reg(const struct sunxi_sid *sid,
+ const unsigned int offset,
+ u8 *out)
+{
+ u32 word;
+ int ret;
+
+ ret = sun8i_sid_register_readout(sid, offset & ~0x03, &word);
+
+ if (ret)
+ return ret;
+
+ *out = (word >> ((offset & 0x3) * 8)) & 0xff;
+
+ return 0;
+}
+
+static int sun8i_sid_read_by_reg(void *context, unsigned int offset,
+ void *val, size_t bytes)
+{
+ struct sunxi_sid *sid = context;
+ u8 *buf = val;
+ int ret;
+
+ while (bytes--) {
+ ret = sun8i_sid_read_byte_by_reg(sid, offset++, buf++);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -131,26 +174,12 @@ static int sunxi_sid_probe(struct platform_device *pdev)
size = cfg->size;
- if (cfg->need_register_readout) {
- /*
- * H3's SID controller have a bug that the value at 0x200
- * offset is not the correct value when the hardware is reseted.
- * However, after doing a register-based read operation, the
- * value become right.
- * Do a full read operation here, but ignore its value
- * (as it's more fast to read by direct MMIO value than
- * with registers)
- */
- for (i = 0; i < (size >> 2); i++) {
- ret = sun8i_sid_register_readout(sid, i);
- if (ret)
- return ret;
- }
- }
-
econfig.size = size;
econfig.dev = dev;
- econfig.reg_read = sunxi_sid_read;
+ if (cfg->need_register_readout)
+ econfig.reg_read = sun8i_sid_read_by_reg;
+ else
+ econfig.reg_read = sunxi_sid_read;
econfig.priv = sid;
nvmem = nvmem_register(&econfig);
if (IS_ERR(nvmem))
@@ -163,7 +192,7 @@ static int sunxi_sid_probe(struct platform_device *pdev)
}
for (i = 0; i < size; i++)
- randomness[i] = sunxi_sid_read_byte(sid, i);
+ econfig.reg_read(sid, i, &randomness[i], 1);
add_device_randomness(randomness, size);
kfree(randomness);
diff --git a/drivers/nvmem/uniphier-efuse.c b/drivers/nvmem/uniphier-efuse.c
index be11880a1358..271f0b2ff86a 100644
--- a/drivers/nvmem/uniphier-efuse.c
+++ b/drivers/nvmem/uniphier-efuse.c
@@ -60,20 +60,9 @@ static int uniphier_efuse_probe(struct platform_device *pdev)
econfig.size = resource_size(res);
econfig.priv = priv;
econfig.dev = dev;
- nvmem = nvmem_register(&econfig);
- if (IS_ERR(nvmem))
- return PTR_ERR(nvmem);
+ nvmem = devm_nvmem_register(dev, &econfig);
- platform_set_drvdata(pdev, nvmem);
-
- return 0;
-}
-
-static int uniphier_efuse_remove(struct platform_device *pdev)
-{
- struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
- return nvmem_unregister(nvmem);
+ return PTR_ERR_OR_ZERO(nvmem);
}
static const struct of_device_id uniphier_efuse_of_match[] = {
@@ -84,7 +73,6 @@ MODULE_DEVICE_TABLE(of, uniphier_efuse_of_match);
static struct platform_driver uniphier_efuse_driver = {
.probe = uniphier_efuse_probe,
- .remove = uniphier_efuse_remove,
.driver = {
.name = "uniphier-efuse",
.of_match_table = uniphier_efuse_of_match,
diff --git a/drivers/nvmem/vf610-ocotp.c b/drivers/nvmem/vf610-ocotp.c
index 5ae9e002f195..4662309489db 100644
--- a/drivers/nvmem/vf610-ocotp.c
+++ b/drivers/nvmem/vf610-ocotp.c
@@ -217,21 +217,13 @@ static const struct of_device_id ocotp_of_match[] = {
};
MODULE_DEVICE_TABLE(of, ocotp_of_match);
-static int vf610_ocotp_remove(struct platform_device *pdev)
-{
- struct vf610_ocotp *ocotp_dev = platform_get_drvdata(pdev);
-
- return nvmem_unregister(ocotp_dev->nvmem);
-}
-
static int vf610_ocotp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct vf610_ocotp *ocotp_dev;
- ocotp_dev = devm_kzalloc(&pdev->dev,
- sizeof(struct vf610_ocotp), GFP_KERNEL);
+ ocotp_dev = devm_kzalloc(dev, sizeof(struct vf610_ocotp), GFP_KERNEL);
if (!ocotp_dev)
return -ENOMEM;
@@ -246,26 +238,20 @@ static int vf610_ocotp_probe(struct platform_device *pdev)
PTR_ERR(ocotp_dev->clk));
return PTR_ERR(ocotp_dev->clk);
}
+ ocotp_dev->dev = dev;
+ ocotp_dev->timing = vf610_ocotp_calculate_timing(ocotp_dev);
ocotp_config.size = resource_size(res);
ocotp_config.priv = ocotp_dev;
ocotp_config.dev = dev;
- ocotp_dev->nvmem = nvmem_register(&ocotp_config);
- if (IS_ERR(ocotp_dev->nvmem))
- return PTR_ERR(ocotp_dev->nvmem);
+ ocotp_dev->nvmem = devm_nvmem_register(dev, &ocotp_config);
- ocotp_dev->dev = dev;
- platform_set_drvdata(pdev, ocotp_dev);
-
- ocotp_dev->timing = vf610_ocotp_calculate_timing(ocotp_dev);
-
- return 0;
+ return PTR_ERR_OR_ZERO(ocotp_dev->nvmem);
}
static struct platform_driver vf610_ocotp_driver = {
.probe = vf610_ocotp_probe,
- .remove = vf610_ocotp_remove,
.driver = {
.name = "vf610-ocotp",
.of_match_table = ocotp_of_match,
diff --git a/drivers/parport/parport_ax88796.c b/drivers/parport/parport_ax88796.c
index 2fc91edb058d..bfe97c2a8d4c 100644
--- a/drivers/parport/parport_ax88796.c
+++ b/drivers/parport/parport_ax88796.c
@@ -273,18 +273,16 @@ static int parport_ax88796_probe(struct platform_device *pdev)
{
struct device *_dev = &pdev->dev;
struct ax_drvdata *dd;
- struct parport *pp = NULL;
+ struct parport *pp;
struct resource *res;
unsigned long size;
int spacing;
int irq;
int ret;
- dd = kzalloc(sizeof(struct ax_drvdata), GFP_KERNEL);
- if (dd == NULL) {
- dev_err(_dev, "no memory for private data\n");
+ dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+ if (!dd)
return -ENOMEM;
- }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index 489492b608cf..380916bff9e0 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -2646,6 +2646,7 @@ enum parport_pc_pci_cards {
netmos_9901,
netmos_9865,
quatech_sppxp100,
+ wch_ch382l,
};
@@ -2708,6 +2709,7 @@ static struct parport_pc_pci {
/* netmos_9901 */ { 1, { { 0, -1 }, } },
/* netmos_9865 */ { 1, { { 0, -1 }, } },
/* quatech_sppxp100 */ { 1, { { 0, 1 }, } },
+ /* wch_ch382l */ { 1, { { 2, -1 }, } },
};
static const struct pci_device_id parport_pc_pci_tbl[] = {
@@ -2797,6 +2799,8 @@ static const struct pci_device_id parport_pc_pci_tbl[] = {
/* Quatech SPPXP-100 Parallel port PCI ExpressCard */
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, quatech_sppxp100 },
+ /* WCH CH382L PCI-E single parallel port card */
+ { 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382l },
{ 0, } /* terminate list */
};
MODULE_DEVICE_TABLE(pci, parport_pc_pci_tbl);
diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c
index e15b4845f7c6..4f8b26033460 100644
--- a/drivers/parport/parport_serial.c
+++ b/drivers/parport/parport_serial.c
@@ -1,30 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Support for common PCI multi-I/O cards (which is most of them)
*
* Copyright (C) 2001 Tim Waugh <twaugh@redhat.com>
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- *
* Multi-function PCI cards are supposed to present separate logical
* devices on the bus. A common thing to do seems to be to just use
* one logical device with lots of base address registers for both
* parallel ports and serial ports. This driver is for dealing with
* that.
- *
*/
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
#include <linux/interrupt.h>
+#include <linux/module.h>
#include <linux/parport.h>
#include <linux/parport_pc.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
#include <linux/8250_pci.h>
enum parport_pc_pci_cards {
@@ -65,6 +59,7 @@ enum parport_pc_pci_cards {
wch_ch353_1s1p,
wch_ch353_2s1p,
wch_ch382_2s1p,
+ brainboxes_5s1p,
sunix_2s1p,
};
@@ -153,6 +148,7 @@ static struct parport_pc_pci cards[] = {
/* wch_ch353_1s1p*/ { 1, { { 1, -1}, } },
/* wch_ch353_2s1p*/ { 1, { { 2, -1}, } },
/* wch_ch382_2s1p*/ { 1, { { 2, -1}, } },
+ /* brainboxes_5s1p */ { 1, { { 3, -1 }, } },
/* sunix_2s1p */ { 1, { { 3, -1 }, } },
};
@@ -261,6 +257,10 @@ static struct pci_device_id parport_serial_pci_tbl[] = {
{ 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p},
{ 0x1c00, 0x3250, 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p},
+ /* BrainBoxes PX272/PX306 MIO card */
+ { PCI_VENDOR_ID_INTASHIELD, 0x4100,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_5s1p },
+
/*
* More SUNIX variations. At least one of these has part number
* '5079A but subdevice 0x102. That board reports 0x0708 as
@@ -504,6 +504,12 @@ static struct pciserial_board pci_parport_serial_boards[] = {
.uart_offset = 8,
.first_offset = 0xC0,
},
+ [brainboxes_5s1p] = {
+ .flags = FL_BASE2,
+ .num_ports = 5,
+ .base_baud = 921600,
+ .uart_offset = 8,
+ },
[sunix_2s1p] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 2,
@@ -527,12 +533,10 @@ static int serial_register(struct pci_dev *dev, const struct pci_device_id *id)
struct serial_private *serial;
board = &pci_parport_serial_boards[id->driver_data];
-
if (board->num_ports == 0)
return 0;
serial = pciserial_init_ports(dev, board);
-
if (IS_ERR(serial))
return PTR_ERR(serial);
@@ -561,10 +565,9 @@ static int parport_register(struct pci_dev *dev, const struct pci_device_id *id)
int irq;
if (priv->num_par == ARRAY_SIZE (priv->port)) {
- printk (KERN_WARNING
- "parport_serial: %s: only %zu parallel ports "
- "supported (%d reported)\n", pci_name (dev),
- ARRAY_SIZE(priv->port), card->numports);
+ dev_warn(&dev->dev,
+ "only %zu parallel ports supported (%d reported)\n",
+ ARRAY_SIZE(priv->port), card->numports);
break;
}
@@ -580,12 +583,12 @@ static int parport_register(struct pci_dev *dev, const struct pci_device_id *id)
irq = dev->irq;
if (irq == IRQ_NONE) {
dev_dbg(&dev->dev,
- "PCI parallel port detected: I/O at %#lx(%#lx)\n",
+ "PCI parallel port detected: I/O at %#lx(%#lx)\n",
io_lo, io_hi);
irq = PARPORT_IRQ_NONE;
} else {
dev_dbg(&dev->dev,
- "PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n",
+ "PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n",
io_lo, io_hi, irq);
}
port = parport_pc_probe_port (io_lo, io_hi, irq,
@@ -608,28 +611,26 @@ static int parport_serial_pci_probe(struct pci_dev *dev,
struct parport_serial_private *priv;
int err;
- priv = kzalloc (sizeof *priv, GFP_KERNEL);
+ priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+
pci_set_drvdata (dev, priv);
- err = pci_enable_device (dev);
- if (err) {
- kfree (priv);
+ err = pcim_enable_device(dev);
+ if (err)
return err;
- }
- if (parport_register (dev, id)) {
- kfree (priv);
- return -ENODEV;
- }
+ err = parport_register(dev, id);
+ if (err)
+ return err;
- if (serial_register (dev, id)) {
+ err = serial_register(dev, id);
+ if (err) {
int i;
for (i = 0; i < priv->num_par; i++)
parport_pc_unregister_port (priv->port[i]);
- kfree (priv);
- return -ENODEV;
+ return err;
}
return 0;
@@ -648,78 +649,47 @@ static void parport_serial_pci_remove(struct pci_dev *dev)
for (i = 0; i < priv->num_par; i++)
parport_pc_unregister_port (priv->port[i]);
- kfree (priv);
return;
}
-#ifdef CONFIG_PM
-static int parport_serial_pci_suspend(struct pci_dev *dev, pm_message_t state)
+static int __maybe_unused parport_serial_pci_suspend(struct device *dev)
{
- struct parport_serial_private *priv = pci_get_drvdata(dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct parport_serial_private *priv = pci_get_drvdata(pdev);
if (priv->serial)
pciserial_suspend_ports(priv->serial);
/* FIXME: What about parport? */
-
- pci_save_state(dev);
- pci_set_power_state(dev, pci_choose_state(dev, state));
return 0;
}
-static int parport_serial_pci_resume(struct pci_dev *dev)
+static int __maybe_unused parport_serial_pci_resume(struct device *dev)
{
- struct parport_serial_private *priv = pci_get_drvdata(dev);
- int err;
-
- pci_set_power_state(dev, PCI_D0);
- pci_restore_state(dev);
-
- /*
- * The device may have been disabled. Re-enable it.
- */
- err = pci_enable_device(dev);
- if (err) {
- printk(KERN_ERR "parport_serial: %s: error enabling "
- "device for resume (%d)\n", pci_name(dev), err);
- return err;
- }
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct parport_serial_private *priv = pci_get_drvdata(pdev);
if (priv->serial)
pciserial_resume_ports(priv->serial);
/* FIXME: What about parport? */
-
return 0;
}
-#endif
+
+static SIMPLE_DEV_PM_OPS(parport_serial_pm_ops,
+ parport_serial_pci_suspend, parport_serial_pci_resume);
static struct pci_driver parport_serial_pci_driver = {
.name = "parport_serial",
.id_table = parport_serial_pci_tbl,
.probe = parport_serial_pci_probe,
.remove = parport_serial_pci_remove,
-#ifdef CONFIG_PM
- .suspend = parport_serial_pci_suspend,
- .resume = parport_serial_pci_resume,
-#endif
+ .driver = {
+ .pm = &parport_serial_pm_ops,
+ },
};
-
-
-static int __init parport_serial_init (void)
-{
- return pci_register_driver (&parport_serial_pci_driver);
-}
-
-static void __exit parport_serial_exit (void)
-{
- pci_unregister_driver (&parport_serial_pci_driver);
- return;
-}
+module_pci_driver(parport_serial_pci_driver);
MODULE_AUTHOR("Tim Waugh <twaugh@redhat.com>");
MODULE_DESCRIPTION("Driver for common parallel+serial multi-I/O PCI cards");
MODULE_LICENSE("GPL");
-
-module_init(parport_serial_init);
-module_exit(parport_serial_exit);
diff --git a/drivers/pps/clients/pps_parport.c b/drivers/pps/clients/pps_parport.c
index 83797d89c30f..4db824f88d00 100644
--- a/drivers/pps/clients/pps_parport.c
+++ b/drivers/pps/clients/pps_parport.c
@@ -49,6 +49,7 @@ MODULE_PARM_DESC(clear_wait,
" zero turns clear edge capture off entirely");
module_param(clear_wait, uint, 0);
+static DEFINE_IDA(pps_client_index);
/* internal per port structure */
struct pps_client_pp {
@@ -56,6 +57,7 @@ struct pps_client_pp {
struct pps_device *pps; /* PPS device */
unsigned int cw; /* port clear timeout */
unsigned int cw_err; /* number of timeouts */
+ int index; /* device number */
};
static inline int signal_is_set(struct parport *port)
@@ -136,6 +138,8 @@ out_both:
static void parport_attach(struct parport *port)
{
+ struct pardev_cb pps_client_cb;
+ int index;
struct pps_client_pp *device;
struct pps_source_info info = {
.name = KBUILD_MODNAME,
@@ -154,8 +158,15 @@ static void parport_attach(struct parport *port)
return;
}
- device->pardev = parport_register_device(port, KBUILD_MODNAME,
- NULL, NULL, parport_irq, PARPORT_FLAG_EXCL, device);
+ index = ida_simple_get(&pps_client_index, 0, 0, GFP_KERNEL);
+ memset(&pps_client_cb, 0, sizeof(pps_client_cb));
+ pps_client_cb.private = device;
+ pps_client_cb.irq_func = parport_irq;
+ pps_client_cb.flags = PARPORT_FLAG_EXCL;
+ device->pardev = parport_register_dev_model(port,
+ KBUILD_MODNAME,
+ &pps_client_cb,
+ index);
if (!device->pardev) {
pr_err("couldn't register with %s\n", port->name);
goto err_free;
@@ -176,6 +187,7 @@ static void parport_attach(struct parport *port)
device->cw = clear_wait;
port->ops->enable_irq(port);
+ device->index = index;
pr_info("attached to %s\n", port->name);
@@ -186,6 +198,7 @@ err_release_dev:
err_unregister_dev:
parport_unregister_device(device->pardev);
err_free:
+ ida_simple_remove(&pps_client_index, index);
kfree(device);
}
@@ -205,13 +218,15 @@ static void parport_detach(struct parport *port)
pps_unregister_source(device->pps);
parport_release(pardev);
parport_unregister_device(pardev);
+ ida_simple_remove(&pps_client_index, device->index);
kfree(device);
}
static struct parport_driver pps_parport_driver = {
.name = KBUILD_MODNAME,
- .attach = parport_attach,
+ .match_port = parport_attach,
.detach = parport_detach,
+ .devmodel = true,
};
/* module staff */
diff --git a/drivers/pps/generators/pps_gen_parport.c b/drivers/pps/generators/pps_gen_parport.c
index 51cfde6afffd..7fd36cac063b 100644
--- a/drivers/pps/generators/pps_gen_parport.c
+++ b/drivers/pps/generators/pps_gen_parport.c
@@ -192,13 +192,18 @@ static inline ktime_t next_intr_time(struct pps_generator_pp *dev)
static void parport_attach(struct parport *port)
{
+ struct pardev_cb pps_cb;
+
if (attached) {
/* we already have a port */
return;
}
- device.pardev = parport_register_device(port, KBUILD_MODNAME,
- NULL, NULL, NULL, PARPORT_FLAG_EXCL, &device);
+ memset(&pps_cb, 0, sizeof(pps_cb));
+ pps_cb.private = &device;
+ pps_cb.flags = PARPORT_FLAG_EXCL;
+ device.pardev = parport_register_dev_model(port, KBUILD_MODNAME,
+ &pps_cb, 0);
if (!device.pardev) {
pr_err("couldn't register with %s\n", port->name);
return;
@@ -236,8 +241,9 @@ static void parport_detach(struct parport *port)
static struct parport_driver pps_gen_parport_driver = {
.name = KBUILD_MODNAME,
- .attach = parport_attach,
+ .match_port = parport_attach,
.detach = parport_detach,
+ .devmodel = true,
};
/* module staff */
diff --git a/drivers/siox/siox-core.c b/drivers/siox/siox-core.c
index fdfcdea25867..16590dfaafa4 100644
--- a/drivers/siox/siox-core.c
+++ b/drivers/siox/siox-core.c
@@ -594,7 +594,7 @@ static ssize_t device_add_store(struct device *dev,
size_t inbytes = 0, outbytes = 0;
u8 statustype = 0;
- ret = sscanf(buf, "%20s %zu %zu %hhu", type, &inbytes,
+ ret = sscanf(buf, "%19s %zu %zu %hhu", type, &inbytes,
&outbytes, &statustype);
if (ret != 3 && ret != 4)
return -EINVAL;
diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c
index 4988a8f4d905..7ddfc675b131 100644
--- a/drivers/slimbus/core.c
+++ b/drivers/slimbus/core.c
@@ -141,7 +141,7 @@ static struct slim_device *slim_alloc_device(struct slim_controller *ctrl,
sbdev->e_addr = *eaddr;
ret = slim_add_device(ctrl, sbdev, node);
if (ret) {
- kfree(sbdev);
+ put_device(&sbdev->dev);
return NULL;
}
diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c
index 8ca549032c27..f695a7e8c314 100644
--- a/drivers/uio/uio_hv_generic.c
+++ b/drivers/uio/uio_hv_generic.c
@@ -121,6 +121,94 @@ static void hv_uio_rescind(struct vmbus_channel *channel)
uio_event_notify(&pdata->info);
}
+/*
+ * Handle fault when looking for sub channel ring buffer
+ * Subchannel ring buffer is same as resource 0 which is main ring buffer
+ * This is derived from uio_vma_fault
+ */
+static int hv_uio_vma_fault(struct vm_fault *vmf)
+{
+ struct vm_area_struct *vma = vmf->vma;
+ void *ring_buffer = vma->vm_private_data;
+ struct page *page;
+ void *addr;
+
+ addr = ring_buffer + (vmf->pgoff << PAGE_SHIFT);
+ page = virt_to_page(addr);
+ get_page(page);
+ vmf->page = page;
+ return 0;
+}
+
+static const struct vm_operations_struct hv_uio_vm_ops = {
+ .fault = hv_uio_vma_fault,
+};
+
+/* Sysfs API to allow mmap of the ring buffers */
+static int hv_uio_ring_mmap(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ struct vm_area_struct *vma)
+{
+ struct vmbus_channel *channel
+ = container_of(kobj, struct vmbus_channel, kobj);
+ unsigned long requested_pages, actual_pages;
+
+ if (vma->vm_end < vma->vm_start)
+ return -EINVAL;
+
+ /* only allow 0 for now */
+ if (vma->vm_pgoff > 0)
+ return -EINVAL;
+
+ requested_pages = vma_pages(vma);
+ actual_pages = 2 * HV_RING_SIZE;
+ if (requested_pages > actual_pages)
+ return -EINVAL;
+
+ vma->vm_private_data = channel->ringbuffer_pages;
+ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+ vma->vm_ops = &hv_uio_vm_ops;
+ return 0;
+}
+
+static struct bin_attribute ring_buffer_bin_attr __ro_after_init = {
+ .attr = {
+ .name = "ring",
+ .mode = 0600,
+ /* size is set at init time */
+ },
+ .mmap = hv_uio_ring_mmap,
+};
+
+/* Callback from VMBUS subystem when new channel created. */
+static void
+hv_uio_new_channel(struct vmbus_channel *new_sc)
+{
+ struct hv_device *hv_dev = new_sc->primary_channel->device_obj;
+ struct device *device = &hv_dev->device;
+ struct hv_uio_private_data *pdata = hv_get_drvdata(hv_dev);
+ const size_t ring_bytes = HV_RING_SIZE * PAGE_SIZE;
+ int ret;
+
+ /* Create host communication ring */
+ ret = vmbus_open(new_sc, ring_bytes, ring_bytes, NULL, 0,
+ hv_uio_channel_cb, pdata);
+ if (ret) {
+ dev_err(device, "vmbus_open subchannel failed: %d\n", ret);
+ return;
+ }
+
+ /* Disable interrupts on sub channel */
+ new_sc->inbound.ring_buffer->interrupt_mask = 1;
+ set_channel_read_mode(new_sc, HV_CALL_ISR);
+
+ ret = sysfs_create_bin_file(&new_sc->kobj, &ring_buffer_bin_attr);
+ if (ret) {
+ dev_err(device, "sysfs create ring bin file failed; %d\n", ret);
+ vmbus_close(new_sc);
+ }
+}
+
static void
hv_uio_cleanup(struct hv_device *dev, struct hv_uio_private_data *pdata)
{
@@ -236,6 +324,7 @@ hv_uio_probe(struct hv_device *dev,
}
vmbus_set_chn_rescind_callback(dev->channel, hv_uio_rescind);
+ vmbus_set_sc_create_callback(dev->channel, hv_uio_new_channel);
hv_set_drvdata(dev, pdata);
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index 0c2a5a8327bd..80a778b02f28 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -706,6 +706,7 @@ static int __w1_attach_slave_device(struct w1_slave *sl)
dev_err(&sl->dev,
"Device registration [%s] failed. err=%d\n",
dev_name(&sl->dev), err);
+ put_device(&sl->dev);
return err;
}
w1_family_notify(BUS_NOTIFY_ADD_DEVICE, sl);