diff options
Diffstat (limited to 'drivers')
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); |