diff options
Diffstat (limited to 'drivers')
245 files changed, 5877 insertions, 4142 deletions
diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index 1982310e6d83..da198b864107 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -428,6 +428,9 @@ acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node, obj_desc->method.mutex->mutex. original_sync_level = obj_desc->method.mutex->mutex.sync_level; + + obj_desc->method.mutex->mutex.thread_id = + acpi_os_get_thread_id(); } } diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index d0f35e63640b..63cc9dbe4f3b 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -287,8 +287,11 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, offset); rc = -ENXIO; } - } else + } else { rc = 0; + if (cmd_rc) + *cmd_rc = xlat_status(buf, cmd); + } out: ACPI_FREE(out_obj); diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 5083f85efea7..cfa936a32513 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -202,6 +202,14 @@ config SATA_FSL If unsure, say N. +config SATA_AHCI_SEATTLE + tristate "AMD Seattle 6.0Gbps AHCI SATA host controller support" + depends on ARCH_SEATTLE + help + This option enables support for AMD Seattle SATA host controller. + + If unsure, say N + config SATA_INIC162X tristate "Initio 162x SATA support (Very Experimental)" depends on PCI diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 18579521464e..0b2afb7e5f35 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_ATA) += libata.o # non-SFF interface obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o +obj-$(CONFIG_SATA_AHCI_SEATTLE) += ahci_seattle.o libahci.o libahci_platform.o obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o libahci_platform.o obj-$(CONFIG_SATA_FSL) += sata_fsl.o obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 40442332bfa7..62a04c8fb5c9 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -51,6 +51,9 @@ static int ahci_probe(struct platform_device *pdev) if (rc) return rc; + of_property_read_u32(dev->of_node, + "ports-implemented", &hpriv->force_port_map); + if (of_device_is_compatible(dev->of_node, "hisilicon,hisi-ahci")) hpriv->flags |= AHCI_HFLAG_NO_FBS | AHCI_HFLAG_NO_NCQ; diff --git a/drivers/ata/ahci_seattle.c b/drivers/ata/ahci_seattle.c new file mode 100644 index 000000000000..6e702ab57220 --- /dev/null +++ b/drivers/ata/ahci_seattle.c @@ -0,0 +1,210 @@ +/* + * AMD Seattle AHCI SATA driver + * + * Copyright (c) 2015, Advanced Micro Devices + * Author: Brijesh Singh <brijesh.singh@amd.com> + * + * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/device.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/libata.h> +#include <linux/ahci_platform.h> +#include <linux/acpi.h> +#include <linux/pci_ids.h> +#include "ahci.h" + +/* SGPIO Control Register definition + * + * Bit Type Description + * 31 RW OD7.2 (activity) + * 30 RW OD7.1 (locate) + * 29 RW OD7.0 (fault) + * 28...8 RW OD6.2...OD0.0 (3bits per port, 1 bit per LED) + * 7 RO SGPIO feature flag + * 6:4 RO Reserved + * 3:0 RO Number of ports (0 means no port supported) + */ +#define ACTIVITY_BIT_POS(x) (8 + (3 * x)) +#define LOCATE_BIT_POS(x) (ACTIVITY_BIT_POS(x) + 1) +#define FAULT_BIT_POS(x) (LOCATE_BIT_POS(x) + 1) + +#define ACTIVITY_MASK 0x00010000 +#define LOCATE_MASK 0x00080000 +#define FAULT_MASK 0x00400000 + +#define DRV_NAME "ahci-seattle" + +static ssize_t seattle_transmit_led_message(struct ata_port *ap, u32 state, + ssize_t size); + +struct seattle_plat_data { + void __iomem *sgpio_ctrl; +}; + +static struct ata_port_operations ahci_port_ops = { + .inherits = &ahci_ops, +}; + +static const struct ata_port_info ahci_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_port_ops, +}; + +static struct ata_port_operations ahci_seattle_ops = { + .inherits = &ahci_ops, + .transmit_led_message = seattle_transmit_led_message, +}; + +static const struct ata_port_info ahci_port_seattle_info = { + .flags = AHCI_FLAG_COMMON | ATA_FLAG_EM | ATA_FLAG_SW_ACTIVITY, + .link_flags = ATA_LFLAG_SW_ACTIVITY, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_seattle_ops, +}; + +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + +static ssize_t seattle_transmit_led_message(struct ata_port *ap, u32 state, + ssize_t size) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + struct seattle_plat_data *plat_data = hpriv->plat_data; + unsigned long flags; + int pmp; + struct ahci_em_priv *emp; + u32 val; + + /* get the slot number from the message */ + pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8; + if (pmp >= EM_MAX_SLOTS) + return -EINVAL; + emp = &pp->em_priv[pmp]; + + val = ioread32(plat_data->sgpio_ctrl); + if (state & ACTIVITY_MASK) + val |= 1 << ACTIVITY_BIT_POS((ap->port_no)); + else + val &= ~(1 << ACTIVITY_BIT_POS((ap->port_no))); + + if (state & LOCATE_MASK) + val |= 1 << LOCATE_BIT_POS((ap->port_no)); + else + val &= ~(1 << LOCATE_BIT_POS((ap->port_no))); + + if (state & FAULT_MASK) + val |= 1 << FAULT_BIT_POS((ap->port_no)); + else + val &= ~(1 << FAULT_BIT_POS((ap->port_no))); + + iowrite32(val, plat_data->sgpio_ctrl); + + spin_lock_irqsave(ap->lock, flags); + + /* save off new led state for port/slot */ + emp->led_state = state; + + spin_unlock_irqrestore(ap->lock, flags); + + return size; +} + +static const struct ata_port_info *ahci_seattle_get_port_info( + struct platform_device *pdev, struct ahci_host_priv *hpriv) +{ + struct device *dev = &pdev->dev; + struct seattle_plat_data *plat_data; + u32 val; + + plat_data = devm_kzalloc(dev, sizeof(*plat_data), GFP_KERNEL); + if (IS_ERR(plat_data)) + return &ahci_port_info; + + plat_data->sgpio_ctrl = devm_ioremap_resource(dev, + platform_get_resource(pdev, IORESOURCE_MEM, 1)); + if (IS_ERR(plat_data->sgpio_ctrl)) + return &ahci_port_info; + + val = ioread32(plat_data->sgpio_ctrl); + + if (!(val & 0xf)) + return &ahci_port_info; + + hpriv->em_loc = 0; + hpriv->em_buf_sz = 4; + hpriv->em_msg_type = EM_MSG_TYPE_LED; + hpriv->plat_data = plat_data; + + dev_info(dev, "SGPIO LED control is enabled.\n"); + return &ahci_port_seattle_info; +} + +static int ahci_seattle_probe(struct platform_device *pdev) +{ + int rc; + struct ahci_host_priv *hpriv; + + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + rc = ahci_platform_init_host(pdev, hpriv, + ahci_seattle_get_port_info(pdev, hpriv), + &ahci_platform_sht); + if (rc) + goto disable_resources; + + return 0; +disable_resources: + ahci_platform_disable_resources(hpriv); + return rc; +} + +static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, + ahci_platform_resume); + +static const struct acpi_device_id ahci_acpi_match[] = { + { "AMDI0600", 0 }, + {} +}; +MODULE_DEVICE_TABLE(acpi, ahci_acpi_match); + +static struct platform_driver ahci_seattle_driver = { + .probe = ahci_seattle_probe, + .remove = ata_platform_remove_one, + .driver = { + .name = DRV_NAME, + .acpi_match_table = ahci_acpi_match, + .pm = &ahci_pm_ops, + }, +}; +module_platform_driver(ahci_seattle_driver); + +MODULE_DESCRIPTION("Seattle AHCI SATA platform driver"); +MODULE_AUTHOR("Brijesh Singh <brijesh.singh@amd.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 3982054060b8..a5d7c1c2a05e 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -507,6 +507,7 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) dev_info(dev, "forcing port_map 0x%x -> 0x%x\n", port_map, hpriv->force_port_map); port_map = hpriv->force_port_map; + hpriv->saved_port_map = port_map; } if (hpriv->mask_port_map) { diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index 0e64a1b5e62a..3657ac1cb801 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -159,7 +159,7 @@ int of_pm_clk_add_clks(struct device *dev) count = of_count_phandle_with_args(dev->of_node, "clocks", "#clock-cells"); - if (count == 0) + if (count <= 0) return -ENODEV; clks = kcalloc(count, sizeof(*clks), GFP_KERNEL); diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 56705b52758e..de23b648fce3 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -229,17 +229,6 @@ static int genpd_poweron(struct generic_pm_domain *genpd, unsigned int depth) return ret; } -static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) -{ - return GENPD_DEV_CALLBACK(genpd, int, save_state, dev); -} - -static int genpd_restore_dev(struct generic_pm_domain *genpd, - struct device *dev) -{ - return GENPD_DEV_CALLBACK(genpd, int, restore_state, dev); -} - static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, unsigned long val, void *ptr) { @@ -372,17 +361,63 @@ static void genpd_power_off_work_fn(struct work_struct *work) } /** - * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. + * __genpd_runtime_suspend - walk the hierarchy of ->runtime_suspend() callbacks + * @dev: Device to handle. + */ +static int __genpd_runtime_suspend(struct device *dev) +{ + int (*cb)(struct device *__dev); + + if (dev->type && dev->type->pm) + cb = dev->type->pm->runtime_suspend; + else if (dev->class && dev->class->pm) + cb = dev->class->pm->runtime_suspend; + else if (dev->bus && dev->bus->pm) + cb = dev->bus->pm->runtime_suspend; + else + cb = NULL; + + if (!cb && dev->driver && dev->driver->pm) + cb = dev->driver->pm->runtime_suspend; + + return cb ? cb(dev) : 0; +} + +/** + * __genpd_runtime_resume - walk the hierarchy of ->runtime_resume() callbacks + * @dev: Device to handle. + */ +static int __genpd_runtime_resume(struct device *dev) +{ + int (*cb)(struct device *__dev); + + if (dev->type && dev->type->pm) + cb = dev->type->pm->runtime_resume; + else if (dev->class && dev->class->pm) + cb = dev->class->pm->runtime_resume; + else if (dev->bus && dev->bus->pm) + cb = dev->bus->pm->runtime_resume; + else + cb = NULL; + + if (!cb && dev->driver && dev->driver->pm) + cb = dev->driver->pm->runtime_resume; + + return cb ? cb(dev) : 0; +} + +/** + * genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. * @dev: Device to suspend. * * Carry out a runtime suspend of a device under the assumption that its * pm_domain field points to the domain member of an object of type * struct generic_pm_domain representing a PM domain consisting of I/O devices. */ -static int pm_genpd_runtime_suspend(struct device *dev) +static int genpd_runtime_suspend(struct device *dev) { struct generic_pm_domain *genpd; - bool (*stop_ok)(struct device *__dev); + bool (*suspend_ok)(struct device *__dev); struct gpd_timing_data *td = &dev_gpd_data(dev)->td; bool runtime_pm = pm_runtime_enabled(dev); ktime_t time_start; @@ -401,21 +436,21 @@ static int pm_genpd_runtime_suspend(struct device *dev) * runtime PM is disabled. Under these circumstances, we shall skip * validating/measuring the PM QoS latency. */ - stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; - if (runtime_pm && stop_ok && !stop_ok(dev)) + suspend_ok = genpd->gov ? genpd->gov->suspend_ok : NULL; + if (runtime_pm && suspend_ok && !suspend_ok(dev)) return -EBUSY; /* Measure suspend latency. */ if (runtime_pm) time_start = ktime_get(); - ret = genpd_save_dev(genpd, dev); + ret = __genpd_runtime_suspend(dev); if (ret) return ret; ret = genpd_stop_dev(genpd, dev); if (ret) { - genpd_restore_dev(genpd, dev); + __genpd_runtime_resume(dev); return ret; } @@ -446,14 +481,14 @@ static int pm_genpd_runtime_suspend(struct device *dev) } /** - * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain. + * genpd_runtime_resume - Resume a device belonging to I/O PM domain. * @dev: Device to resume. * * Carry out a runtime resume of a device under the assumption that its * pm_domain field points to the domain member of an object of type * struct generic_pm_domain representing a PM domain consisting of I/O devices. */ -static int pm_genpd_runtime_resume(struct device *dev) +static int genpd_runtime_resume(struct device *dev) { struct generic_pm_domain *genpd; struct gpd_timing_data *td = &dev_gpd_data(dev)->td; @@ -491,7 +526,7 @@ static int pm_genpd_runtime_resume(struct device *dev) if (ret) goto err_poweroff; - ret = genpd_restore_dev(genpd, dev); + ret = __genpd_runtime_resume(dev); if (ret) goto err_stop; @@ -695,15 +730,6 @@ static int pm_genpd_prepare(struct device *dev) * at this point and a system wakeup event should be reported if it's * set up to wake up the system from sleep states. */ - pm_runtime_get_noresume(dev); - if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) - pm_wakeup_event(dev, 0); - - if (pm_wakeup_pending()) { - pm_runtime_put(dev); - return -EBUSY; - } - if (resume_needed(dev, genpd)) pm_runtime_resume(dev); @@ -716,10 +742,8 @@ static int pm_genpd_prepare(struct device *dev) mutex_unlock(&genpd->lock); - if (genpd->suspend_power_off) { - pm_runtime_put_noidle(dev); + if (genpd->suspend_power_off) return 0; - } /* * The PM domain must be in the GPD_STATE_ACTIVE state at this point, @@ -741,7 +765,6 @@ static int pm_genpd_prepare(struct device *dev) pm_runtime_enable(dev); } - pm_runtime_put(dev); return ret; } @@ -1427,54 +1450,6 @@ out: } EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain); -/* Default device callbacks for generic PM domains. */ - -/** - * pm_genpd_default_save_state - Default "save device state" for PM domains. - * @dev: Device to handle. - */ -static int pm_genpd_default_save_state(struct device *dev) -{ - int (*cb)(struct device *__dev); - - if (dev->type && dev->type->pm) - cb = dev->type->pm->runtime_suspend; - else if (dev->class && dev->class->pm) - cb = dev->class->pm->runtime_suspend; - else if (dev->bus && dev->bus->pm) - cb = dev->bus->pm->runtime_suspend; - else - cb = NULL; - - if (!cb && dev->driver && dev->driver->pm) - cb = dev->driver->pm->runtime_suspend; - - return cb ? cb(dev) : 0; -} - -/** - * pm_genpd_default_restore_state - Default PM domains "restore device state". - * @dev: Device to handle. - */ -static int pm_genpd_default_restore_state(struct device *dev) -{ - int (*cb)(struct device *__dev); - - if (dev->type && dev->type->pm) - cb = dev->type->pm->runtime_resume; - else if (dev->class && dev->class->pm) - cb = dev->class->pm->runtime_resume; - else if (dev->bus && dev->bus->pm) - cb = dev->bus->pm->runtime_resume; - else - cb = NULL; - - if (!cb && dev->driver && dev->driver->pm) - cb = dev->driver->pm->runtime_resume; - - return cb ? cb(dev) : 0; -} - /** * pm_genpd_init - Initialize a generic I/O PM domain object. * @genpd: PM domain object to initialize. @@ -1498,8 +1473,8 @@ void pm_genpd_init(struct generic_pm_domain *genpd, genpd->device_count = 0; genpd->max_off_time_ns = -1; genpd->max_off_time_changed = true; - genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; - genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; + genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; + genpd->domain.ops.runtime_resume = genpd_runtime_resume; genpd->domain.ops.prepare = pm_genpd_prepare; genpd->domain.ops.suspend = pm_genpd_suspend; genpd->domain.ops.suspend_late = pm_genpd_suspend_late; @@ -1520,8 +1495,6 @@ void pm_genpd_init(struct generic_pm_domain *genpd, genpd->domain.ops.restore_early = pm_genpd_resume_early; genpd->domain.ops.restore = pm_genpd_resume; genpd->domain.ops.complete = pm_genpd_complete; - genpd->dev_ops.save_state = pm_genpd_default_save_state; - genpd->dev_ops.restore_state = pm_genpd_default_restore_state; if (genpd->flags & GENPD_FLAG_PM_CLK) { genpd->dev_ops.stop = pm_clk_suspend; diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c index 00a5436dd44b..2e0fce711135 100644 --- a/drivers/base/power/domain_governor.c +++ b/drivers/base/power/domain_governor.c @@ -37,10 +37,10 @@ static int dev_update_qos_constraint(struct device *dev, void *data) } /** - * default_stop_ok - Default PM domain governor routine for stopping devices. + * default_suspend_ok - Default PM domain governor routine to suspend devices. * @dev: Device to check. */ -static bool default_stop_ok(struct device *dev) +static bool default_suspend_ok(struct device *dev) { struct gpd_timing_data *td = &dev_gpd_data(dev)->td; unsigned long flags; @@ -51,13 +51,13 @@ static bool default_stop_ok(struct device *dev) spin_lock_irqsave(&dev->power.lock, flags); if (!td->constraint_changed) { - bool ret = td->cached_stop_ok; + bool ret = td->cached_suspend_ok; spin_unlock_irqrestore(&dev->power.lock, flags); return ret; } td->constraint_changed = false; - td->cached_stop_ok = false; + td->cached_suspend_ok = false; td->effective_constraint_ns = -1; constraint_ns = __dev_pm_qos_read_value(dev); @@ -83,13 +83,13 @@ static bool default_stop_ok(struct device *dev) return false; } td->effective_constraint_ns = constraint_ns; - td->cached_stop_ok = constraint_ns >= 0; + td->cached_suspend_ok = constraint_ns >= 0; /* * The children have been suspended already, so we don't need to take - * their stop latencies into account here. + * their suspend latencies into account here. */ - return td->cached_stop_ok; + return td->cached_suspend_ok; } /** @@ -150,7 +150,7 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd, */ td = &to_gpd_data(pdd)->td; constraint_ns = td->effective_constraint_ns; - /* default_stop_ok() need not be called before us. */ + /* default_suspend_ok() need not be called before us. */ if (constraint_ns < 0) { constraint_ns = dev_pm_qos_read_value(pdd->dev); constraint_ns *= NSEC_PER_USEC; @@ -227,7 +227,7 @@ static bool always_on_power_down_ok(struct dev_pm_domain *domain) } struct dev_power_governor simple_qos_governor = { - .stop_ok = default_stop_ok, + .suspend_ok = default_suspend_ok, .power_down_ok = default_power_down_ok, }; @@ -236,5 +236,5 @@ struct dev_power_governor simple_qos_governor = { */ struct dev_power_governor pm_domain_always_on_gov = { .power_down_ok = always_on_power_down_ok, - .stop_ok = default_stop_ok, + .suspend_ok = default_suspend_ok, }; diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 6e7c3ccea24b..c81667d4bb60 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1556,7 +1556,6 @@ int dpm_suspend(pm_message_t state) static int device_prepare(struct device *dev, pm_message_t state) { int (*callback)(struct device *) = NULL; - char *info = NULL; int ret = 0; if (dev->power.syscore) @@ -1579,24 +1578,17 @@ static int device_prepare(struct device *dev, pm_message_t state) goto unlock; } - if (dev->pm_domain) { - info = "preparing power domain "; + if (dev->pm_domain) callback = dev->pm_domain->ops.prepare; - } else if (dev->type && dev->type->pm) { - info = "preparing type "; + else if (dev->type && dev->type->pm) callback = dev->type->pm->prepare; - } else if (dev->class && dev->class->pm) { - info = "preparing class "; + else if (dev->class && dev->class->pm) callback = dev->class->pm->prepare; - } else if (dev->bus && dev->bus->pm) { - info = "preparing bus "; + else if (dev->bus && dev->bus->pm) callback = dev->bus->pm->prepare; - } - if (!callback && dev->driver && dev->driver->pm) { - info = "preparing driver "; + if (!callback && dev->driver && dev->driver->pm) callback = dev->driver->pm->prepare; - } if (callback) ret = callback(dev); diff --git a/drivers/base/power/opp/Makefile b/drivers/base/power/opp/Makefile index 19837ef04d8e..e70ceb406fe9 100644 --- a/drivers/base/power/opp/Makefile +++ b/drivers/base/power/opp/Makefile @@ -1,3 +1,4 @@ ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG obj-y += core.o cpu.o +obj-$(CONFIG_OF) += of.o obj-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index 433b60092972..7c04c87738a6 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c @@ -18,7 +18,6 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/device.h> -#include <linux/of.h> #include <linux/export.h> #include <linux/regulator/consumer.h> @@ -29,7 +28,7 @@ * from here, with each opp_table containing the list of opps it supports in * various states of availability. */ -static LIST_HEAD(opp_tables); +LIST_HEAD(opp_tables); /* Lock to allow exclusive modification to the device and opp lists */ DEFINE_MUTEX(opp_table_lock); @@ -53,26 +52,6 @@ static struct opp_device *_find_opp_dev(const struct device *dev, return NULL; } -static struct opp_table *_managed_opp(const struct device_node *np) -{ - struct opp_table *opp_table; - - list_for_each_entry_rcu(opp_table, &opp_tables, node) { - if (opp_table->np == np) { - /* - * Multiple devices can point to the same OPP table and - * so will have same node-pointer, np. - * - * But the OPPs will be considered as shared only if the - * OPP table contains a "opp-shared" property. - */ - return opp_table->shared_opp ? opp_table : NULL; - } - } - - return NULL; -} - /** * _find_opp_table() - find opp_table struct using device pointer * @dev: device pointer used to lookup OPP table @@ -259,9 +238,6 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) reg = opp_table->regulator; if (IS_ERR(reg)) { /* Regulator may not be required for device */ - if (reg) - dev_err(dev, "%s: Invalid regulator (%ld)\n", __func__, - PTR_ERR(reg)); rcu_read_unlock(); return 0; } @@ -760,7 +736,6 @@ static struct opp_table *_add_opp_table(struct device *dev) { struct opp_table *opp_table; struct opp_device *opp_dev; - struct device_node *np; int ret; /* Check for existing table for 'dev' first */ @@ -784,20 +759,7 @@ static struct opp_table *_add_opp_table(struct device *dev) return NULL; } - /* - * Only required for backward compatibility with v1 bindings, but isn't - * harmful for other cases. And so we do it unconditionally. - */ - np = of_node_get(dev->of_node); - if (np) { - u32 val; - - if (!of_property_read_u32(np, "clock-latency", &val)) - opp_table->clock_latency_ns_max = val; - of_property_read_u32(np, "voltage-tolerance", - &opp_table->voltage_tolerance_v1); - of_node_put(np); - } + _of_init_opp_table(opp_table, dev); /* Set regulator to a non-NULL error value */ opp_table->regulator = ERR_PTR(-ENXIO); @@ -893,8 +855,8 @@ static void _kfree_opp_rcu(struct rcu_head *head) * It is assumed that the caller holds required mutex for an RCU updater * strategy. */ -static void _opp_remove(struct opp_table *opp_table, - struct dev_pm_opp *opp, bool notify) +void _opp_remove(struct opp_table *opp_table, struct dev_pm_opp *opp, + bool notify) { /* * Notify the changes in the availability of the operable @@ -955,8 +917,8 @@ unlock: } EXPORT_SYMBOL_GPL(dev_pm_opp_remove); -static struct dev_pm_opp *_allocate_opp(struct device *dev, - struct opp_table **opp_table) +struct dev_pm_opp *_allocate_opp(struct device *dev, + struct opp_table **opp_table) { struct dev_pm_opp *opp; @@ -992,8 +954,8 @@ static bool _opp_supported_by_regulators(struct dev_pm_opp *opp, return true; } -static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, - struct opp_table *opp_table) +int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, + struct opp_table *opp_table) { struct dev_pm_opp *opp; struct list_head *head = &opp_table->opp_list; @@ -1069,8 +1031,8 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, * Duplicate OPPs (both freq and volt are same) and !opp->available * -ENOMEM Memory allocation failure */ -static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt, - bool dynamic) +int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt, + bool dynamic) { struct opp_table *opp_table; struct dev_pm_opp *new_opp; @@ -1115,83 +1077,6 @@ unlock: return ret; } -/* TODO: Support multiple regulators */ -static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, - struct opp_table *opp_table) -{ - u32 microvolt[3] = {0}; - u32 val; - int count, ret; - struct property *prop = NULL; - char name[NAME_MAX]; - - /* Search for "opp-microvolt-<name>" */ - if (opp_table->prop_name) { - snprintf(name, sizeof(name), "opp-microvolt-%s", - opp_table->prop_name); - prop = of_find_property(opp->np, name, NULL); - } - - if (!prop) { - /* Search for "opp-microvolt" */ - sprintf(name, "opp-microvolt"); - prop = of_find_property(opp->np, name, NULL); - - /* Missing property isn't a problem, but an invalid entry is */ - if (!prop) - return 0; - } - - count = of_property_count_u32_elems(opp->np, name); - if (count < 0) { - dev_err(dev, "%s: Invalid %s property (%d)\n", - __func__, name, count); - return count; - } - - /* There can be one or three elements here */ - if (count != 1 && count != 3) { - dev_err(dev, "%s: Invalid number of elements in %s property (%d)\n", - __func__, name, count); - return -EINVAL; - } - - ret = of_property_read_u32_array(opp->np, name, microvolt, count); - if (ret) { - dev_err(dev, "%s: error parsing %s: %d\n", __func__, name, ret); - return -EINVAL; - } - - opp->u_volt = microvolt[0]; - - if (count == 1) { - opp->u_volt_min = opp->u_volt; - opp->u_volt_max = opp->u_volt; - } else { - opp->u_volt_min = microvolt[1]; - opp->u_volt_max = microvolt[2]; - } - - /* Search for "opp-microamp-<name>" */ - prop = NULL; - if (opp_table->prop_name) { - snprintf(name, sizeof(name), "opp-microamp-%s", - opp_table->prop_name); - prop = of_find_property(opp->np, name, NULL); - } - - if (!prop) { - /* Search for "opp-microamp" */ - sprintf(name, "opp-microamp"); - prop = of_find_property(opp->np, name, NULL); - } - - if (prop && !of_property_read_u32(opp->np, name, &val)) - opp->u_amp = val; - - return 0; -} - /** * dev_pm_opp_set_supported_hw() - Set supported platforms * @dev: Device for which supported-hw has to be set. @@ -1520,144 +1405,6 @@ unlock: } EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulator); -static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table, - struct device_node *np) -{ - unsigned int count = opp_table->supported_hw_count; - u32 version; - int ret; - - if (!opp_table->supported_hw) - return true; - - while (count--) { - ret = of_property_read_u32_index(np, "opp-supported-hw", count, - &version); - if (ret) { - dev_warn(dev, "%s: failed to read opp-supported-hw property at index %d: %d\n", - __func__, count, ret); - return false; - } - - /* Both of these are bitwise masks of the versions */ - if (!(version & opp_table->supported_hw[count])) - return false; - } - - return true; -} - -/** - * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings) - * @dev: device for which we do this operation - * @np: device node - * - * This function adds an opp definition to the opp table and returns status. The - * opp can be controlled using dev_pm_opp_enable/disable functions and may be - * removed by dev_pm_opp_remove. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. - * - * Return: - * 0 On success OR - * Duplicate OPPs (both freq and volt are same) and opp->available - * -EEXIST Freq are same and volt are different OR - * Duplicate OPPs (both freq and volt are same) and !opp->available - * -ENOMEM Memory allocation failure - * -EINVAL Failed parsing the OPP node - */ -static int _opp_add_static_v2(struct device *dev, struct device_node *np) -{ - struct opp_table *opp_table; - struct dev_pm_opp *new_opp; - u64 rate; - u32 val; - int ret; - - /* Hold our table modification lock here */ - mutex_lock(&opp_table_lock); - - new_opp = _allocate_opp(dev, &opp_table); - if (!new_opp) { - ret = -ENOMEM; - goto unlock; - } - - ret = of_property_read_u64(np, "opp-hz", &rate); - if (ret < 0) { - dev_err(dev, "%s: opp-hz not found\n", __func__); - goto free_opp; - } - - /* Check if the OPP supports hardware's hierarchy of versions or not */ - if (!_opp_is_supported(dev, opp_table, np)) { - dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate); - goto free_opp; - } - - /* - * Rate is defined as an unsigned long in clk API, and so casting - * explicitly to its type. Must be fixed once rate is 64 bit - * guaranteed in clk API. - */ - new_opp->rate = (unsigned long)rate; - new_opp->turbo = of_property_read_bool(np, "turbo-mode"); - - new_opp->np = np; - new_opp->dynamic = false; - new_opp->available = true; - - if (!of_property_read_u32(np, "clock-latency-ns", &val)) - new_opp->clock_latency_ns = val; - - ret = opp_parse_supplies(new_opp, dev, opp_table); - if (ret) - goto free_opp; - - ret = _opp_add(dev, new_opp, opp_table); - if (ret) - goto free_opp; - - /* OPP to select on device suspend */ - if (of_property_read_bool(np, "opp-suspend")) { - if (opp_table->suspend_opp) { - dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n", - __func__, opp_table->suspend_opp->rate, - new_opp->rate); - } else { - new_opp->suspend = true; - opp_table->suspend_opp = new_opp; - } - } - - if (new_opp->clock_latency_ns > opp_table->clock_latency_ns_max) - opp_table->clock_latency_ns_max = new_opp->clock_latency_ns; - - mutex_unlock(&opp_table_lock); - - pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu\n", - __func__, new_opp->turbo, new_opp->rate, new_opp->u_volt, - new_opp->u_volt_min, new_opp->u_volt_max, - new_opp->clock_latency_ns); - - /* - * Notify the changes in the availability of the operable - * frequency/voltage list. - */ - srcu_notifier_call_chain(&opp_table->srcu_head, OPP_EVENT_ADD, new_opp); - return 0; - -free_opp: - _opp_remove(opp_table, new_opp, false); -unlock: - mutex_unlock(&opp_table_lock); - return ret; -} - /** * dev_pm_opp_add() - Add an OPP table from a table definitions * @dev: device for which we do this operation @@ -1845,21 +1592,11 @@ struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev) } EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); -#ifdef CONFIG_OF -/** - * dev_pm_opp_of_remove_table() - Free OPP table entries created from static DT - * entries - * @dev: device pointer used to lookup OPP table. - * - * Free OPPs created using static entries present in DT. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function indirectly uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. +/* + * Free OPPs either created using static entries present in DT or even the + * dynamically added entries based on remove_all param. */ -void dev_pm_opp_of_remove_table(struct device *dev) +void _dev_pm_opp_remove_table(struct device *dev, bool remove_all) { struct opp_table *opp_table; struct dev_pm_opp *opp, *tmp; @@ -1884,7 +1621,7 @@ void dev_pm_opp_of_remove_table(struct device *dev) if (list_is_singular(&opp_table->dev_list)) { /* Free static OPPs */ list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) { - if (!opp->dynamic) + if (remove_all || !opp->dynamic) _opp_remove(opp_table, opp, true); } } else { @@ -1894,160 +1631,22 @@ void dev_pm_opp_of_remove_table(struct device *dev) unlock: mutex_unlock(&opp_table_lock); } -EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); - -/* Returns opp descriptor node for a device, caller must do of_node_put() */ -struct device_node *_of_get_opp_desc_node(struct device *dev) -{ - /* - * TODO: Support for multiple OPP tables. - * - * There should be only ONE phandle present in "operating-points-v2" - * property. - */ - - return of_parse_phandle(dev->of_node, "operating-points-v2", 0); -} - -/* Initializes OPP tables based on new bindings */ -static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) -{ - struct device_node *np; - struct opp_table *opp_table; - int ret = 0, count = 0; - - mutex_lock(&opp_table_lock); - - opp_table = _managed_opp(opp_np); - if (opp_table) { - /* OPPs are already managed */ - if (!_add_opp_dev(dev, opp_table)) - ret = -ENOMEM; - mutex_unlock(&opp_table_lock); - return ret; - } - mutex_unlock(&opp_table_lock); - - /* We have opp-table node now, iterate over it and add OPPs */ - for_each_available_child_of_node(opp_np, np) { - count++; - - ret = _opp_add_static_v2(dev, np); - if (ret) { - dev_err(dev, "%s: Failed to add OPP, %d\n", __func__, - ret); - goto free_table; - } - } - - /* There should be one of more OPP defined */ - if (WARN_ON(!count)) - return -ENOENT; - - mutex_lock(&opp_table_lock); - - opp_table = _find_opp_table(dev); - if (WARN_ON(IS_ERR(opp_table))) { - ret = PTR_ERR(opp_table); - mutex_unlock(&opp_table_lock); - goto free_table; - } - - opp_table->np = opp_np; - opp_table->shared_opp = of_property_read_bool(opp_np, "opp-shared"); - - mutex_unlock(&opp_table_lock); - - return 0; - -free_table: - dev_pm_opp_of_remove_table(dev); - - return ret; -} - -/* Initializes OPP tables based on old-deprecated bindings */ -static int _of_add_opp_table_v1(struct device *dev) -{ - const struct property *prop; - const __be32 *val; - int nr; - - prop = of_find_property(dev->of_node, "operating-points", NULL); - if (!prop) - return -ENODEV; - if (!prop->value) - return -ENODATA; - - /* - * Each OPP is a set of tuples consisting of frequency and - * voltage like <freq-kHz vol-uV>. - */ - nr = prop->length / sizeof(u32); - if (nr % 2) { - dev_err(dev, "%s: Invalid OPP table\n", __func__); - return -EINVAL; - } - - val = prop->value; - while (nr) { - unsigned long freq = be32_to_cpup(val++) * 1000; - unsigned long volt = be32_to_cpup(val++); - - if (_opp_add_v1(dev, freq, volt, false)) - dev_warn(dev, "%s: Failed to add OPP %ld\n", - __func__, freq); - nr -= 2; - } - - return 0; -} /** - * dev_pm_opp_of_add_table() - Initialize opp table from device tree + * dev_pm_opp_remove_table() - Free all OPPs associated with the device * @dev: device pointer used to lookup OPP table. * - * Register the initial OPP table with the OPP library for given device. + * Free both OPPs created using static entries present in DT and the + * dynamically added entries. * * Locking: The internal opp_table and opp structures are RCU protected. * Hence this function indirectly uses RCU updater strategy with mutex locks * to keep the integrity of the internal data structures. Callers should ensure * that this function is *NOT* called under RCU protection or in contexts where * mutex cannot be locked. - * - * Return: - * 0 On success OR - * Duplicate OPPs (both freq and volt are same) and opp->available - * -EEXIST Freq are same and volt are different OR - * Duplicate OPPs (both freq and volt are same) and !opp->available - * -ENOMEM Memory allocation failure - * -ENODEV when 'operating-points' property is not found or is invalid data - * in device node. - * -ENODATA when empty 'operating-points' property is found - * -EINVAL when invalid entries are found in opp-v2 table */ -int dev_pm_opp_of_add_table(struct device *dev) +void dev_pm_opp_remove_table(struct device *dev) { - struct device_node *opp_np; - int ret; - - /* - * OPPs have two version of bindings now. The older one is deprecated, - * try for the new binding first. - */ - opp_np = _of_get_opp_desc_node(dev); - if (!opp_np) { - /* - * Try old-deprecated bindings for backward compatibility with - * older dtbs. - */ - return _of_add_opp_table_v1(dev); - } - - ret = _of_add_opp_table_v2(dev, opp_np); - of_node_put(opp_np); - - return ret; + _dev_pm_opp_remove_table(dev, true); } -EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); -#endif +EXPORT_SYMBOL_GPL(dev_pm_opp_remove_table); diff --git a/drivers/base/power/opp/cpu.c b/drivers/base/power/opp/cpu.c index ba2bdbd932ef..83d6e7ba1a34 100644 --- a/drivers/base/power/opp/cpu.c +++ b/drivers/base/power/opp/cpu.c @@ -18,7 +18,6 @@ #include <linux/err.h> #include <linux/errno.h> #include <linux/export.h> -#include <linux/of.h> #include <linux/slab.h> #include "opp.h" @@ -119,8 +118,66 @@ void dev_pm_opp_free_cpufreq_table(struct device *dev, EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table); #endif /* CONFIG_CPU_FREQ */ -/* Required only for V1 bindings, as v2 can manage it from DT itself */ -int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) +void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, bool of) +{ + struct device *cpu_dev; + int cpu; + + WARN_ON(cpumask_empty(cpumask)); + + for_each_cpu(cpu, cpumask) { + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("%s: failed to get cpu%d device\n", __func__, + cpu); + continue; + } + + if (of) + dev_pm_opp_of_remove_table(cpu_dev); + else + dev_pm_opp_remove_table(cpu_dev); + } +} + +/** + * dev_pm_opp_cpumask_remove_table() - Removes OPP table for @cpumask + * @cpumask: cpumask for which OPP table needs to be removed + * + * This removes the OPP tables for CPUs present in the @cpumask. + * This should be used to remove all the OPPs entries associated with + * the cpus in @cpumask. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +void dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask) +{ + _dev_pm_opp_cpumask_remove_table(cpumask, false); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_cpumask_remove_table); + +/** + * dev_pm_opp_set_sharing_cpus() - Mark OPP table as shared by few CPUs + * @cpu_dev: CPU device for which we do this operation + * @cpumask: cpumask of the CPUs which share the OPP table with @cpu_dev + * + * This marks OPP table of the @cpu_dev as shared by the CPUs present in + * @cpumask. + * + * Returns -ENODEV if OPP table isn't already present. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, + const struct cpumask *cpumask) { struct opp_device *opp_dev; struct opp_table *opp_table; @@ -131,7 +188,7 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) opp_table = _find_opp_table(cpu_dev); if (IS_ERR(opp_table)) { - ret = -EINVAL; + ret = PTR_ERR(opp_table); goto unlock; } @@ -152,6 +209,9 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) __func__, cpu); continue; } + + /* Mark opp-table as multiple CPUs are sharing it now */ + opp_table->shared_opp = true; } unlock: mutex_unlock(&opp_table_lock); @@ -160,112 +220,47 @@ unlock: } EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus); -#ifdef CONFIG_OF -void dev_pm_opp_of_cpumask_remove_table(cpumask_var_t cpumask) -{ - struct device *cpu_dev; - int cpu; - - WARN_ON(cpumask_empty(cpumask)); - - for_each_cpu(cpu, cpumask) { - cpu_dev = get_cpu_device(cpu); - if (!cpu_dev) { - pr_err("%s: failed to get cpu%d device\n", __func__, - cpu); - continue; - } - - dev_pm_opp_of_remove_table(cpu_dev); - } -} -EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table); - -int dev_pm_opp_of_cpumask_add_table(cpumask_var_t cpumask) -{ - struct device *cpu_dev; - int cpu, ret = 0; - - WARN_ON(cpumask_empty(cpumask)); - - for_each_cpu(cpu, cpumask) { - cpu_dev = get_cpu_device(cpu); - if (!cpu_dev) { - pr_err("%s: failed to get cpu%d device\n", __func__, - cpu); - continue; - } - - ret = dev_pm_opp_of_add_table(cpu_dev); - if (ret) { - pr_err("%s: couldn't find opp table for cpu:%d, %d\n", - __func__, cpu, ret); - - /* Free all other OPPs */ - dev_pm_opp_of_cpumask_remove_table(cpumask); - break; - } - } - - return ret; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table); - -/* - * Works only for OPP v2 bindings. +/** + * dev_pm_opp_get_sharing_cpus() - Get cpumask of CPUs sharing OPPs with @cpu_dev + * @cpu_dev: CPU device for which we do this operation + * @cpumask: cpumask to update with information of sharing CPUs + * + * This updates the @cpumask with CPUs that are sharing OPPs with @cpu_dev. * - * Returns -ENOENT if operating-points-v2 bindings aren't supported. + * Returns -ENODEV if OPP table isn't already present. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. */ -int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) +int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask) { - struct device_node *np, *tmp_np; - struct device *tcpu_dev; - int cpu, ret = 0; - - /* Get OPP descriptor node */ - np = _of_get_opp_desc_node(cpu_dev); - if (!np) { - dev_dbg(cpu_dev, "%s: Couldn't find cpu_dev node.\n", __func__); - return -ENOENT; - } - - cpumask_set_cpu(cpu_dev->id, cpumask); - - /* OPPs are shared ? */ - if (!of_property_read_bool(np, "opp-shared")) - goto put_cpu_node; - - for_each_possible_cpu(cpu) { - if (cpu == cpu_dev->id) - continue; + struct opp_device *opp_dev; + struct opp_table *opp_table; + int ret = 0; - tcpu_dev = get_cpu_device(cpu); - if (!tcpu_dev) { - dev_err(cpu_dev, "%s: failed to get cpu%d device\n", - __func__, cpu); - ret = -ENODEV; - goto put_cpu_node; - } + mutex_lock(&opp_table_lock); - /* Get OPP descriptor node */ - tmp_np = _of_get_opp_desc_node(tcpu_dev); - if (!tmp_np) { - dev_err(tcpu_dev, "%s: Couldn't find tcpu_dev node.\n", - __func__); - ret = -ENOENT; - goto put_cpu_node; - } + opp_table = _find_opp_table(cpu_dev); + if (IS_ERR(opp_table)) { + ret = PTR_ERR(opp_table); + goto unlock; + } - /* CPUs are sharing opp node */ - if (np == tmp_np) - cpumask_set_cpu(cpu, cpumask); + cpumask_clear(cpumask); - of_node_put(tmp_np); + if (opp_table->shared_opp) { + list_for_each_entry(opp_dev, &opp_table->dev_list, node) + cpumask_set_cpu(opp_dev->dev->id, cpumask); + } else { + cpumask_set_cpu(cpu_dev->id, cpumask); } -put_cpu_node: - of_node_put(np); +unlock: + mutex_unlock(&opp_table_lock); + return ret; } -EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus); -#endif +EXPORT_SYMBOL_GPL(dev_pm_opp_get_sharing_cpus); diff --git a/drivers/base/power/opp/of.c b/drivers/base/power/opp/of.c new file mode 100644 index 000000000000..94d2010558e3 --- /dev/null +++ b/drivers/base/power/opp/of.c @@ -0,0 +1,591 @@ +/* + * Generic OPP OF helpers + * + * Copyright (C) 2009-2010 Texas Instruments Incorporated. + * Nishanth Menon + * Romit Dasgupta + * Kevin Hilman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/cpu.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/of.h> +#include <linux/export.h> + +#include "opp.h" + +static struct opp_table *_managed_opp(const struct device_node *np) +{ + struct opp_table *opp_table; + + list_for_each_entry_rcu(opp_table, &opp_tables, node) { + if (opp_table->np == np) { + /* + * Multiple devices can point to the same OPP table and + * so will have same node-pointer, np. + * + * But the OPPs will be considered as shared only if the + * OPP table contains a "opp-shared" property. + */ + return opp_table->shared_opp ? opp_table : NULL; + } + } + + return NULL; +} + +void _of_init_opp_table(struct opp_table *opp_table, struct device *dev) +{ + struct device_node *np; + + /* + * Only required for backward compatibility with v1 bindings, but isn't + * harmful for other cases. And so we do it unconditionally. + */ + np = of_node_get(dev->of_node); + if (np) { + u32 val; + + if (!of_property_read_u32(np, "clock-latency", &val)) + opp_table->clock_latency_ns_max = val; + of_property_read_u32(np, "voltage-tolerance", + &opp_table->voltage_tolerance_v1); + of_node_put(np); + } +} + +static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table, + struct device_node *np) +{ + unsigned int count = opp_table->supported_hw_count; + u32 version; + int ret; + + if (!opp_table->supported_hw) + return true; + + while (count--) { + ret = of_property_read_u32_index(np, "opp-supported-hw", count, + &version); + if (ret) { + dev_warn(dev, "%s: failed to read opp-supported-hw property at index %d: %d\n", + __func__, count, ret); + return false; + } + + /* Both of these are bitwise masks of the versions */ + if (!(version & opp_table->supported_hw[count])) + return false; + } + + return true; +} + +/* TODO: Support multiple regulators */ +static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, + struct opp_table *opp_table) +{ + u32 microvolt[3] = {0}; + u32 val; + int count, ret; + struct property *prop = NULL; + char name[NAME_MAX]; + + /* Search for "opp-microvolt-<name>" */ + if (opp_table->prop_name) { + snprintf(name, sizeof(name), "opp-microvolt-%s", + opp_table->prop_name); + prop = of_find_property(opp->np, name, NULL); + } + + if (!prop) { + /* Search for "opp-microvolt" */ + sprintf(name, "opp-microvolt"); + prop = of_find_property(opp->np, name, NULL); + + /* Missing property isn't a problem, but an invalid entry is */ + if (!prop) + return 0; + } + + count = of_property_count_u32_elems(opp->np, name); + if (count < 0) { + dev_err(dev, "%s: Invalid %s property (%d)\n", + __func__, name, count); + return count; + } + + /* There can be one or three elements here */ + if (count != 1 && count != 3) { + dev_err(dev, "%s: Invalid number of elements in %s property (%d)\n", + __func__, name, count); + return -EINVAL; + } + + ret = of_property_read_u32_array(opp->np, name, microvolt, count); + if (ret) { + dev_err(dev, "%s: error parsing %s: %d\n", __func__, name, ret); + return -EINVAL; + } + + opp->u_volt = microvolt[0]; + + if (count == 1) { + opp->u_volt_min = opp->u_volt; + opp->u_volt_max = opp->u_volt; + } else { + opp->u_volt_min = microvolt[1]; + opp->u_volt_max = microvolt[2]; + } + + /* Search for "opp-microamp-<name>" */ + prop = NULL; + if (opp_table->prop_name) { + snprintf(name, sizeof(name), "opp-microamp-%s", + opp_table->prop_name); + prop = of_find_property(opp->np, name, NULL); + } + + if (!prop) { + /* Search for "opp-microamp" */ + sprintf(name, "opp-microamp"); + prop = of_find_property(opp->np, name, NULL); + } + + if (prop && !of_property_read_u32(opp->np, name, &val)) + opp->u_amp = val; + + return 0; +} + +/** + * dev_pm_opp_of_remove_table() - Free OPP table entries created from static DT + * entries + * @dev: device pointer used to lookup OPP table. + * + * Free OPPs created using static entries present in DT. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function indirectly uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +void dev_pm_opp_of_remove_table(struct device *dev) +{ + _dev_pm_opp_remove_table(dev, false); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); + +/* Returns opp descriptor node for a device, caller must do of_node_put() */ +struct device_node *_of_get_opp_desc_node(struct device *dev) +{ + /* + * TODO: Support for multiple OPP tables. + * + * There should be only ONE phandle present in "operating-points-v2" + * property. + */ + + return of_parse_phandle(dev->of_node, "operating-points-v2", 0); +} + +/** + * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings) + * @dev: device for which we do this operation + * @np: device node + * + * This function adds an opp definition to the opp table and returns status. The + * opp can be controlled using dev_pm_opp_enable/disable functions and may be + * removed by dev_pm_opp_remove. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + * + * Return: + * 0 On success OR + * Duplicate OPPs (both freq and volt are same) and opp->available + * -EEXIST Freq are same and volt are different OR + * Duplicate OPPs (both freq and volt are same) and !opp->available + * -ENOMEM Memory allocation failure + * -EINVAL Failed parsing the OPP node + */ +static int _opp_add_static_v2(struct device *dev, struct device_node *np) +{ + struct opp_table *opp_table; + struct dev_pm_opp *new_opp; + u64 rate; + u32 val; + int ret; + + /* Hold our table modification lock here */ + mutex_lock(&opp_table_lock); + + new_opp = _allocate_opp(dev, &opp_table); + if (!new_opp) { + ret = -ENOMEM; + goto unlock; + } + + ret = of_property_read_u64(np, "opp-hz", &rate); + if (ret < 0) { + dev_err(dev, "%s: opp-hz not found\n", __func__); + goto free_opp; + } + + /* Check if the OPP supports hardware's hierarchy of versions or not */ + if (!_opp_is_supported(dev, opp_table, np)) { + dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate); + goto free_opp; + } + + /* + * Rate is defined as an unsigned long in clk API, and so casting + * explicitly to its type. Must be fixed once rate is 64 bit + * guaranteed in clk API. + */ + new_opp->rate = (unsigned long)rate; + new_opp->turbo = of_property_read_bool(np, "turbo-mode"); + + new_opp->np = np; + new_opp->dynamic = false; + new_opp->available = true; + + if (!of_property_read_u32(np, "clock-latency-ns", &val)) + new_opp->clock_latency_ns = val; + + ret = opp_parse_supplies(new_opp, dev, opp_table); + if (ret) + goto free_opp; + + ret = _opp_add(dev, new_opp, opp_table); + if (ret) + goto free_opp; + + /* OPP to select on device suspend */ + if (of_property_read_bool(np, "opp-suspend")) { + if (opp_table->suspend_opp) { + dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n", + __func__, opp_table->suspend_opp->rate, + new_opp->rate); + } else { + new_opp->suspend = true; + opp_table->suspend_opp = new_opp; + } + } + + if (new_opp->clock_latency_ns > opp_table->clock_latency_ns_max) + opp_table->clock_latency_ns_max = new_opp->clock_latency_ns; + + mutex_unlock(&opp_table_lock); + + pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu\n", + __func__, new_opp->turbo, new_opp->rate, new_opp->u_volt, + new_opp->u_volt_min, new_opp->u_volt_max, + new_opp->clock_latency_ns); + + /* + * Notify the changes in the availability of the operable + * frequency/voltage list. + */ + srcu_notifier_call_chain(&opp_table->srcu_head, OPP_EVENT_ADD, new_opp); + return 0; + +free_opp: + _opp_remove(opp_table, new_opp, false); +unlock: + mutex_unlock(&opp_table_lock); + return ret; +} + +/* Initializes OPP tables based on new bindings */ +static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) +{ + struct device_node *np; + struct opp_table *opp_table; + int ret = 0, count = 0; + + mutex_lock(&opp_table_lock); + + opp_table = _managed_opp(opp_np); + if (opp_table) { + /* OPPs are already managed */ + if (!_add_opp_dev(dev, opp_table)) + ret = -ENOMEM; + mutex_unlock(&opp_table_lock); + return ret; + } + mutex_unlock(&opp_table_lock); + + /* We have opp-table node now, iterate over it and add OPPs */ + for_each_available_child_of_node(opp_np, np) { + count++; + + ret = _opp_add_static_v2(dev, np); + if (ret) { + dev_err(dev, "%s: Failed to add OPP, %d\n", __func__, + ret); + goto free_table; + } + } + + /* There should be one of more OPP defined */ + if (WARN_ON(!count)) + return -ENOENT; + + mutex_lock(&opp_table_lock); + + opp_table = _find_opp_table(dev); + if (WARN_ON(IS_ERR(opp_table))) { + ret = PTR_ERR(opp_table); + mutex_unlock(&opp_table_lock); + goto free_table; + } + + opp_table->np = opp_np; + opp_table->shared_opp = of_property_read_bool(opp_np, "opp-shared"); + + mutex_unlock(&opp_table_lock); + + return 0; + +free_table: + dev_pm_opp_of_remove_table(dev); + + return ret; +} + +/* Initializes OPP tables based on old-deprecated bindings */ +static int _of_add_opp_table_v1(struct device *dev) +{ + const struct property *prop; + const __be32 *val; + int nr; + + prop = of_find_property(dev->of_node, "operating-points", NULL); + if (!prop) + return -ENODEV; + if (!prop->value) + return -ENODATA; + + /* + * Each OPP is a set of tuples consisting of frequency and + * voltage like <freq-kHz vol-uV>. + */ + nr = prop->length / sizeof(u32); + if (nr % 2) { + dev_err(dev, "%s: Invalid OPP table\n", __func__); + return -EINVAL; + } + + val = prop->value; + while (nr) { + unsigned long freq = be32_to_cpup(val++) * 1000; + unsigned long volt = be32_to_cpup(val++); + + if (_opp_add_v1(dev, freq, volt, false)) + dev_warn(dev, "%s: Failed to add OPP %ld\n", + __func__, freq); + nr -= 2; + } + + return 0; +} + +/** + * dev_pm_opp_of_add_table() - Initialize opp table from device tree + * @dev: device pointer used to lookup OPP table. + * + * Register the initial OPP table with the OPP library for given device. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function indirectly uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + * + * Return: + * 0 On success OR + * Duplicate OPPs (both freq and volt are same) and opp->available + * -EEXIST Freq are same and volt are different OR + * Duplicate OPPs (both freq and volt are same) and !opp->available + * -ENOMEM Memory allocation failure + * -ENODEV when 'operating-points' property is not found or is invalid data + * in device node. + * -ENODATA when empty 'operating-points' property is found + * -EINVAL when invalid entries are found in opp-v2 table + */ +int dev_pm_opp_of_add_table(struct device *dev) +{ + struct device_node *opp_np; + int ret; + + /* + * OPPs have two version of bindings now. The older one is deprecated, + * try for the new binding first. + */ + opp_np = _of_get_opp_desc_node(dev); + if (!opp_np) { + /* + * Try old-deprecated bindings for backward compatibility with + * older dtbs. + */ + return _of_add_opp_table_v1(dev); + } + + ret = _of_add_opp_table_v2(dev, opp_np); + of_node_put(opp_np); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); + +/* CPU device specific helpers */ + +/** + * dev_pm_opp_of_cpumask_remove_table() - Removes OPP table for @cpumask + * @cpumask: cpumask for which OPP table needs to be removed + * + * This removes the OPP tables for CPUs present in the @cpumask. + * This should be used only to remove static entries created from DT. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask) +{ + _dev_pm_opp_cpumask_remove_table(cpumask, true); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table); + +/** + * dev_pm_opp_of_cpumask_add_table() - Adds OPP table for @cpumask + * @cpumask: cpumask for which OPP table needs to be added. + * + * This adds the OPP tables for CPUs present in the @cpumask. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask) +{ + struct device *cpu_dev; + int cpu, ret = 0; + + WARN_ON(cpumask_empty(cpumask)); + + for_each_cpu(cpu, cpumask) { + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("%s: failed to get cpu%d device\n", __func__, + cpu); + continue; + } + + ret = dev_pm_opp_of_add_table(cpu_dev); + if (ret) { + pr_err("%s: couldn't find opp table for cpu:%d, %d\n", + __func__, cpu, ret); + + /* Free all other OPPs */ + dev_pm_opp_of_cpumask_remove_table(cpumask); + break; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table); + +/* + * Works only for OPP v2 bindings. + * + * Returns -ENOENT if operating-points-v2 bindings aren't supported. + */ +/** + * dev_pm_opp_of_get_sharing_cpus() - Get cpumask of CPUs sharing OPPs with + * @cpu_dev using operating-points-v2 + * bindings. + * + * @cpu_dev: CPU device for which we do this operation + * @cpumask: cpumask to update with information of sharing CPUs + * + * This updates the @cpumask with CPUs that are sharing OPPs with @cpu_dev. + * + * Returns -ENOENT if operating-points-v2 isn't present for @cpu_dev. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, + struct cpumask *cpumask) +{ + struct device_node *np, *tmp_np; + struct device *tcpu_dev; + int cpu, ret = 0; + + /* Get OPP descriptor node */ + np = _of_get_opp_desc_node(cpu_dev); + if (!np) { + dev_dbg(cpu_dev, "%s: Couldn't find cpu_dev node.\n", __func__); + return -ENOENT; + } + + cpumask_set_cpu(cpu_dev->id, cpumask); + + /* OPPs are shared ? */ + if (!of_property_read_bool(np, "opp-shared")) + goto put_cpu_node; + + for_each_possible_cpu(cpu) { + if (cpu == cpu_dev->id) + continue; + + tcpu_dev = get_cpu_device(cpu); + if (!tcpu_dev) { + dev_err(cpu_dev, "%s: failed to get cpu%d device\n", + __func__, cpu); + ret = -ENODEV; + goto put_cpu_node; + } + + /* Get OPP descriptor node */ + tmp_np = _of_get_opp_desc_node(tcpu_dev); + if (!tmp_np) { + dev_err(tcpu_dev, "%s: Couldn't find tcpu_dev node.\n", + __func__); + ret = -ENOENT; + goto put_cpu_node; + } + + /* CPUs are sharing opp node */ + if (np == tmp_np) + cpumask_set_cpu(cpu, cpumask); + + of_node_put(tmp_np); + } + +put_cpu_node: + of_node_put(np); + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus); diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h index f67f806fcf3a..20f3be22e060 100644 --- a/drivers/base/power/opp/opp.h +++ b/drivers/base/power/opp/opp.h @@ -28,6 +28,8 @@ struct regulator; /* Lock to allow exclusive modification to the device and opp lists */ extern struct mutex opp_table_lock; +extern struct list_head opp_tables; + /* * Internal data structure organization with the OPP layer library is as * follows: @@ -183,6 +185,18 @@ struct opp_table { struct opp_table *_find_opp_table(struct device *dev); struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_table); struct device_node *_of_get_opp_desc_node(struct device *dev); +void _dev_pm_opp_remove_table(struct device *dev, bool remove_all); +struct dev_pm_opp *_allocate_opp(struct device *dev, struct opp_table **opp_table); +int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table); +void _opp_remove(struct opp_table *opp_table, struct dev_pm_opp *opp, bool notify); +int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt, bool dynamic); +void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, bool of); + +#ifdef CONFIG_OF +void _of_init_opp_table(struct opp_table *opp_table, struct device *dev); +#else +static inline void _of_init_opp_table(struct opp_table *opp_table, struct device *dev) {} +#endif #ifdef CONFIG_DEBUG_FS void opp_debug_remove_one(struct dev_pm_opp *opp); diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 4c7055009bd6..b74690418504 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -1506,11 +1506,16 @@ int pm_runtime_force_resume(struct device *dev) goto out; } - ret = callback(dev); + ret = pm_runtime_set_active(dev); if (ret) goto out; - pm_runtime_set_active(dev); + ret = callback(dev); + if (ret) { + pm_runtime_set_suspended(dev); + goto out; + } + pm_runtime_mark_last_busy(dev); out: pm_runtime_enable(dev); diff --git a/drivers/base/property.c b/drivers/base/property.c index 9b1a65debd49..7f692accdc90 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -21,7 +21,7 @@ static inline bool is_pset_node(struct fwnode_handle *fwnode) { - return fwnode && fwnode->type == FWNODE_PDATA; + return !IS_ERR_OR_NULL(fwnode) && fwnode->type == FWNODE_PDATA; } static inline struct property_set *to_pset_node(struct fwnode_handle *fwnode) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 94a1843b0426..0ede6d7e2568 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -538,7 +538,6 @@ static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id, u8 *order, u64 *snap_size); static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id, u64 *snap_features); -static u64 rbd_snap_id_by_name(struct rbd_device *rbd_dev, const char *name); static int rbd_open(struct block_device *bdev, fmode_t mode) { @@ -3127,9 +3126,6 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) struct rbd_device *rbd_dev = (struct rbd_device *)data; int ret; - if (!rbd_dev) - return; - dout("%s: \"%s\" notify_id %llu opcode %u\n", __func__, rbd_dev->header_name, (unsigned long long)notify_id, (unsigned int)opcode); @@ -3263,6 +3259,9 @@ static void rbd_dev_header_unwatch_sync(struct rbd_device *rbd_dev) ceph_osdc_cancel_event(rbd_dev->watch_event); rbd_dev->watch_event = NULL; + + dout("%s flushing notifies\n", __func__); + ceph_osdc_flush_notifies(&rbd_dev->rbd_client->client->osdc); } /* @@ -3642,21 +3641,14 @@ static void rbd_exists_validate(struct rbd_device *rbd_dev) static void rbd_dev_update_size(struct rbd_device *rbd_dev) { sector_t size; - bool removing; /* - * Don't hold the lock while doing disk operations, - * or lock ordering will conflict with the bdev mutex via: - * rbd_add() -> blkdev_get() -> rbd_open() + * If EXISTS is not set, rbd_dev->disk may be NULL, so don't + * try to update its size. If REMOVING is set, updating size + * is just useless work since the device can't be opened. */ - spin_lock_irq(&rbd_dev->lock); - removing = test_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags); - spin_unlock_irq(&rbd_dev->lock); - /* - * If the device is being removed, rbd_dev->disk has - * been destroyed, so don't try to update its size - */ - if (!removing) { + if (test_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags) && + !test_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags)) { size = (sector_t)rbd_dev->mapping.size / SECTOR_SIZE; dout("setting size to %llu sectors", (unsigned long long)size); set_capacity(rbd_dev->disk, size); @@ -4191,7 +4183,7 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id, __le64 features; __le64 incompat; } __attribute__ ((packed)) features_buf = { 0 }; - u64 incompat; + u64 unsup; int ret; ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name, @@ -4204,9 +4196,12 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id, if (ret < sizeof (features_buf)) return -ERANGE; - incompat = le64_to_cpu(features_buf.incompat); - if (incompat & ~RBD_FEATURES_SUPPORTED) + unsup = le64_to_cpu(features_buf.incompat) & ~RBD_FEATURES_SUPPORTED; + if (unsup) { + rbd_warn(rbd_dev, "image uses unsupported features: 0x%llx", + unsup); return -ENXIO; + } *snap_features = le64_to_cpu(features_buf.features); @@ -5187,6 +5182,10 @@ out_err: return ret; } +/* + * rbd_dev->header_rwsem must be locked for write and will be unlocked + * upon return. + */ static int rbd_dev_device_setup(struct rbd_device *rbd_dev) { int ret; @@ -5195,7 +5194,7 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev) ret = rbd_dev_id_get(rbd_dev); if (ret) - return ret; + goto err_out_unlock; BUILD_BUG_ON(DEV_NAME_LEN < sizeof (RBD_DRV_NAME) + MAX_INT_FORMAT_WIDTH); @@ -5236,8 +5235,9 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev) /* Everything's ready. Announce the disk to the world. */ set_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags); - add_disk(rbd_dev->disk); + up_write(&rbd_dev->header_rwsem); + add_disk(rbd_dev->disk); pr_info("%s: added with size 0x%llx\n", rbd_dev->disk->disk_name, (unsigned long long) rbd_dev->mapping.size); @@ -5252,6 +5252,8 @@ err_out_blkdev: unregister_blkdev(rbd_dev->major, rbd_dev->name); err_out_id: rbd_dev_id_put(rbd_dev); +err_out_unlock: + up_write(&rbd_dev->header_rwsem); return ret; } @@ -5442,6 +5444,7 @@ static ssize_t do_rbd_add(struct bus_type *bus, spec = NULL; /* rbd_dev now owns this */ rbd_opts = NULL; /* rbd_dev now owns this */ + down_write(&rbd_dev->header_rwsem); rc = rbd_dev_image_probe(rbd_dev, 0); if (rc < 0) goto err_out_rbd_dev; @@ -5471,6 +5474,7 @@ out: return rc; err_out_rbd_dev: + up_write(&rbd_dev->header_rwsem); rbd_dev_destroy(rbd_dev); err_out_client: rbd_put_client(rbdc); @@ -5577,12 +5581,6 @@ static ssize_t do_rbd_remove(struct bus_type *bus, return ret; rbd_dev_header_unwatch_sync(rbd_dev); - /* - * flush remaining watch callbacks - these must be complete - * before the osd_client is shutdown - */ - dout("%s: flushing notifies", __func__); - ceph_osdc_flush_notifies(&rbd_dev->rbd_client->client->osdc); /* * Don't free anything from rbd_dev->disk until after all diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index 02e18182fcb5..2beb396fe652 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -394,7 +394,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_LDB_DI1_DIV_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1", 2, 7); } else { clk[IMX6QDL_CLK_ECSPI_ROOT] = imx_clk_divider("ecspi_root", "pll3_60m", base + 0x38, 19, 6); - clk[IMX6QDL_CLK_CAN_ROOT] = imx_clk_divider("can_root", "pll3_60", base + 0x20, 2, 6); + clk[IMX6QDL_CLK_CAN_ROOT] = imx_clk_divider("can_root", "pll3_60m", base + 0x20, 2, 6); clk[IMX6QDL_CLK_IPG_PER] = imx_clk_fixup_divider("ipg_per", "ipg", base + 0x1c, 0, 6, imx_cscmr1_fixup); clk[IMX6QDL_CLK_UART_SERIAL_PODF] = imx_clk_divider("uart_serial_podf", "pll3_80m", base + 0x24, 0, 6); clk[IMX6QDL_CLK_LDB_DI0_DIV_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7); diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index a7f45853c103..b7445b6ae5a4 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -18,7 +18,11 @@ config CPU_FREQ if CPU_FREQ +config CPU_FREQ_GOV_ATTR_SET + bool + config CPU_FREQ_GOV_COMMON + select CPU_FREQ_GOV_ATTR_SET select IRQ_WORK bool @@ -103,6 +107,17 @@ config CPU_FREQ_DEFAULT_GOV_CONSERVATIVE Be aware that not all cpufreq drivers support the conservative governor. If unsure have a look at the help section of the driver. Fallback governor will be the performance governor. + +config CPU_FREQ_DEFAULT_GOV_SCHEDUTIL + bool "schedutil" + depends on SMP + select CPU_FREQ_GOV_SCHEDUTIL + select CPU_FREQ_GOV_PERFORMANCE + help + Use the 'schedutil' CPUFreq governor by default. If unsure, + have a look at the help section of that governor. The fallback + governor will be 'performance'. + endchoice config CPU_FREQ_GOV_PERFORMANCE @@ -184,6 +199,26 @@ config CPU_FREQ_GOV_CONSERVATIVE If in doubt, say N. +config CPU_FREQ_GOV_SCHEDUTIL + tristate "'schedutil' cpufreq policy governor" + depends on CPU_FREQ && SMP + select CPU_FREQ_GOV_ATTR_SET + select IRQ_WORK + help + This governor makes decisions based on the utilization data provided + by the scheduler. It sets the CPU frequency to be proportional to + the utilization/capacity ratio coming from the scheduler. If the + utilization is frequency-invariant, the new frequency is also + proportional to the maximum available frequency. If that is not the + case, it is proportional to the current frequency of the CPU. The + frequency tipping point is at utilization/capacity equal to 80% in + both cases. + + To compile this driver as a module, choose M here: the module will + be called cpufreq_schedutil. + + If in doubt, say N. + comment "CPU frequency scaling drivers" config CPUFREQ_DT @@ -191,6 +226,7 @@ config CPUFREQ_DT depends on HAVE_CLK && OF # if CPU_THERMAL is on and THERMAL=m, CPUFREQ_DT cannot be =y: depends on !CPU_THERMAL || THERMAL + select CPUFREQ_DT_PLATDEV select PM_OPP help This adds a generic DT based cpufreq driver for frequency management. @@ -199,6 +235,15 @@ config CPUFREQ_DT If in doubt, say N. +config CPUFREQ_DT_PLATDEV + bool + help + This adds a generic DT based cpufreq platdev driver for frequency + management. This creates a 'cpufreq-dt' platform device, on the + supported platforms. + + If in doubt, say N. + if X86 source "drivers/cpufreq/Kconfig.x86" endif diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 14b1f9393b05..d89b8afe23b6 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -50,15 +50,6 @@ config ARM_HIGHBANK_CPUFREQ If in doubt, say N. -config ARM_HISI_ACPU_CPUFREQ - tristate "Hisilicon ACPU CPUfreq driver" - depends on ARCH_HISI && CPUFREQ_DT - select PM_OPP - help - This enables the hisilicon ACPU CPUfreq driver. - - If in doubt, say N. - config ARM_IMX6Q_CPUFREQ tristate "Freescale i.MX6 cpufreq support" depends on ARCH_MXC diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86 index c59bdcb83217..adbd1de1cea5 100644 --- a/drivers/cpufreq/Kconfig.x86 +++ b/drivers/cpufreq/Kconfig.x86 @@ -5,6 +5,7 @@ config X86_INTEL_PSTATE bool "Intel P state control" depends on X86 + select ACPI_PROCESSOR if ACPI help This driver provides a P state for Intel core processors. The driver implements an internal governor and will become diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 9e63fb1b09f8..e1eb11ee3570 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -11,8 +11,10 @@ obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o +obj-$(CONFIG_CPU_FREQ_GOV_ATTR_SET) += cpufreq_governor_attr_set.o obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o +obj-$(CONFIG_CPUFREQ_DT_PLATDEV) += cpufreq-dt-platdev.o ################################################################################## # x86 drivers. @@ -53,7 +55,6 @@ obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o -obj-$(CONFIG_ARM_HISI_ACPU_CPUFREQ) += hisi-acpu-cpufreq.o obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o @@ -78,6 +79,7 @@ obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o +obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o ################################################################################## diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index fb5712141040..32a15052f363 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -25,6 +25,8 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -50,8 +52,6 @@ MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski"); MODULE_DESCRIPTION("ACPI Processor P-States Driver"); MODULE_LICENSE("GPL"); -#define PFX "acpi-cpufreq: " - enum { UNDEFINED_CAPABLE = 0, SYSTEM_INTEL_MSR_CAPABLE, @@ -65,7 +65,6 @@ enum { #define MSR_K7_HWCR_CPB_DIS (1ULL << 25) struct acpi_cpufreq_data { - struct cpufreq_frequency_table *freq_table; unsigned int resume; unsigned int cpu_feature; unsigned int acpi_perf_cpu; @@ -200,8 +199,9 @@ static int check_amd_hwpstate_cpu(unsigned int cpuid) return cpu_has(cpu, X86_FEATURE_HW_PSTATE); } -static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data) +static unsigned extract_io(struct cpufreq_policy *policy, u32 value) { + struct acpi_cpufreq_data *data = policy->driver_data; struct acpi_processor_performance *perf; int i; @@ -209,13 +209,14 @@ static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data) for (i = 0; i < perf->state_count; i++) { if (value == perf->states[i].status) - return data->freq_table[i].frequency; + return policy->freq_table[i].frequency; } return 0; } -static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data) +static unsigned extract_msr(struct cpufreq_policy *policy, u32 msr) { + struct acpi_cpufreq_data *data = policy->driver_data; struct cpufreq_frequency_table *pos; struct acpi_processor_performance *perf; @@ -226,20 +227,22 @@ static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data) perf = to_perf_data(data); - cpufreq_for_each_entry(pos, data->freq_table) + cpufreq_for_each_entry(pos, policy->freq_table) if (msr == perf->states[pos->driver_data].status) return pos->frequency; - return data->freq_table[0].frequency; + return policy->freq_table[0].frequency; } -static unsigned extract_freq(u32 val, struct acpi_cpufreq_data *data) +static unsigned extract_freq(struct cpufreq_policy *policy, u32 val) { + struct acpi_cpufreq_data *data = policy->driver_data; + switch (data->cpu_feature) { case SYSTEM_INTEL_MSR_CAPABLE: case SYSTEM_AMD_MSR_CAPABLE: - return extract_msr(val, data); + return extract_msr(policy, val); case SYSTEM_IO_CAPABLE: - return extract_io(val, data); + return extract_io(policy, val); default: return 0; } @@ -374,11 +377,11 @@ static unsigned int get_cur_freq_on_cpu(unsigned int cpu) return 0; data = policy->driver_data; - if (unlikely(!data || !data->freq_table)) + if (unlikely(!data || !policy->freq_table)) return 0; - cached_freq = data->freq_table[to_perf_data(data)->state].frequency; - freq = extract_freq(get_cur_val(cpumask_of(cpu), data), data); + cached_freq = policy->freq_table[to_perf_data(data)->state].frequency; + freq = extract_freq(policy, get_cur_val(cpumask_of(cpu), data)); if (freq != cached_freq) { /* * The dreaded BIOS frequency change behind our back. @@ -392,14 +395,15 @@ static unsigned int get_cur_freq_on_cpu(unsigned int cpu) return freq; } -static unsigned int check_freqs(const struct cpumask *mask, unsigned int freq, - struct acpi_cpufreq_data *data) +static unsigned int check_freqs(struct cpufreq_policy *policy, + const struct cpumask *mask, unsigned int freq) { + struct acpi_cpufreq_data *data = policy->driver_data; unsigned int cur_freq; unsigned int i; for (i = 0; i < 100; i++) { - cur_freq = extract_freq(get_cur_val(mask, data), data); + cur_freq = extract_freq(policy, get_cur_val(mask, data)); if (cur_freq == freq) return 1; udelay(10); @@ -416,12 +420,12 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, unsigned int next_perf_state = 0; /* Index into perf table */ int result = 0; - if (unlikely(data == NULL || data->freq_table == NULL)) { + if (unlikely(!data)) { return -ENODEV; } perf = to_perf_data(data); - next_perf_state = data->freq_table[index].driver_data; + next_perf_state = policy->freq_table[index].driver_data; if (perf->state == next_perf_state) { if (unlikely(data->resume)) { pr_debug("Called after resume, resetting to P%d\n", @@ -444,8 +448,8 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, drv_write(data, mask, perf->states[next_perf_state].control); if (acpi_pstate_strict) { - if (!check_freqs(mask, data->freq_table[index].frequency, - data)) { + if (!check_freqs(policy, mask, + policy->freq_table[index].frequency)) { pr_debug("acpi_cpufreq_target failed (%d)\n", policy->cpu); result = -EAGAIN; @@ -458,6 +462,43 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, return result; } +unsigned int acpi_cpufreq_fast_switch(struct cpufreq_policy *policy, + unsigned int target_freq) +{ + struct acpi_cpufreq_data *data = policy->driver_data; + struct acpi_processor_performance *perf; + struct cpufreq_frequency_table *entry; + unsigned int next_perf_state, next_freq, freq; + + /* + * Find the closest frequency above target_freq. + * + * The table is sorted in the reverse order with respect to the + * frequency and all of the entries are valid (see the initialization). + */ + entry = policy->freq_table; + do { + entry++; + freq = entry->frequency; + } while (freq >= target_freq && freq != CPUFREQ_TABLE_END); + entry--; + next_freq = entry->frequency; + next_perf_state = entry->driver_data; + + perf = to_perf_data(data); + if (perf->state == next_perf_state) { + if (unlikely(data->resume)) + data->resume = 0; + else + return next_freq; + } + + data->cpu_freq_write(&perf->control_register, + perf->states[next_perf_state].control); + perf->state = next_perf_state; + return next_freq; +} + static unsigned long acpi_cpufreq_guess_freq(struct acpi_cpufreq_data *data, unsigned int cpu) { @@ -611,10 +652,7 @@ static int acpi_cpufreq_blacklist(struct cpuinfo_x86 *c) if ((c->x86 == 15) && (c->x86_model == 6) && (c->x86_mask == 8)) { - printk(KERN_INFO "acpi-cpufreq: Intel(R) " - "Xeon(R) 7100 Errata AL30, processors may " - "lock up on frequency changes: disabling " - "acpi-cpufreq.\n"); + pr_info("Intel(R) Xeon(R) 7100 Errata AL30, processors may lock up on frequency changes: disabling acpi-cpufreq\n"); return -ENODEV; } } @@ -631,6 +669,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) unsigned int result = 0; struct cpuinfo_x86 *c = &cpu_data(policy->cpu); struct acpi_processor_performance *perf; + struct cpufreq_frequency_table *freq_table; #ifdef CONFIG_SMP static int blacklisted; #endif @@ -690,7 +729,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) cpumask_copy(data->freqdomain_cpus, topology_sibling_cpumask(cpu)); policy->shared_type = CPUFREQ_SHARED_TYPE_HW; - pr_info_once(PFX "overriding BIOS provided _PSD data\n"); + pr_info_once("overriding BIOS provided _PSD data\n"); } #endif @@ -742,9 +781,9 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) goto err_unreg; } - data->freq_table = kzalloc(sizeof(*data->freq_table) * + freq_table = kzalloc(sizeof(*freq_table) * (perf->state_count+1), GFP_KERNEL); - if (!data->freq_table) { + if (!freq_table) { result = -ENOMEM; goto err_unreg; } @@ -762,30 +801,29 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) if (perf->control_register.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE && policy->cpuinfo.transition_latency > 20 * 1000) { policy->cpuinfo.transition_latency = 20 * 1000; - printk_once(KERN_INFO - "P-state transition latency capped at 20 uS\n"); + pr_info_once("P-state transition latency capped at 20 uS\n"); } /* table init */ for (i = 0; i < perf->state_count; i++) { if (i > 0 && perf->states[i].core_frequency >= - data->freq_table[valid_states-1].frequency / 1000) + freq_table[valid_states-1].frequency / 1000) continue; - data->freq_table[valid_states].driver_data = i; - data->freq_table[valid_states].frequency = + freq_table[valid_states].driver_data = i; + freq_table[valid_states].frequency = perf->states[i].core_frequency * 1000; valid_states++; } - data->freq_table[valid_states].frequency = CPUFREQ_TABLE_END; + freq_table[valid_states].frequency = CPUFREQ_TABLE_END; perf->state = 0; - result = cpufreq_table_validate_and_show(policy, data->freq_table); + result = cpufreq_table_validate_and_show(policy, freq_table); if (result) goto err_freqfree; if (perf->states[0].core_frequency * 1000 != policy->cpuinfo.max_freq) - printk(KERN_WARNING FW_WARN "P-state 0 is not max freq\n"); + pr_warn(FW_WARN "P-state 0 is not max freq\n"); switch (perf->control_register.space_id) { case ACPI_ADR_SPACE_SYSTEM_IO: @@ -821,10 +859,13 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) */ data->resume = 1; + policy->fast_switch_possible = !acpi_pstate_strict && + !(policy_is_shared(policy) && policy->shared_type != CPUFREQ_SHARED_TYPE_ANY); + return result; err_freqfree: - kfree(data->freq_table); + kfree(freq_table); err_unreg: acpi_processor_unregister_performance(cpu); err_free_mask: @@ -842,13 +883,12 @@ static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy) pr_debug("acpi_cpufreq_cpu_exit\n"); - if (data) { - policy->driver_data = NULL; - acpi_processor_unregister_performance(data->acpi_perf_cpu); - free_cpumask_var(data->freqdomain_cpus); - kfree(data->freq_table); - kfree(data); - } + policy->fast_switch_possible = false; + policy->driver_data = NULL; + acpi_processor_unregister_performance(data->acpi_perf_cpu); + free_cpumask_var(data->freqdomain_cpus); + kfree(policy->freq_table); + kfree(data); return 0; } @@ -876,6 +916,7 @@ static struct freq_attr *acpi_cpufreq_attr[] = { static struct cpufreq_driver acpi_cpufreq_driver = { .verify = cpufreq_generic_frequency_table_verify, .target_index = acpi_cpufreq_target, + .fast_switch = acpi_cpufreq_fast_switch, .bios_limit = acpi_processor_get_bios_limit, .init = acpi_cpufreq_cpu_init, .exit = acpi_cpufreq_cpu_exit, diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index c251247ae661..418042201e6d 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -298,7 +298,8 @@ static int merge_cluster_tables(void) return 0; } -static void _put_cluster_clk_and_freq_table(struct device *cpu_dev) +static void _put_cluster_clk_and_freq_table(struct device *cpu_dev, + const struct cpumask *cpumask) { u32 cluster = raw_cpu_to_cluster(cpu_dev->id); @@ -308,11 +309,12 @@ static void _put_cluster_clk_and_freq_table(struct device *cpu_dev) clk_put(clk[cluster]); dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]); if (arm_bL_ops->free_opp_table) - arm_bL_ops->free_opp_table(cpu_dev); + arm_bL_ops->free_opp_table(cpumask); dev_dbg(cpu_dev, "%s: cluster: %d\n", __func__, cluster); } -static void put_cluster_clk_and_freq_table(struct device *cpu_dev) +static void put_cluster_clk_and_freq_table(struct device *cpu_dev, + const struct cpumask *cpumask) { u32 cluster = cpu_to_cluster(cpu_dev->id); int i; @@ -321,7 +323,7 @@ static void put_cluster_clk_and_freq_table(struct device *cpu_dev) return; if (cluster < MAX_CLUSTERS) - return _put_cluster_clk_and_freq_table(cpu_dev); + return _put_cluster_clk_and_freq_table(cpu_dev, cpumask); for_each_present_cpu(i) { struct device *cdev = get_cpu_device(i); @@ -330,14 +332,15 @@ static void put_cluster_clk_and_freq_table(struct device *cpu_dev) return; } - _put_cluster_clk_and_freq_table(cdev); + _put_cluster_clk_and_freq_table(cdev, cpumask); } /* free virtual table */ kfree(freq_table[cluster]); } -static int _get_cluster_clk_and_freq_table(struct device *cpu_dev) +static int _get_cluster_clk_and_freq_table(struct device *cpu_dev, + const struct cpumask *cpumask) { u32 cluster = raw_cpu_to_cluster(cpu_dev->id); int ret; @@ -345,7 +348,7 @@ static int _get_cluster_clk_and_freq_table(struct device *cpu_dev) if (freq_table[cluster]) return 0; - ret = arm_bL_ops->init_opp_table(cpu_dev); + ret = arm_bL_ops->init_opp_table(cpumask); if (ret) { dev_err(cpu_dev, "%s: init_opp_table failed, cpu: %d, err: %d\n", __func__, cpu_dev->id, ret); @@ -374,14 +377,15 @@ static int _get_cluster_clk_and_freq_table(struct device *cpu_dev) free_opp_table: if (arm_bL_ops->free_opp_table) - arm_bL_ops->free_opp_table(cpu_dev); + arm_bL_ops->free_opp_table(cpumask); out: dev_err(cpu_dev, "%s: Failed to get data for cluster: %d\n", __func__, cluster); return ret; } -static int get_cluster_clk_and_freq_table(struct device *cpu_dev) +static int get_cluster_clk_and_freq_table(struct device *cpu_dev, + const struct cpumask *cpumask) { u32 cluster = cpu_to_cluster(cpu_dev->id); int i, ret; @@ -390,7 +394,7 @@ static int get_cluster_clk_and_freq_table(struct device *cpu_dev) return 0; if (cluster < MAX_CLUSTERS) { - ret = _get_cluster_clk_and_freq_table(cpu_dev); + ret = _get_cluster_clk_and_freq_table(cpu_dev, cpumask); if (ret) atomic_dec(&cluster_usage[cluster]); return ret; @@ -407,7 +411,7 @@ static int get_cluster_clk_and_freq_table(struct device *cpu_dev) return -ENODEV; } - ret = _get_cluster_clk_and_freq_table(cdev); + ret = _get_cluster_clk_and_freq_table(cdev, cpumask); if (ret) goto put_clusters; } @@ -433,7 +437,7 @@ put_clusters: return -ENODEV; } - _put_cluster_clk_and_freq_table(cdev); + _put_cluster_clk_and_freq_table(cdev, cpumask); } atomic_dec(&cluster_usage[cluster]); @@ -455,18 +459,6 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy) return -ENODEV; } - ret = get_cluster_clk_and_freq_table(cpu_dev); - if (ret) - return ret; - - ret = cpufreq_table_validate_and_show(policy, freq_table[cur_cluster]); - if (ret) { - dev_err(cpu_dev, "CPU %d, cluster: %d invalid freq table\n", - policy->cpu, cur_cluster); - put_cluster_clk_and_freq_table(cpu_dev); - return ret; - } - if (cur_cluster < MAX_CLUSTERS) { int cpu; @@ -479,6 +471,18 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy) per_cpu(physical_cluster, policy->cpu) = A15_CLUSTER; } + ret = get_cluster_clk_and_freq_table(cpu_dev, policy->cpus); + if (ret) + return ret; + + ret = cpufreq_table_validate_and_show(policy, freq_table[cur_cluster]); + if (ret) { + dev_err(cpu_dev, "CPU %d, cluster: %d invalid freq table\n", + policy->cpu, cur_cluster); + put_cluster_clk_and_freq_table(cpu_dev, policy->cpus); + return ret; + } + if (arm_bL_ops->get_transition_latency) policy->cpuinfo.transition_latency = arm_bL_ops->get_transition_latency(cpu_dev); @@ -509,7 +513,7 @@ static int bL_cpufreq_exit(struct cpufreq_policy *policy) return -ENODEV; } - put_cluster_clk_and_freq_table(cpu_dev); + put_cluster_clk_and_freq_table(cpu_dev, policy->related_cpus); dev_dbg(cpu_dev, "%s: Exited, cpu: %d\n", __func__, policy->cpu); return 0; diff --git a/drivers/cpufreq/arm_big_little.h b/drivers/cpufreq/arm_big_little.h index b88889d9387e..184d7c3a112a 100644 --- a/drivers/cpufreq/arm_big_little.h +++ b/drivers/cpufreq/arm_big_little.h @@ -30,11 +30,11 @@ struct cpufreq_arm_bL_ops { * This must set opp table for cpu_dev in a similar way as done by * dev_pm_opp_of_add_table(). */ - int (*init_opp_table)(struct device *cpu_dev); + int (*init_opp_table)(const struct cpumask *cpumask); /* Optional */ int (*get_transition_latency)(struct device *cpu_dev); - void (*free_opp_table)(struct device *cpu_dev); + void (*free_opp_table)(const struct cpumask *cpumask); }; int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops); diff --git a/drivers/cpufreq/arm_big_little_dt.c b/drivers/cpufreq/arm_big_little_dt.c index 16ddeefe9443..39b3f51d9a30 100644 --- a/drivers/cpufreq/arm_big_little_dt.c +++ b/drivers/cpufreq/arm_big_little_dt.c @@ -43,23 +43,6 @@ static struct device_node *get_cpu_node_with_valid_op(int cpu) return np; } -static int dt_init_opp_table(struct device *cpu_dev) -{ - struct device_node *np; - int ret; - - np = of_node_get(cpu_dev->of_node); - if (!np) { - pr_err("failed to find cpu%d node\n", cpu_dev->id); - return -ENOENT; - } - - ret = dev_pm_opp_of_add_table(cpu_dev); - of_node_put(np); - - return ret; -} - static int dt_get_transition_latency(struct device *cpu_dev) { struct device_node *np; @@ -81,8 +64,8 @@ static int dt_get_transition_latency(struct device *cpu_dev) static struct cpufreq_arm_bL_ops dt_bL_ops = { .name = "dt-bl", .get_transition_latency = dt_get_transition_latency, - .init_opp_table = dt_init_opp_table, - .free_opp_table = dev_pm_opp_of_remove_table, + .init_opp_table = dev_pm_opp_of_cpumask_add_table, + .free_opp_table = dev_pm_opp_of_cpumask_remove_table, }; static int generic_bL_probe(struct platform_device *pdev) diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 7c0bdfb1a2ca..8882b8e2ecd0 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -173,4 +173,25 @@ out: return -ENODEV; } +static void __exit cppc_cpufreq_exit(void) +{ + struct cpudata *cpu; + int i; + + cpufreq_unregister_driver(&cppc_cpufreq_driver); + + for_each_possible_cpu(i) { + cpu = all_cpu_data[i]; + free_cpumask_var(cpu->shared_cpu_map); + kfree(cpu); + } + + kfree(all_cpu_data); +} + +module_exit(cppc_cpufreq_exit); +MODULE_AUTHOR("Ashwin Chaugule"); +MODULE_DESCRIPTION("CPUFreq driver based on the ACPI CPPC v5.0+ spec"); +MODULE_LICENSE("GPL"); + late_initcall(cppc_cpufreq_init); diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c new file mode 100644 index 000000000000..3646b143bbf5 --- /dev/null +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2016 Linaro. + * Viresh Kumar <viresh.kumar@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/err.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +static const struct of_device_id machines[] __initconst = { + { .compatible = "allwinner,sun4i-a10", }, + { .compatible = "allwinner,sun5i-a10s", }, + { .compatible = "allwinner,sun5i-a13", }, + { .compatible = "allwinner,sun5i-r8", }, + { .compatible = "allwinner,sun6i-a31", }, + { .compatible = "allwinner,sun6i-a31s", }, + { .compatible = "allwinner,sun7i-a20", }, + { .compatible = "allwinner,sun8i-a23", }, + { .compatible = "allwinner,sun8i-a33", }, + { .compatible = "allwinner,sun8i-a83t", }, + { .compatible = "allwinner,sun8i-h3", }, + + { .compatible = "hisilicon,hi6220", }, + + { .compatible = "fsl,imx27", }, + { .compatible = "fsl,imx51", }, + { .compatible = "fsl,imx53", }, + { .compatible = "fsl,imx7d", }, + + { .compatible = "marvell,berlin", }, + + { .compatible = "samsung,exynos3250", }, + { .compatible = "samsung,exynos4210", }, + { .compatible = "samsung,exynos4212", }, + { .compatible = "samsung,exynos4412", }, + { .compatible = "samsung,exynos5250", }, +#ifndef CONFIG_BL_SWITCHER + { .compatible = "samsung,exynos5420", }, + { .compatible = "samsung,exynos5800", }, +#endif + + { .compatible = "renesas,emev2", }, + { .compatible = "renesas,r7s72100", }, + { .compatible = "renesas,r8a73a4", }, + { .compatible = "renesas,r8a7740", }, + { .compatible = "renesas,r8a7778", }, + { .compatible = "renesas,r8a7779", }, + { .compatible = "renesas,r8a7790", }, + { .compatible = "renesas,r8a7791", }, + { .compatible = "renesas,r8a7793", }, + { .compatible = "renesas,r8a7794", }, + { .compatible = "renesas,sh73a0", }, + + { .compatible = "rockchip,rk2928", }, + { .compatible = "rockchip,rk3036", }, + { .compatible = "rockchip,rk3066a", }, + { .compatible = "rockchip,rk3066b", }, + { .compatible = "rockchip,rk3188", }, + { .compatible = "rockchip,rk3228", }, + { .compatible = "rockchip,rk3288", }, + { .compatible = "rockchip,rk3366", }, + { .compatible = "rockchip,rk3368", }, + { .compatible = "rockchip,rk3399", }, + + { .compatible = "sigma,tango4" }, + + { .compatible = "ti,omap2", }, + { .compatible = "ti,omap3", }, + { .compatible = "ti,omap4", }, + { .compatible = "ti,omap5", }, + + { .compatible = "xlnx,zynq-7000", }, +}; + +static int __init cpufreq_dt_platdev_init(void) +{ + struct device_node *np = of_find_node_by_path("/"); + + if (!np) + return -ENODEV; + + if (!of_match_node(machines, np)) + return -ENODEV; + + of_node_put(of_root); + + return PTR_ERR_OR_ZERO(platform_device_register_simple("cpufreq-dt", -1, + NULL, 0)); +} +device_initcall(cpufreq_dt_platdev_init); diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index 5f8dbe640a20..3957de801ae8 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -15,7 +15,6 @@ #include <linux/cpu.h> #include <linux/cpu_cooling.h> #include <linux/cpufreq.h> -#include <linux/cpufreq-dt.h> #include <linux/cpumask.h> #include <linux/err.h> #include <linux/module.h> @@ -147,7 +146,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) struct clk *cpu_clk; struct dev_pm_opp *suspend_opp; unsigned int transition_latency; - bool opp_v1 = false; + bool fallback = false; const char *name; int ret; @@ -167,14 +166,16 @@ static int cpufreq_init(struct cpufreq_policy *policy) /* Get OPP-sharing information from "operating-points-v2" bindings */ ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, policy->cpus); if (ret) { + if (ret != -ENOENT) + goto out_put_clk; + /* * operating-points-v2 not supported, fallback to old method of - * finding shared-OPPs for backward compatibility. + * finding shared-OPPs for backward compatibility if the + * platform hasn't set sharing CPUs. */ - if (ret == -ENOENT) - opp_v1 = true; - else - goto out_put_clk; + if (dev_pm_opp_get_sharing_cpus(cpu_dev, policy->cpus)) + fallback = true; } /* @@ -214,11 +215,8 @@ static int cpufreq_init(struct cpufreq_policy *policy) goto out_free_opp; } - if (opp_v1) { - struct cpufreq_dt_platform_data *pd = cpufreq_get_driver_data(); - - if (!pd || !pd->independent_clocks) - cpumask_setall(policy->cpus); + if (fallback) { + cpumask_setall(policy->cpus); /* * OPP tables are initialized only for policy->cpu, do it for diff --git a/drivers/cpufreq/cpufreq-nforce2.c b/drivers/cpufreq/cpufreq-nforce2.c index db69eeb501a7..5503d491b016 100644 --- a/drivers/cpufreq/cpufreq-nforce2.c +++ b/drivers/cpufreq/cpufreq-nforce2.c @@ -7,6 +7,8 @@ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -56,8 +58,6 @@ MODULE_PARM_DESC(fid, "CPU multiplier to use (11.5 = 115)"); MODULE_PARM_DESC(min_fsb, "Minimum FSB to use, if not defined: current FSB - 50"); -#define PFX "cpufreq-nforce2: " - /** * nforce2_calc_fsb - calculate FSB * @pll: PLL value @@ -174,13 +174,13 @@ static int nforce2_set_fsb(unsigned int fsb) int pll = 0; if ((fsb > max_fsb) || (fsb < NFORCE2_MIN_FSB)) { - printk(KERN_ERR PFX "FSB %d is out of range!\n", fsb); + pr_err("FSB %d is out of range!\n", fsb); return -EINVAL; } tfsb = nforce2_fsb_read(0); if (!tfsb) { - printk(KERN_ERR PFX "Error while reading the FSB\n"); + pr_err("Error while reading the FSB\n"); return -EINVAL; } @@ -276,8 +276,7 @@ static int nforce2_target(struct cpufreq_policy *policy, /* local_irq_save(flags); */ if (nforce2_set_fsb(target_fsb) < 0) - printk(KERN_ERR PFX "Changing FSB to %d failed\n", - target_fsb); + pr_err("Changing FSB to %d failed\n", target_fsb); else pr_debug("Changed FSB successfully to %d\n", target_fsb); @@ -325,8 +324,7 @@ static int nforce2_cpu_init(struct cpufreq_policy *policy) /* FIX: Get FID from CPU */ if (!fid) { if (!cpu_khz) { - printk(KERN_WARNING PFX - "cpu_khz not set, can't calculate multiplier!\n"); + pr_warn("cpu_khz not set, can't calculate multiplier!\n"); return -ENODEV; } @@ -341,8 +339,8 @@ static int nforce2_cpu_init(struct cpufreq_policy *policy) } } - printk(KERN_INFO PFX "FSB currently at %i MHz, FID %d.%d\n", fsb, - fid / 10, fid % 10); + pr_info("FSB currently at %i MHz, FID %d.%d\n", + fsb, fid / 10, fid % 10); /* Set maximum FSB to FSB at boot time */ max_fsb = nforce2_fsb_read(1); @@ -401,11 +399,9 @@ static int nforce2_detect_chipset(void) if (nforce2_dev == NULL) return -ENODEV; - printk(KERN_INFO PFX "Detected nForce2 chipset revision %X\n", - nforce2_dev->revision); - printk(KERN_INFO PFX - "FSB changing is maybe unstable and can lead to " - "crashes and data loss.\n"); + pr_info("Detected nForce2 chipset revision %X\n", + nforce2_dev->revision); + pr_info("FSB changing is maybe unstable and can lead to crashes and data loss\n"); return 0; } @@ -423,7 +419,7 @@ static int __init nforce2_init(void) /* detect chipset */ if (nforce2_detect_chipset()) { - printk(KERN_INFO PFX "No nForce2 chipset.\n"); + pr_info("No nForce2 chipset\n"); return -ENODEV; } diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index e93405f0eac4..035513b012ee 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -78,6 +78,11 @@ static int cpufreq_governor(struct cpufreq_policy *policy, unsigned int event); static unsigned int __cpufreq_get(struct cpufreq_policy *policy); static int cpufreq_start_governor(struct cpufreq_policy *policy); +static inline int cpufreq_exit_governor(struct cpufreq_policy *policy) +{ + return cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT); +} + /** * Two notifier lists: the "policy" list is involved in the * validation process for a new CPU frequency policy; the @@ -429,6 +434,73 @@ void cpufreq_freq_transition_end(struct cpufreq_policy *policy, } EXPORT_SYMBOL_GPL(cpufreq_freq_transition_end); +/* + * Fast frequency switching status count. Positive means "enabled", negative + * means "disabled" and 0 means "not decided yet". + */ +static int cpufreq_fast_switch_count; +static DEFINE_MUTEX(cpufreq_fast_switch_lock); + +static void cpufreq_list_transition_notifiers(void) +{ + struct notifier_block *nb; + + pr_info("Registered transition notifiers:\n"); + + mutex_lock(&cpufreq_transition_notifier_list.mutex); + + for (nb = cpufreq_transition_notifier_list.head; nb; nb = nb->next) + pr_info("%pF\n", nb->notifier_call); + + mutex_unlock(&cpufreq_transition_notifier_list.mutex); +} + +/** + * cpufreq_enable_fast_switch - Enable fast frequency switching for policy. + * @policy: cpufreq policy to enable fast frequency switching for. + * + * Try to enable fast frequency switching for @policy. + * + * The attempt will fail if there is at least one transition notifier registered + * at this point, as fast frequency switching is quite fundamentally at odds + * with transition notifiers. Thus if successful, it will make registration of + * transition notifiers fail going forward. + */ +void cpufreq_enable_fast_switch(struct cpufreq_policy *policy) +{ + lockdep_assert_held(&policy->rwsem); + + if (!policy->fast_switch_possible) + return; + + mutex_lock(&cpufreq_fast_switch_lock); + if (cpufreq_fast_switch_count >= 0) { + cpufreq_fast_switch_count++; + policy->fast_switch_enabled = true; + } else { + pr_warn("CPU%u: Fast frequency switching not enabled\n", + policy->cpu); + cpufreq_list_transition_notifiers(); + } + mutex_unlock(&cpufreq_fast_switch_lock); +} +EXPORT_SYMBOL_GPL(cpufreq_enable_fast_switch); + +/** + * cpufreq_disable_fast_switch - Disable fast frequency switching for policy. + * @policy: cpufreq policy to disable fast frequency switching for. + */ +void cpufreq_disable_fast_switch(struct cpufreq_policy *policy) +{ + mutex_lock(&cpufreq_fast_switch_lock); + if (policy->fast_switch_enabled) { + policy->fast_switch_enabled = false; + if (!WARN_ON(cpufreq_fast_switch_count <= 0)) + cpufreq_fast_switch_count--; + } + mutex_unlock(&cpufreq_fast_switch_lock); +} +EXPORT_SYMBOL_GPL(cpufreq_disable_fast_switch); /********************************************************************* * SYSFS INTERFACE * @@ -1248,26 +1320,24 @@ out_free_policy: */ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) { + struct cpufreq_policy *policy; unsigned cpu = dev->id; - int ret; dev_dbg(dev, "%s: adding CPU%u\n", __func__, cpu); - if (cpu_online(cpu)) { - ret = cpufreq_online(cpu); - } else { - /* - * A hotplug notifier will follow and we will handle it as CPU - * online then. For now, just create the sysfs link, unless - * there is no policy or the link is already present. - */ - struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); + if (cpu_online(cpu)) + return cpufreq_online(cpu); - ret = policy && !cpumask_test_and_set_cpu(cpu, policy->real_cpus) - ? add_cpu_dev_symlink(policy, cpu) : 0; - } + /* + * A hotplug notifier will follow and we will handle it as CPU online + * then. For now, just create the sysfs link, unless there is no policy + * or the link is already present. + */ + policy = per_cpu(cpufreq_cpu_data, cpu); + if (!policy || cpumask_test_and_set_cpu(cpu, policy->real_cpus)) + return 0; - return ret; + return add_cpu_dev_symlink(policy, cpu); } static void cpufreq_offline(unsigned int cpu) @@ -1319,7 +1389,7 @@ static void cpufreq_offline(unsigned int cpu) /* If cpu is last user of policy, free policy */ if (has_target()) { - ret = cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT); + ret = cpufreq_exit_governor(policy); if (ret) pr_err("%s: Failed to exit governor\n", __func__); } @@ -1447,8 +1517,12 @@ static unsigned int __cpufreq_get(struct cpufreq_policy *policy) ret_freq = cpufreq_driver->get(policy->cpu); - /* Updating inactive policies is invalid, so avoid doing that. */ - if (unlikely(policy_is_inactive(policy))) + /* + * Updating inactive policies is invalid, so avoid doing that. Also + * if fast frequency switching is used with the given policy, the check + * against policy->cur is pointless, so skip it in that case too. + */ + if (unlikely(policy_is_inactive(policy)) || policy->fast_switch_enabled) return ret_freq; if (ret_freq && policy->cur && @@ -1557,21 +1631,25 @@ void cpufreq_suspend(void) if (!cpufreq_driver) return; - if (!has_target()) + if (!has_target() && !cpufreq_driver->suspend) goto suspend; pr_debug("%s: Suspending Governors\n", __func__); for_each_active_policy(policy) { - down_write(&policy->rwsem); - ret = cpufreq_governor(policy, CPUFREQ_GOV_STOP); - up_write(&policy->rwsem); + if (has_target()) { + down_write(&policy->rwsem); + ret = cpufreq_governor(policy, CPUFREQ_GOV_STOP); + up_write(&policy->rwsem); - if (ret) - pr_err("%s: Failed to stop governor for policy: %p\n", - __func__, policy); - else if (cpufreq_driver->suspend - && cpufreq_driver->suspend(policy)) + if (ret) { + pr_err("%s: Failed to stop governor for policy: %p\n", + __func__, policy); + continue; + } + } + + if (cpufreq_driver->suspend && cpufreq_driver->suspend(policy)) pr_err("%s: Failed to suspend driver: %p\n", __func__, policy); } @@ -1596,7 +1674,7 @@ void cpufreq_resume(void) cpufreq_suspended = false; - if (!has_target()) + if (!has_target() && !cpufreq_driver->resume) return; pr_debug("%s: Resuming Governors\n", __func__); @@ -1605,7 +1683,7 @@ void cpufreq_resume(void) if (cpufreq_driver->resume && cpufreq_driver->resume(policy)) { pr_err("%s: Failed to resume driver: %p\n", __func__, policy); - } else { + } else if (has_target()) { down_write(&policy->rwsem); ret = cpufreq_start_governor(policy); up_write(&policy->rwsem); @@ -1675,8 +1753,18 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) switch (list) { case CPUFREQ_TRANSITION_NOTIFIER: + mutex_lock(&cpufreq_fast_switch_lock); + + if (cpufreq_fast_switch_count > 0) { + mutex_unlock(&cpufreq_fast_switch_lock); + return -EBUSY; + } ret = srcu_notifier_chain_register( &cpufreq_transition_notifier_list, nb); + if (!ret) + cpufreq_fast_switch_count--; + + mutex_unlock(&cpufreq_fast_switch_lock); break; case CPUFREQ_POLICY_NOTIFIER: ret = blocking_notifier_chain_register( @@ -1709,8 +1797,14 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list) switch (list) { case CPUFREQ_TRANSITION_NOTIFIER: + mutex_lock(&cpufreq_fast_switch_lock); + ret = srcu_notifier_chain_unregister( &cpufreq_transition_notifier_list, nb); + if (!ret && !WARN_ON(cpufreq_fast_switch_count >= 0)) + cpufreq_fast_switch_count++; + + mutex_unlock(&cpufreq_fast_switch_lock); break; case CPUFREQ_POLICY_NOTIFIER: ret = blocking_notifier_chain_unregister( @@ -1729,6 +1823,37 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier); * GOVERNORS * *********************************************************************/ +/** + * cpufreq_driver_fast_switch - Carry out a fast CPU frequency switch. + * @policy: cpufreq policy to switch the frequency for. + * @target_freq: New frequency to set (may be approximate). + * + * Carry out a fast frequency switch without sleeping. + * + * The driver's ->fast_switch() callback invoked by this function must be + * suitable for being called from within RCU-sched read-side critical sections + * and it is expected to select the minimum available frequency greater than or + * equal to @target_freq (CPUFREQ_RELATION_L). + * + * This function must not be called if policy->fast_switch_enabled is unset. + * + * Governors calling this function must guarantee that it will never be invoked + * twice in parallel for the same policy and that it will never be called in + * parallel with either ->target() or ->target_index() for the same policy. + * + * If CPUFREQ_ENTRY_INVALID is returned by the driver's ->fast_switch() + * callback to indicate an error condition, the hardware configuration must be + * preserved. + */ +unsigned int cpufreq_driver_fast_switch(struct cpufreq_policy *policy, + unsigned int target_freq) +{ + clamp_val(target_freq, policy->min, policy->max); + + return cpufreq_driver->fast_switch(policy, target_freq); +} +EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch); + /* Must set freqs->new to intermediate frequency */ static int __target_intermediate(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs, int index) @@ -2104,7 +2229,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, return ret; } - ret = cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT); + ret = cpufreq_exit_governor(policy); if (ret) { pr_err("%s: Failed to Exit Governor: %s (%d)\n", __func__, old_gov->name, ret); @@ -2121,7 +2246,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, pr_debug("cpufreq: governor change\n"); return 0; } - cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT); + cpufreq_exit_governor(policy); } /* new governor failed, so re-start old one */ @@ -2189,16 +2314,13 @@ static int cpufreq_cpu_callback(struct notifier_block *nfb, switch (action & ~CPU_TASKS_FROZEN) { case CPU_ONLINE: + case CPU_DOWN_FAILED: cpufreq_online(cpu); break; case CPU_DOWN_PREPARE: cpufreq_offline(cpu); break; - - case CPU_DOWN_FAILED: - cpufreq_online(cpu); - break; } return NOTIFY_OK; } diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index bf4913f6453b..316df247e00d 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -129,9 +129,10 @@ static struct notifier_block cs_cpufreq_notifier_block = { /************************** sysfs interface ************************/ static struct dbs_governor cs_dbs_gov; -static ssize_t store_sampling_down_factor(struct dbs_data *dbs_data, - const char *buf, size_t count) +static ssize_t store_sampling_down_factor(struct gov_attr_set *attr_set, + const char *buf, size_t count) { + struct dbs_data *dbs_data = to_dbs_data(attr_set); unsigned int input; int ret; ret = sscanf(buf, "%u", &input); @@ -143,9 +144,10 @@ static ssize_t store_sampling_down_factor(struct dbs_data *dbs_data, return count; } -static ssize_t store_up_threshold(struct dbs_data *dbs_data, const char *buf, - size_t count) +static ssize_t store_up_threshold(struct gov_attr_set *attr_set, + const char *buf, size_t count) { + struct dbs_data *dbs_data = to_dbs_data(attr_set); struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; unsigned int input; int ret; @@ -158,9 +160,10 @@ static ssize_t store_up_threshold(struct dbs_data *dbs_data, const char *buf, return count; } -static ssize_t store_down_threshold(struct dbs_data *dbs_data, const char *buf, - size_t count) +static ssize_t store_down_threshold(struct gov_attr_set *attr_set, + const char *buf, size_t count) { + struct dbs_data *dbs_data = to_dbs_data(attr_set); struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; unsigned int input; int ret; @@ -175,9 +178,10 @@ static ssize_t store_down_threshold(struct dbs_data *dbs_data, const char *buf, return count; } -static ssize_t store_ignore_nice_load(struct dbs_data *dbs_data, - const char *buf, size_t count) +static ssize_t store_ignore_nice_load(struct gov_attr_set *attr_set, + const char *buf, size_t count) { + struct dbs_data *dbs_data = to_dbs_data(attr_set); unsigned int input; int ret; @@ -199,9 +203,10 @@ static ssize_t store_ignore_nice_load(struct dbs_data *dbs_data, return count; } -static ssize_t store_freq_step(struct dbs_data *dbs_data, const char *buf, - size_t count) +static ssize_t store_freq_step(struct gov_attr_set *attr_set, const char *buf, + size_t count) { + struct dbs_data *dbs_data = to_dbs_data(attr_set); struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; unsigned int input; int ret; diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index 10a5cfeae8c5..be498d56dd69 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -43,9 +43,10 @@ static DEFINE_MUTEX(gov_dbs_data_mutex); * This must be called with dbs_data->mutex held, otherwise traversing * policy_dbs_list isn't safe. */ -ssize_t store_sampling_rate(struct dbs_data *dbs_data, const char *buf, +ssize_t store_sampling_rate(struct gov_attr_set *attr_set, const char *buf, size_t count) { + struct dbs_data *dbs_data = to_dbs_data(attr_set); struct policy_dbs_info *policy_dbs; unsigned int rate; int ret; @@ -59,7 +60,7 @@ ssize_t store_sampling_rate(struct dbs_data *dbs_data, const char *buf, * We are operating under dbs_data->mutex and so the list and its * entries can't be freed concurrently. */ - list_for_each_entry(policy_dbs, &dbs_data->policy_dbs_list, list) { + list_for_each_entry(policy_dbs, &attr_set->policy_list, list) { mutex_lock(&policy_dbs->timer_mutex); /* * On 32-bit architectures this may race with the @@ -96,13 +97,13 @@ void gov_update_cpu_data(struct dbs_data *dbs_data) { struct policy_dbs_info *policy_dbs; - list_for_each_entry(policy_dbs, &dbs_data->policy_dbs_list, list) { + list_for_each_entry(policy_dbs, &dbs_data->attr_set.policy_list, list) { unsigned int j; for_each_cpu(j, policy_dbs->policy->cpus) { struct cpu_dbs_info *j_cdbs = &per_cpu(cpu_dbs, j); - j_cdbs->prev_cpu_idle = get_cpu_idle_time(j, &j_cdbs->prev_cpu_wall, + j_cdbs->prev_cpu_idle = get_cpu_idle_time(j, &j_cdbs->prev_update_time, dbs_data->io_is_busy); if (dbs_data->ignore_nice_load) j_cdbs->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; @@ -111,54 +112,6 @@ void gov_update_cpu_data(struct dbs_data *dbs_data) } EXPORT_SYMBOL_GPL(gov_update_cpu_data); -static inline struct dbs_data *to_dbs_data(struct kobject *kobj) -{ - return container_of(kobj, struct dbs_data, kobj); -} - -static inline struct governor_attr *to_gov_attr(struct attribute *attr) -{ - return container_of(attr, struct governor_attr, attr); -} - -static ssize_t governor_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct dbs_data *dbs_data = to_dbs_data(kobj); - struct governor_attr *gattr = to_gov_attr(attr); - - return gattr->show(dbs_data, buf); -} - -static ssize_t governor_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) -{ - struct dbs_data *dbs_data = to_dbs_data(kobj); - struct governor_attr *gattr = to_gov_attr(attr); - int ret = -EBUSY; - - mutex_lock(&dbs_data->mutex); - - if (dbs_data->usage_count) - ret = gattr->store(dbs_data, buf, count); - - mutex_unlock(&dbs_data->mutex); - - return ret; -} - -/* - * Sysfs Ops for accessing governor attributes. - * - * All show/store invocations for governor specific sysfs attributes, will first - * call the below show/store callbacks and the attribute specific callback will - * be called from within it. - */ -static const struct sysfs_ops governor_sysfs_ops = { - .show = governor_show, - .store = governor_store, -}; - unsigned int dbs_update(struct cpufreq_policy *policy) { struct policy_dbs_info *policy_dbs = policy->governor_data; @@ -184,21 +137,17 @@ unsigned int dbs_update(struct cpufreq_policy *policy) /* Get Absolute Load */ for_each_cpu(j, policy->cpus) { struct cpu_dbs_info *j_cdbs = &per_cpu(cpu_dbs, j); - u64 cur_wall_time, cur_idle_time; - unsigned int idle_time, wall_time; + u64 update_time, cur_idle_time; + unsigned int idle_time, time_elapsed; unsigned int load; - cur_idle_time = get_cpu_idle_time(j, &cur_wall_time, io_busy); + cur_idle_time = get_cpu_idle_time(j, &update_time, io_busy); - wall_time = cur_wall_time - j_cdbs->prev_cpu_wall; - j_cdbs->prev_cpu_wall = cur_wall_time; + time_elapsed = update_time - j_cdbs->prev_update_time; + j_cdbs->prev_update_time = update_time; - if (cur_idle_time <= j_cdbs->prev_cpu_idle) { - idle_time = 0; - } else { - idle_time = cur_idle_time - j_cdbs->prev_cpu_idle; - j_cdbs->prev_cpu_idle = cur_idle_time; - } + idle_time = cur_idle_time - j_cdbs->prev_cpu_idle; + j_cdbs->prev_cpu_idle = cur_idle_time; if (ignore_nice) { u64 cur_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; @@ -207,47 +156,62 @@ unsigned int dbs_update(struct cpufreq_policy *policy) j_cdbs->prev_cpu_nice = cur_nice; } - if (unlikely(!wall_time || wall_time < idle_time)) - continue; - - /* - * If the CPU had gone completely idle, and a task just woke up - * on this CPU now, it would be unfair to calculate 'load' the - * usual way for this elapsed time-window, because it will show - * near-zero load, irrespective of how CPU intensive that task - * actually is. This is undesirable for latency-sensitive bursty - * workloads. - * - * To avoid this, we reuse the 'load' from the previous - * time-window and give this task a chance to start with a - * reasonably high CPU frequency. (However, we shouldn't over-do - * this copy, lest we get stuck at a high load (high frequency) - * for too long, even when the current system load has actually - * dropped down. So we perform the copy only once, upon the - * first wake-up from idle.) - * - * Detecting this situation is easy: the governor's utilization - * update handler would not have run during CPU-idle periods. - * Hence, an unusually large 'wall_time' (as compared to the - * sampling rate) indicates this scenario. - * - * prev_load can be zero in two cases and we must recalculate it - * for both cases: - * - during long idle intervals - * - explicitly set to zero - */ - if (unlikely(wall_time > (2 * sampling_rate) && - j_cdbs->prev_load)) { + if (unlikely(!time_elapsed)) { + /* + * That can only happen when this function is called + * twice in a row with a very short interval between the + * calls, so the previous load value can be used then. + */ load = j_cdbs->prev_load; - + } else if (unlikely(time_elapsed > 2 * sampling_rate && + j_cdbs->prev_load)) { /* - * Perform a destructive copy, to ensure that we copy - * the previous load only once, upon the first wake-up - * from idle. + * If the CPU had gone completely idle and a task has + * just woken up on this CPU now, it would be unfair to + * calculate 'load' the usual way for this elapsed + * time-window, because it would show near-zero load, + * irrespective of how CPU intensive that task actually + * was. This is undesirable for latency-sensitive bursty + * workloads. + * + * To avoid this, reuse the 'load' from the previous + * time-window and give this task a chance to start with + * a reasonably high CPU frequency. However, that + * shouldn't be over-done, lest we get stuck at a high + * load (high frequency) for too long, even when the + * current system load has actually dropped down, so + * clear prev_load to guarantee that the load will be + * computed again next time. + * + * Detecting this situation is easy: the governor's + * utilization update handler would not have run during + * CPU-idle periods. Hence, an unusually large + * 'time_elapsed' (as compared to the sampling rate) + * indicates this scenario. */ + load = j_cdbs->prev_load; j_cdbs->prev_load = 0; } else { - load = 100 * (wall_time - idle_time) / wall_time; + if (time_elapsed >= idle_time) { + load = 100 * (time_elapsed - idle_time) / time_elapsed; + } else { + /* + * That can happen if idle_time is returned by + * get_cpu_idle_time_jiffy(). In that case + * idle_time is roughly equal to the difference + * between time_elapsed and "busy time" obtained + * from CPU statistics. Then, the "busy time" + * can end up being greater than time_elapsed + * (for example, if jiffies_64 and the CPU + * statistics are updated by different CPUs), + * so idle_time may in fact be negative. That + * means, though, that the CPU was busy all + * the time (on the rough average) during the + * last sampling interval and 100 can be + * returned as the load. + */ + load = (int)idle_time < 0 ? 100 : 0; + } j_cdbs->prev_load = load; } @@ -258,43 +222,6 @@ unsigned int dbs_update(struct cpufreq_policy *policy) } EXPORT_SYMBOL_GPL(dbs_update); -static void gov_set_update_util(struct policy_dbs_info *policy_dbs, - unsigned int delay_us) -{ - struct cpufreq_policy *policy = policy_dbs->policy; - int cpu; - - gov_update_sample_delay(policy_dbs, delay_us); - policy_dbs->last_sample_time = 0; - - for_each_cpu(cpu, policy->cpus) { - struct cpu_dbs_info *cdbs = &per_cpu(cpu_dbs, cpu); - - cpufreq_set_update_util_data(cpu, &cdbs->update_util); - } -} - -static inline void gov_clear_update_util(struct cpufreq_policy *policy) -{ - int i; - - for_each_cpu(i, policy->cpus) - cpufreq_set_update_util_data(i, NULL); - - synchronize_sched(); -} - -static void gov_cancel_work(struct cpufreq_policy *policy) -{ - struct policy_dbs_info *policy_dbs = policy->governor_data; - - gov_clear_update_util(policy_dbs->policy); - irq_work_sync(&policy_dbs->irq_work); - cancel_work_sync(&policy_dbs->work); - atomic_set(&policy_dbs->work_count, 0); - policy_dbs->work_in_progress = false; -} - static void dbs_work_handler(struct work_struct *work) { struct policy_dbs_info *policy_dbs; @@ -382,6 +309,44 @@ static void dbs_update_util_handler(struct update_util_data *data, u64 time, irq_work_queue(&policy_dbs->irq_work); } +static void gov_set_update_util(struct policy_dbs_info *policy_dbs, + unsigned int delay_us) +{ + struct cpufreq_policy *policy = policy_dbs->policy; + int cpu; + + gov_update_sample_delay(policy_dbs, delay_us); + policy_dbs->last_sample_time = 0; + + for_each_cpu(cpu, policy->cpus) { + struct cpu_dbs_info *cdbs = &per_cpu(cpu_dbs, cpu); + + cpufreq_add_update_util_hook(cpu, &cdbs->update_util, + dbs_update_util_handler); + } +} + +static inline void gov_clear_update_util(struct cpufreq_policy *policy) +{ + int i; + + for_each_cpu(i, policy->cpus) + cpufreq_remove_update_util_hook(i); + + synchronize_sched(); +} + +static void gov_cancel_work(struct cpufreq_policy *policy) +{ + struct policy_dbs_info *policy_dbs = policy->governor_data; + + gov_clear_update_util(policy_dbs->policy); + irq_work_sync(&policy_dbs->irq_work); + cancel_work_sync(&policy_dbs->work); + atomic_set(&policy_dbs->work_count, 0); + policy_dbs->work_in_progress = false; +} + static struct policy_dbs_info *alloc_policy_dbs_info(struct cpufreq_policy *policy, struct dbs_governor *gov) { @@ -404,7 +369,6 @@ static struct policy_dbs_info *alloc_policy_dbs_info(struct cpufreq_policy *poli struct cpu_dbs_info *j_cdbs = &per_cpu(cpu_dbs, j); j_cdbs->policy_dbs = policy_dbs; - j_cdbs->update_util.func = dbs_update_util_handler; } return policy_dbs; } @@ -453,10 +417,7 @@ static int cpufreq_governor_init(struct cpufreq_policy *policy) policy_dbs->dbs_data = dbs_data; policy->governor_data = policy_dbs; - mutex_lock(&dbs_data->mutex); - dbs_data->usage_count++; - list_add(&policy_dbs->list, &dbs_data->policy_dbs_list); - mutex_unlock(&dbs_data->mutex); + gov_attr_set_get(&dbs_data->attr_set, &policy_dbs->list); goto out; } @@ -466,8 +427,7 @@ static int cpufreq_governor_init(struct cpufreq_policy *policy) goto free_policy_dbs_info; } - INIT_LIST_HEAD(&dbs_data->policy_dbs_list); - mutex_init(&dbs_data->mutex); + gov_attr_set_init(&dbs_data->attr_set, &policy_dbs->list); ret = gov->init(dbs_data, !policy->governor->initialized); if (ret) @@ -487,14 +447,11 @@ static int cpufreq_governor_init(struct cpufreq_policy *policy) if (!have_governor_per_policy()) gov->gdbs_data = dbs_data; - policy->governor_data = policy_dbs; - policy_dbs->dbs_data = dbs_data; - dbs_data->usage_count = 1; - list_add(&policy_dbs->list, &dbs_data->policy_dbs_list); + policy->governor_data = policy_dbs; gov->kobj_type.sysfs_ops = &governor_sysfs_ops; - ret = kobject_init_and_add(&dbs_data->kobj, &gov->kobj_type, + ret = kobject_init_and_add(&dbs_data->attr_set.kobj, &gov->kobj_type, get_governor_parent_kobj(policy), "%s", gov->gov.name); if (!ret) @@ -523,29 +480,21 @@ static int cpufreq_governor_exit(struct cpufreq_policy *policy) struct dbs_governor *gov = dbs_governor_of(policy); struct policy_dbs_info *policy_dbs = policy->governor_data; struct dbs_data *dbs_data = policy_dbs->dbs_data; - int count; + unsigned int count; /* Protect gov->gdbs_data against concurrent updates. */ mutex_lock(&gov_dbs_data_mutex); - mutex_lock(&dbs_data->mutex); - list_del(&policy_dbs->list); - count = --dbs_data->usage_count; - mutex_unlock(&dbs_data->mutex); + count = gov_attr_set_put(&dbs_data->attr_set, &policy_dbs->list); - if (!count) { - kobject_put(&dbs_data->kobj); - - policy->governor_data = NULL; + policy->governor_data = NULL; + if (!count) { if (!have_governor_per_policy()) gov->gdbs_data = NULL; gov->exit(dbs_data, policy->governor->initialized == 1); - mutex_destroy(&dbs_data->mutex); kfree(dbs_data); - } else { - policy->governor_data = NULL; } free_policy_dbs_info(policy_dbs, gov); @@ -574,12 +523,12 @@ static int cpufreq_governor_start(struct cpufreq_policy *policy) for_each_cpu(j, policy->cpus) { struct cpu_dbs_info *j_cdbs = &per_cpu(cpu_dbs, j); - unsigned int prev_load; - j_cdbs->prev_cpu_idle = get_cpu_idle_time(j, &j_cdbs->prev_cpu_wall, io_busy); - - prev_load = j_cdbs->prev_cpu_wall - j_cdbs->prev_cpu_idle; - j_cdbs->prev_load = 100 * prev_load / (unsigned int)j_cdbs->prev_cpu_wall; + j_cdbs->prev_cpu_idle = get_cpu_idle_time(j, &j_cdbs->prev_update_time, io_busy); + /* + * Make the first invocation of dbs_update() compute the load. + */ + j_cdbs->prev_load = 0; if (ignore_nice) j_cdbs->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h index 61ff82fe0613..34eb214b6d57 100644 --- a/drivers/cpufreq/cpufreq_governor.h +++ b/drivers/cpufreq/cpufreq_governor.h @@ -24,20 +24,6 @@ #include <linux/module.h> #include <linux/mutex.h> -/* - * The polling frequency depends on the capability of the processor. Default - * polling frequency is 1000 times the transition latency of the processor. The - * governor will work on any processor with transition latency <= 10ms, using - * appropriate sampling rate. - * - * For CPUs with transition latency > 10ms (mostly drivers with CPUFREQ_ETERNAL) - * this governor will not work. All times here are in us (micro seconds). - */ -#define MIN_SAMPLING_RATE_RATIO (2) -#define LATENCY_MULTIPLIER (1000) -#define MIN_LATENCY_MULTIPLIER (20) -#define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) - /* Ondemand Sampling types */ enum {OD_NORMAL_SAMPLE, OD_SUB_SAMPLE}; @@ -52,7 +38,7 @@ enum {OD_NORMAL_SAMPLE, OD_SUB_SAMPLE}; /* Governor demand based switching data (per-policy or global). */ struct dbs_data { - int usage_count; + struct gov_attr_set attr_set; void *tuners; unsigned int min_sampling_rate; unsigned int ignore_nice_load; @@ -60,37 +46,27 @@ struct dbs_data { unsigned int sampling_down_factor; unsigned int up_threshold; unsigned int io_is_busy; - - struct kobject kobj; - struct list_head policy_dbs_list; - /* - * Protect concurrent updates to governor tunables from sysfs, - * policy_dbs_list and usage_count. - */ - struct mutex mutex; }; -/* Governor's specific attributes */ -struct dbs_data; -struct governor_attr { - struct attribute attr; - ssize_t (*show)(struct dbs_data *dbs_data, char *buf); - ssize_t (*store)(struct dbs_data *dbs_data, const char *buf, - size_t count); -}; +static inline struct dbs_data *to_dbs_data(struct gov_attr_set *attr_set) +{ + return container_of(attr_set, struct dbs_data, attr_set); +} #define gov_show_one(_gov, file_name) \ static ssize_t show_##file_name \ -(struct dbs_data *dbs_data, char *buf) \ +(struct gov_attr_set *attr_set, char *buf) \ { \ + struct dbs_data *dbs_data = to_dbs_data(attr_set); \ struct _gov##_dbs_tuners *tuners = dbs_data->tuners; \ return sprintf(buf, "%u\n", tuners->file_name); \ } #define gov_show_one_common(file_name) \ static ssize_t show_##file_name \ -(struct dbs_data *dbs_data, char *buf) \ +(struct gov_attr_set *attr_set, char *buf) \ { \ + struct dbs_data *dbs_data = to_dbs_data(attr_set); \ return sprintf(buf, "%u\n", dbs_data->file_name); \ } @@ -135,7 +111,7 @@ static inline void gov_update_sample_delay(struct policy_dbs_info *policy_dbs, /* Per cpu structures */ struct cpu_dbs_info { u64 prev_cpu_idle; - u64 prev_cpu_wall; + u64 prev_update_time; u64 prev_cpu_nice; /* * Used to keep track of load in the previous interval. However, when @@ -184,7 +160,7 @@ void od_register_powersave_bias_handler(unsigned int (*f) (struct cpufreq_policy *, unsigned int, unsigned int), unsigned int powersave_bias); void od_unregister_powersave_bias_handler(void); -ssize_t store_sampling_rate(struct dbs_data *dbs_data, const char *buf, +ssize_t store_sampling_rate(struct gov_attr_set *attr_set, const char *buf, size_t count); void gov_update_cpu_data(struct dbs_data *dbs_data); #endif /* _CPUFREQ_GOVERNOR_H */ diff --git a/drivers/cpufreq/cpufreq_governor_attr_set.c b/drivers/cpufreq/cpufreq_governor_attr_set.c new file mode 100644 index 000000000000..52841f807a7e --- /dev/null +++ b/drivers/cpufreq/cpufreq_governor_attr_set.c @@ -0,0 +1,84 @@ +/* + * Abstract code for CPUFreq governor tunable sysfs attributes. + * + * Copyright (C) 2016, Intel Corporation + * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "cpufreq_governor.h" + +static inline struct gov_attr_set *to_gov_attr_set(struct kobject *kobj) +{ + return container_of(kobj, struct gov_attr_set, kobj); +} + +static inline struct governor_attr *to_gov_attr(struct attribute *attr) +{ + return container_of(attr, struct governor_attr, attr); +} + +static ssize_t governor_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct governor_attr *gattr = to_gov_attr(attr); + + return gattr->show(to_gov_attr_set(kobj), buf); +} + +static ssize_t governor_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct gov_attr_set *attr_set = to_gov_attr_set(kobj); + struct governor_attr *gattr = to_gov_attr(attr); + int ret; + + mutex_lock(&attr_set->update_lock); + ret = attr_set->usage_count ? gattr->store(attr_set, buf, count) : -EBUSY; + mutex_unlock(&attr_set->update_lock); + return ret; +} + +const struct sysfs_ops governor_sysfs_ops = { + .show = governor_show, + .store = governor_store, +}; +EXPORT_SYMBOL_GPL(governor_sysfs_ops); + +void gov_attr_set_init(struct gov_attr_set *attr_set, struct list_head *list_node) +{ + INIT_LIST_HEAD(&attr_set->policy_list); + mutex_init(&attr_set->update_lock); + attr_set->usage_count = 1; + list_add(list_node, &attr_set->policy_list); +} +EXPORT_SYMBOL_GPL(gov_attr_set_init); + +void gov_attr_set_get(struct gov_attr_set *attr_set, struct list_head *list_node) +{ + mutex_lock(&attr_set->update_lock); + attr_set->usage_count++; + list_add(list_node, &attr_set->policy_list); + mutex_unlock(&attr_set->update_lock); +} +EXPORT_SYMBOL_GPL(gov_attr_set_get); + +unsigned int gov_attr_set_put(struct gov_attr_set *attr_set, struct list_head *list_node) +{ + unsigned int count; + + mutex_lock(&attr_set->update_lock); + list_del(list_node); + count = --attr_set->usage_count; + mutex_unlock(&attr_set->update_lock); + if (count) + return count; + + kobject_put(&attr_set->kobj); + mutex_destroy(&attr_set->update_lock); + return 0; +} +EXPORT_SYMBOL_GPL(gov_attr_set_put); diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index acd80272ded6..300163430516 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -207,9 +207,10 @@ static unsigned int od_dbs_timer(struct cpufreq_policy *policy) /************************** sysfs interface ************************/ static struct dbs_governor od_dbs_gov; -static ssize_t store_io_is_busy(struct dbs_data *dbs_data, const char *buf, - size_t count) +static ssize_t store_io_is_busy(struct gov_attr_set *attr_set, const char *buf, + size_t count) { + struct dbs_data *dbs_data = to_dbs_data(attr_set); unsigned int input; int ret; @@ -224,9 +225,10 @@ static ssize_t store_io_is_busy(struct dbs_data *dbs_data, const char *buf, return count; } -static ssize_t store_up_threshold(struct dbs_data *dbs_data, const char *buf, - size_t count) +static ssize_t store_up_threshold(struct gov_attr_set *attr_set, + const char *buf, size_t count) { + struct dbs_data *dbs_data = to_dbs_data(attr_set); unsigned int input; int ret; ret = sscanf(buf, "%u", &input); @@ -240,9 +242,10 @@ static ssize_t store_up_threshold(struct dbs_data *dbs_data, const char *buf, return count; } -static ssize_t store_sampling_down_factor(struct dbs_data *dbs_data, - const char *buf, size_t count) +static ssize_t store_sampling_down_factor(struct gov_attr_set *attr_set, + const char *buf, size_t count) { + struct dbs_data *dbs_data = to_dbs_data(attr_set); struct policy_dbs_info *policy_dbs; unsigned int input; int ret; @@ -254,7 +257,7 @@ static ssize_t store_sampling_down_factor(struct dbs_data *dbs_data, dbs_data->sampling_down_factor = input; /* Reset down sampling multiplier in case it was active */ - list_for_each_entry(policy_dbs, &dbs_data->policy_dbs_list, list) { + list_for_each_entry(policy_dbs, &attr_set->policy_list, list) { /* * Doing this without locking might lead to using different * rate_mult values in od_update() and od_dbs_timer(). @@ -267,9 +270,10 @@ static ssize_t store_sampling_down_factor(struct dbs_data *dbs_data, return count; } -static ssize_t store_ignore_nice_load(struct dbs_data *dbs_data, - const char *buf, size_t count) +static ssize_t store_ignore_nice_load(struct gov_attr_set *attr_set, + const char *buf, size_t count) { + struct dbs_data *dbs_data = to_dbs_data(attr_set); unsigned int input; int ret; @@ -291,9 +295,10 @@ static ssize_t store_ignore_nice_load(struct dbs_data *dbs_data, return count; } -static ssize_t store_powersave_bias(struct dbs_data *dbs_data, const char *buf, - size_t count) +static ssize_t store_powersave_bias(struct gov_attr_set *attr_set, + const char *buf, size_t count) { + struct dbs_data *dbs_data = to_dbs_data(attr_set); struct od_dbs_tuners *od_tuners = dbs_data->tuners; struct policy_dbs_info *policy_dbs; unsigned int input; @@ -308,7 +313,7 @@ static ssize_t store_powersave_bias(struct dbs_data *dbs_data, const char *buf, od_tuners->powersave_bias = input; - list_for_each_entry(policy_dbs, &dbs_data->policy_dbs_list, list) + list_for_each_entry(policy_dbs, &attr_set->policy_list, list) ondemand_powersave_bias_init(policy_dbs->policy); return count; diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c index 4d16f45ee1da..9f3dec9a3f36 100644 --- a/drivers/cpufreq/cpufreq_userspace.c +++ b/drivers/cpufreq/cpufreq_userspace.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/slab.h> static DEFINE_PER_CPU(unsigned int, cpu_is_managed); static DEFINE_MUTEX(userspace_mutex); @@ -31,6 +32,7 @@ static DEFINE_MUTEX(userspace_mutex); static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq) { int ret = -EINVAL; + unsigned int *setspeed = policy->governor_data; pr_debug("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq); @@ -38,6 +40,8 @@ static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq) if (!per_cpu(cpu_is_managed, policy->cpu)) goto err; + *setspeed = freq; + ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L); err: mutex_unlock(&userspace_mutex); @@ -49,19 +53,45 @@ static ssize_t show_speed(struct cpufreq_policy *policy, char *buf) return sprintf(buf, "%u\n", policy->cur); } +static int cpufreq_userspace_policy_init(struct cpufreq_policy *policy) +{ + unsigned int *setspeed; + + setspeed = kzalloc(sizeof(*setspeed), GFP_KERNEL); + if (!setspeed) + return -ENOMEM; + + policy->governor_data = setspeed; + return 0; +} + static int cpufreq_governor_userspace(struct cpufreq_policy *policy, unsigned int event) { + unsigned int *setspeed = policy->governor_data; unsigned int cpu = policy->cpu; int rc = 0; + if (event == CPUFREQ_GOV_POLICY_INIT) + return cpufreq_userspace_policy_init(policy); + + if (!setspeed) + return -EINVAL; + switch (event) { + case CPUFREQ_GOV_POLICY_EXIT: + mutex_lock(&userspace_mutex); + policy->governor_data = NULL; + kfree(setspeed); + mutex_unlock(&userspace_mutex); + break; case CPUFREQ_GOV_START: BUG_ON(!policy->cur); pr_debug("started managing cpu %u\n", cpu); mutex_lock(&userspace_mutex); per_cpu(cpu_is_managed, cpu) = 1; + *setspeed = policy->cur; mutex_unlock(&userspace_mutex); break; case CPUFREQ_GOV_STOP: @@ -69,20 +99,23 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, mutex_lock(&userspace_mutex); per_cpu(cpu_is_managed, cpu) = 0; + *setspeed = 0; mutex_unlock(&userspace_mutex); break; case CPUFREQ_GOV_LIMITS: mutex_lock(&userspace_mutex); - pr_debug("limit event for cpu %u: %u - %u kHz, currently %u kHz\n", - cpu, policy->min, policy->max, - policy->cur); + pr_debug("limit event for cpu %u: %u - %u kHz, currently %u kHz, last set to %u kHz\n", + cpu, policy->min, policy->max, policy->cur, *setspeed); - if (policy->max < policy->cur) + if (policy->max < *setspeed) __cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H); - else if (policy->min > policy->cur) + else if (policy->min > *setspeed) __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L); + else + __cpufreq_driver_target(policy, *setspeed, + CPUFREQ_RELATION_L); mutex_unlock(&userspace_mutex); break; } diff --git a/drivers/cpufreq/e_powersaver.c b/drivers/cpufreq/e_powersaver.c index 4085244c8a67..cdf097b29862 100644 --- a/drivers/cpufreq/e_powersaver.c +++ b/drivers/cpufreq/e_powersaver.c @@ -6,6 +6,8 @@ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -20,7 +22,7 @@ #include <asm/msr.h> #include <asm/tsc.h> -#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE +#if IS_ENABLED(CONFIG_ACPI_PROCESSOR) #include <linux/acpi.h> #include <acpi/processor.h> #endif @@ -33,7 +35,7 @@ struct eps_cpu_data { u32 fsb; -#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE +#if IS_ENABLED(CONFIG_ACPI_PROCESSOR) u32 bios_limit; #endif struct cpufreq_frequency_table freq_table[]; @@ -46,7 +48,7 @@ static int freq_failsafe_off; static int voltage_failsafe_off; static int set_max_voltage; -#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE +#if IS_ENABLED(CONFIG_ACPI_PROCESSOR) static int ignore_acpi_limit; static struct acpi_processor_performance *eps_acpi_cpu_perf; @@ -141,11 +143,9 @@ static int eps_set_state(struct eps_cpu_data *centaur, /* Print voltage and multiplier */ rdmsr(MSR_IA32_PERF_STATUS, lo, hi); current_voltage = lo & 0xff; - printk(KERN_INFO "eps: Current voltage = %dmV\n", - current_voltage * 16 + 700); + pr_info("Current voltage = %dmV\n", current_voltage * 16 + 700); current_multiplier = (lo >> 8) & 0xff; - printk(KERN_INFO "eps: Current multiplier = %d\n", - current_multiplier); + pr_info("Current multiplier = %d\n", current_multiplier); } #endif return 0; @@ -166,7 +166,7 @@ static int eps_target(struct cpufreq_policy *policy, unsigned int index) dest_state = centaur->freq_table[index].driver_data & 0xffff; ret = eps_set_state(centaur, policy, dest_state); if (ret) - printk(KERN_ERR "eps: Timeout!\n"); + pr_err("Timeout!\n"); return ret; } @@ -186,7 +186,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy) int k, step, voltage; int ret; int states; -#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE +#if IS_ENABLED(CONFIG_ACPI_PROCESSOR) unsigned int limit; #endif @@ -194,36 +194,36 @@ static int eps_cpu_init(struct cpufreq_policy *policy) return -ENODEV; /* Check brand */ - printk(KERN_INFO "eps: Detected VIA "); + pr_info("Detected VIA "); switch (c->x86_model) { case 10: rdmsr(0x1153, lo, hi); brand = (((lo >> 2) ^ lo) >> 18) & 3; - printk(KERN_CONT "Model A "); + pr_cont("Model A "); break; case 13: rdmsr(0x1154, lo, hi); brand = (((lo >> 4) ^ (lo >> 2))) & 0x000000ff; - printk(KERN_CONT "Model D "); + pr_cont("Model D "); break; } switch (brand) { case EPS_BRAND_C7M: - printk(KERN_CONT "C7-M\n"); + pr_cont("C7-M\n"); break; case EPS_BRAND_C7: - printk(KERN_CONT "C7\n"); + pr_cont("C7\n"); break; case EPS_BRAND_EDEN: - printk(KERN_CONT "Eden\n"); + pr_cont("Eden\n"); break; case EPS_BRAND_C7D: - printk(KERN_CONT "C7-D\n"); + pr_cont("C7-D\n"); break; case EPS_BRAND_C3: - printk(KERN_CONT "C3\n"); + pr_cont("C3\n"); return -ENODEV; break; } @@ -235,7 +235,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy) /* Can be locked at 0 */ rdmsrl(MSR_IA32_MISC_ENABLE, val); if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) { - printk(KERN_INFO "eps: Can't enable Enhanced PowerSaver\n"); + pr_info("Can't enable Enhanced PowerSaver\n"); return -ENODEV; } } @@ -243,22 +243,19 @@ static int eps_cpu_init(struct cpufreq_policy *policy) /* Print voltage and multiplier */ rdmsr(MSR_IA32_PERF_STATUS, lo, hi); current_voltage = lo & 0xff; - printk(KERN_INFO "eps: Current voltage = %dmV\n", - current_voltage * 16 + 700); + pr_info("Current voltage = %dmV\n", current_voltage * 16 + 700); current_multiplier = (lo >> 8) & 0xff; - printk(KERN_INFO "eps: Current multiplier = %d\n", current_multiplier); + pr_info("Current multiplier = %d\n", current_multiplier); /* Print limits */ max_voltage = hi & 0xff; - printk(KERN_INFO "eps: Highest voltage = %dmV\n", - max_voltage * 16 + 700); + pr_info("Highest voltage = %dmV\n", max_voltage * 16 + 700); max_multiplier = (hi >> 8) & 0xff; - printk(KERN_INFO "eps: Highest multiplier = %d\n", max_multiplier); + pr_info("Highest multiplier = %d\n", max_multiplier); min_voltage = (hi >> 16) & 0xff; - printk(KERN_INFO "eps: Lowest voltage = %dmV\n", - min_voltage * 16 + 700); + pr_info("Lowest voltage = %dmV\n", min_voltage * 16 + 700); min_multiplier = (hi >> 24) & 0xff; - printk(KERN_INFO "eps: Lowest multiplier = %d\n", min_multiplier); + pr_info("Lowest multiplier = %d\n", min_multiplier); /* Sanity checks */ if (current_multiplier == 0 || max_multiplier == 0 @@ -276,34 +273,30 @@ static int eps_cpu_init(struct cpufreq_policy *policy) /* Check for systems using underclocked CPU */ if (!freq_failsafe_off && max_multiplier != current_multiplier) { - printk(KERN_INFO "eps: Your processor is running at different " - "frequency then its maximum. Aborting.\n"); - printk(KERN_INFO "eps: You can use freq_failsafe_off option " - "to disable this check.\n"); + pr_info("Your processor is running at different frequency then its maximum. Aborting.\n"); + pr_info("You can use freq_failsafe_off option to disable this check.\n"); return -EINVAL; } if (!voltage_failsafe_off && max_voltage != current_voltage) { - printk(KERN_INFO "eps: Your processor is running at different " - "voltage then its maximum. Aborting.\n"); - printk(KERN_INFO "eps: You can use voltage_failsafe_off " - "option to disable this check.\n"); + pr_info("Your processor is running at different voltage then its maximum. Aborting.\n"); + pr_info("You can use voltage_failsafe_off option to disable this check.\n"); return -EINVAL; } /* Calc FSB speed */ fsb = cpu_khz / current_multiplier; -#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE +#if IS_ENABLED(CONFIG_ACPI_PROCESSOR) /* Check for ACPI processor speed limit */ if (!ignore_acpi_limit && !eps_acpi_init()) { if (!acpi_processor_get_bios_limit(policy->cpu, &limit)) { - printk(KERN_INFO "eps: ACPI limit %u.%uGHz\n", + pr_info("ACPI limit %u.%uGHz\n", limit/1000000, (limit%1000000)/10000); eps_acpi_exit(policy); /* Check if max_multiplier is in BIOS limits */ if (limit && max_multiplier * fsb > limit) { - printk(KERN_INFO "eps: Aborting.\n"); + pr_info("Aborting\n"); return -EINVAL; } } @@ -319,8 +312,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy) v = (set_max_voltage - 700) / 16; /* Check if voltage is within limits */ if (v >= min_voltage && v <= max_voltage) { - printk(KERN_INFO "eps: Setting %dmV as maximum.\n", - v * 16 + 700); + pr_info("Setting %dmV as maximum\n", v * 16 + 700); max_voltage = v; } } @@ -341,7 +333,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy) /* Copy basic values */ centaur->fsb = fsb; -#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE +#if IS_ENABLED(CONFIG_ACPI_PROCESSOR) centaur->bios_limit = limit; #endif @@ -426,7 +418,7 @@ module_param(freq_failsafe_off, int, 0644); MODULE_PARM_DESC(freq_failsafe_off, "Disable current vs max frequency check"); module_param(voltage_failsafe_off, int, 0644); MODULE_PARM_DESC(voltage_failsafe_off, "Disable current vs max voltage check"); -#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE +#if IS_ENABLED(CONFIG_ACPI_PROCESSOR) module_param(ignore_acpi_limit, int, 0644); MODULE_PARM_DESC(ignore_acpi_limit, "Don't check ACPI's processor speed limit"); #endif diff --git a/drivers/cpufreq/elanfreq.c b/drivers/cpufreq/elanfreq.c index 1c06e786c9ba..bfce11cba1df 100644 --- a/drivers/cpufreq/elanfreq.c +++ b/drivers/cpufreq/elanfreq.c @@ -16,6 +16,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -185,7 +187,7 @@ static int elanfreq_cpu_init(struct cpufreq_policy *policy) static int __init elanfreq_setup(char *str) { max_freq = simple_strtoul(str, &str, 0); - printk(KERN_WARNING "You're using the deprecated elanfreq command line option. Use elanfreq.max_freq instead, please!\n"); + pr_warn("You're using the deprecated elanfreq command line option. Use elanfreq.max_freq instead, please!\n"); return 1; } __setup("elanfreq=", elanfreq_setup); diff --git a/drivers/cpufreq/hisi-acpu-cpufreq.c b/drivers/cpufreq/hisi-acpu-cpufreq.c deleted file mode 100644 index 026d5b2224de..000000000000 --- a/drivers/cpufreq/hisi-acpu-cpufreq.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Hisilicon Platforms Using ACPU CPUFreq Support - * - * Copyright (c) 2015 Hisilicon Limited. - * Copyright (c) 2015 Linaro Limited. - * - * Leo Yan <leo.yan@linaro.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/err.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/platform_device.h> - -static int __init hisi_acpu_cpufreq_driver_init(void) -{ - struct platform_device *pdev; - - if (!of_machine_is_compatible("hisilicon,hi6220")) - return -ENODEV; - - pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); - return PTR_ERR_OR_ZERO(pdev); -} -module_init(hisi_acpu_cpufreq_driver_init); - -MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>"); -MODULE_DESCRIPTION("Hisilicon acpu cpufreq driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/cpufreq/ia64-acpi-cpufreq.c b/drivers/cpufreq/ia64-acpi-cpufreq.c index 0202429f1c5b..759612da4fdc 100644 --- a/drivers/cpufreq/ia64-acpi-cpufreq.c +++ b/drivers/cpufreq/ia64-acpi-cpufreq.c @@ -8,6 +8,8 @@ * Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/slab.h> #include <linux/module.h> @@ -118,8 +120,7 @@ processor_get_freq ( if (ret) { set_cpus_allowed_ptr(current, &saved_mask); - printk(KERN_WARNING "get performance failed with error %d\n", - ret); + pr_warn("get performance failed with error %d\n", ret); ret = 0; goto migrate_end; } @@ -177,7 +178,7 @@ processor_set_freq ( ret = processor_set_pstate(value); if (ret) { - printk(KERN_WARNING "Transition failed with error %d\n", ret); + pr_warn("Transition failed with error %d\n", ret); retval = -ENODEV; goto migrate_end; } @@ -291,8 +292,7 @@ acpi_cpufreq_cpu_init ( /* notify BIOS that we exist */ acpi_processor_notify_smm(THIS_MODULE); - printk(KERN_INFO "acpi-cpufreq: CPU%u - ACPI performance management " - "activated.\n", cpu); + pr_info("CPU%u - ACPI performance management activated\n", cpu); for (i = 0; i < data->acpi_data.state_count; i++) pr_debug(" %cP%d: %d MHz, %d mW, %d uS, %d uS, 0x%x 0x%x\n", diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 30fe323c4551..b76a98dd9988 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -10,6 +10,8 @@ * of the License. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/kernel_stat.h> #include <linux/module.h> @@ -39,10 +41,17 @@ #define ATOM_TURBO_RATIOS 0x66c #define ATOM_TURBO_VIDS 0x66d +#ifdef CONFIG_ACPI +#include <acpi/processor.h> +#endif + #define FRAC_BITS 8 #define int_tofp(X) ((int64_t)(X) << FRAC_BITS) #define fp_toint(X) ((X) >> FRAC_BITS) +#define EXT_BITS 6 +#define EXT_FRAC_BITS (EXT_BITS + FRAC_BITS) + static inline int32_t mul_fp(int32_t x, int32_t y) { return ((int64_t)x * (int64_t)y) >> FRAC_BITS; @@ -64,12 +73,22 @@ static inline int ceiling_fp(int32_t x) return ret; } +static inline u64 mul_ext_fp(u64 x, u64 y) +{ + return (x * y) >> EXT_FRAC_BITS; +} + +static inline u64 div_ext_fp(u64 x, u64 y) +{ + return div64_u64(x << EXT_FRAC_BITS, y); +} + /** * struct sample - Store performance sample - * @core_pct_busy: Ratio of APERF/MPERF in percent, which is actual + * @core_avg_perf: Ratio of APERF/MPERF which is the actual average * performance during last sample period * @busy_scaled: Scaled busy value which is used to calculate next - * P state. This can be different than core_pct_busy + * P state. This can be different than core_avg_perf * to account for cpu idle period * @aperf: Difference of actual performance frequency clock count * read from APERF MSR between last and current sample @@ -84,7 +103,7 @@ static inline int ceiling_fp(int32_t x) * data for choosing next P State. */ struct sample { - int32_t core_pct_busy; + int32_t core_avg_perf; int32_t busy_scaled; u64 aperf; u64 mperf; @@ -162,6 +181,7 @@ struct _pid { * struct cpudata - Per CPU instance data storage * @cpu: CPU number for this instance data * @update_util: CPUFreq utility callback information + * @update_util_set: CPUFreq utility callback is set * @pstate: Stores P state limits for this CPU * @vid: Stores VID limits for this CPU * @pid: Stores PID parameters for this CPU @@ -172,6 +192,8 @@ struct _pid { * @prev_cummulative_iowait: IO Wait time difference from last and * current sample * @sample: Storage for storing last Sample data + * @acpi_perf_data: Stores ACPI perf information read from _PSS + * @valid_pss_table: Set to true for valid ACPI _PSS entries found * * This structure stores per CPU instance data for all CPUs. */ @@ -179,6 +201,7 @@ struct cpudata { int cpu; struct update_util_data update_util; + bool update_util_set; struct pstate_data pstate; struct vid_data vid; @@ -190,6 +213,10 @@ struct cpudata { u64 prev_tsc; u64 prev_cummulative_iowait; struct sample sample; +#ifdef CONFIG_ACPI + struct acpi_processor_performance acpi_perf_data; + bool valid_pss_table; +#endif }; static struct cpudata **all_cpu_data; @@ -258,6 +285,9 @@ static struct pstate_adjust_policy pid_params; static struct pstate_funcs pstate_funcs; static int hwp_active; +#ifdef CONFIG_ACPI +static bool acpi_ppc; +#endif /** * struct perf_limits - Store user and policy limits @@ -331,6 +361,124 @@ static struct perf_limits *limits = &performance_limits; static struct perf_limits *limits = &powersave_limits; #endif +#ifdef CONFIG_ACPI + +static bool intel_pstate_get_ppc_enable_status(void) +{ + if (acpi_gbl_FADT.preferred_profile == PM_ENTERPRISE_SERVER || + acpi_gbl_FADT.preferred_profile == PM_PERFORMANCE_SERVER) + return true; + + return acpi_ppc; +} + +/* + * The max target pstate ratio is a 8 bit value in both PLATFORM_INFO MSR and + * in TURBO_RATIO_LIMIT MSR, which pstate driver stores in max_pstate and + * max_turbo_pstate fields. The PERF_CTL MSR contains 16 bit value for P state + * ratio, out of it only high 8 bits are used. For example 0x1700 is setting + * target ratio 0x17. The _PSS control value stores in a format which can be + * directly written to PERF_CTL MSR. But in intel_pstate driver this shift + * occurs during write to PERF_CTL (E.g. for cores core_set_pstate()). + * This function converts the _PSS control value to intel pstate driver format + * for comparison and assignment. + */ +static int convert_to_native_pstate_format(struct cpudata *cpu, int index) +{ + return cpu->acpi_perf_data.states[index].control >> 8; +} + +static void intel_pstate_init_acpi_perf_limits(struct cpufreq_policy *policy) +{ + struct cpudata *cpu; + int turbo_pss_ctl; + int ret; + int i; + + if (hwp_active) + return; + + if (!intel_pstate_get_ppc_enable_status()) + return; + + cpu = all_cpu_data[policy->cpu]; + + ret = acpi_processor_register_performance(&cpu->acpi_perf_data, + policy->cpu); + if (ret) + return; + + /* + * Check if the control value in _PSS is for PERF_CTL MSR, which should + * guarantee that the states returned by it map to the states in our + * list directly. + */ + if (cpu->acpi_perf_data.control_register.space_id != + ACPI_ADR_SPACE_FIXED_HARDWARE) + goto err; + + /* + * If there is only one entry _PSS, simply ignore _PSS and continue as + * usual without taking _PSS into account + */ + if (cpu->acpi_perf_data.state_count < 2) + goto err; + + pr_debug("CPU%u - ACPI _PSS perf data\n", policy->cpu); + for (i = 0; i < cpu->acpi_perf_data.state_count; i++) { + pr_debug(" %cP%d: %u MHz, %u mW, 0x%x\n", + (i == cpu->acpi_perf_data.state ? '*' : ' '), i, + (u32) cpu->acpi_perf_data.states[i].core_frequency, + (u32) cpu->acpi_perf_data.states[i].power, + (u32) cpu->acpi_perf_data.states[i].control); + } + + /* + * The _PSS table doesn't contain whole turbo frequency range. + * This just contains +1 MHZ above the max non turbo frequency, + * with control value corresponding to max turbo ratio. But + * when cpufreq set policy is called, it will call with this + * max frequency, which will cause a reduced performance as + * this driver uses real max turbo frequency as the max + * frequency. So correct this frequency in _PSS table to + * correct max turbo frequency based on the turbo ratio. + * Also need to convert to MHz as _PSS freq is in MHz. + */ + turbo_pss_ctl = convert_to_native_pstate_format(cpu, 0); + if (turbo_pss_ctl > cpu->pstate.max_pstate) + cpu->acpi_perf_data.states[0].core_frequency = + policy->cpuinfo.max_freq / 1000; + cpu->valid_pss_table = true; + pr_info("_PPC limits will be enforced\n"); + + return; + + err: + cpu->valid_pss_table = false; + acpi_processor_unregister_performance(policy->cpu); +} + +static void intel_pstate_exit_perf_limits(struct cpufreq_policy *policy) +{ + struct cpudata *cpu; + + cpu = all_cpu_data[policy->cpu]; + if (!cpu->valid_pss_table) + return; + + acpi_processor_unregister_performance(policy->cpu); +} + +#else +static void intel_pstate_init_acpi_perf_limits(struct cpufreq_policy *policy) +{ +} + +static void intel_pstate_exit_perf_limits(struct cpufreq_policy *policy) +{ +} +#endif + static inline void pid_reset(struct _pid *pid, int setpoint, int busy, int deadband, int integral) { pid->setpoint = int_tofp(setpoint); @@ -341,17 +489,17 @@ static inline void pid_reset(struct _pid *pid, int setpoint, int busy, static inline void pid_p_gain_set(struct _pid *pid, int percent) { - pid->p_gain = div_fp(int_tofp(percent), int_tofp(100)); + pid->p_gain = div_fp(percent, 100); } static inline void pid_i_gain_set(struct _pid *pid, int percent) { - pid->i_gain = div_fp(int_tofp(percent), int_tofp(100)); + pid->i_gain = div_fp(percent, 100); } static inline void pid_d_gain_set(struct _pid *pid, int percent) { - pid->d_gain = div_fp(int_tofp(percent), int_tofp(100)); + pid->d_gain = div_fp(percent, 100); } static signed int pid_calc(struct _pid *pid, int32_t busy) @@ -453,6 +601,14 @@ static void intel_pstate_hwp_set(const struct cpumask *cpumask) } } +static int intel_pstate_hwp_set_policy(struct cpufreq_policy *policy) +{ + if (hwp_active) + intel_pstate_hwp_set(policy->cpus); + + return 0; +} + static void intel_pstate_hwp_set_online_cpus(void) { get_online_cpus(); @@ -529,7 +685,7 @@ static ssize_t show_turbo_pct(struct kobject *kobj, total = cpu->pstate.turbo_pstate - cpu->pstate.min_pstate + 1; no_turbo = cpu->pstate.max_pstate - cpu->pstate.min_pstate + 1; - turbo_fp = div_fp(int_tofp(no_turbo), int_tofp(total)); + turbo_fp = div_fp(no_turbo, total); turbo_pct = 100 - fp_toint(mul_fp(turbo_fp, int_tofp(100))); return sprintf(buf, "%u\n", turbo_pct); } @@ -571,7 +727,7 @@ static ssize_t store_no_turbo(struct kobject *a, struct attribute *b, update_turbo_state(); if (limits->turbo_disabled) { - pr_warn("intel_pstate: Turbo disabled by BIOS or unavailable on processor\n"); + pr_warn("Turbo disabled by BIOS or unavailable on processor\n"); return -EPERM; } @@ -600,8 +756,7 @@ static ssize_t store_max_perf_pct(struct kobject *a, struct attribute *b, limits->max_perf_pct); limits->max_perf_pct = max(limits->min_perf_pct, limits->max_perf_pct); - limits->max_perf = div_fp(int_tofp(limits->max_perf_pct), - int_tofp(100)); + limits->max_perf = div_fp(limits->max_perf_pct, 100); if (hwp_active) intel_pstate_hwp_set_online_cpus(); @@ -625,8 +780,7 @@ static ssize_t store_min_perf_pct(struct kobject *a, struct attribute *b, limits->min_perf_pct); limits->min_perf_pct = min(limits->max_perf_pct, limits->min_perf_pct); - limits->min_perf = div_fp(int_tofp(limits->min_perf_pct), - int_tofp(100)); + limits->min_perf = div_fp(limits->min_perf_pct, 100); if (hwp_active) intel_pstate_hwp_set_online_cpus(); @@ -813,6 +967,11 @@ static int core_get_max_pstate(void) if (err) goto skip_tar; + /* For level 1 and 2, bits[23:16] contain the ratio */ + if (tdp_ctrl) + tdp_ratio >>= 16; + + tdp_ratio &= 0xff; /* ratios are only 8 bits long */ if (tdp_ratio - 1 == tar) { max_pstate = tar; pr_debug("max_pstate=TAC %x\n", max_pstate); @@ -1006,15 +1165,11 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) intel_pstate_set_min_pstate(cpu); } -static inline void intel_pstate_calc_busy(struct cpudata *cpu) +static inline void intel_pstate_calc_avg_perf(struct cpudata *cpu) { struct sample *sample = &cpu->sample; - int64_t core_pct; - - core_pct = int_tofp(sample->aperf) * int_tofp(100); - core_pct = div64_u64(core_pct, int_tofp(sample->mperf)); - sample->core_pct_busy = (int32_t)core_pct; + sample->core_avg_perf = div_ext_fp(sample->aperf, sample->mperf); } static inline bool intel_pstate_sample(struct cpudata *cpu, u64 time) @@ -1057,8 +1212,14 @@ static inline bool intel_pstate_sample(struct cpudata *cpu, u64 time) static inline int32_t get_avg_frequency(struct cpudata *cpu) { - return div64_u64(cpu->pstate.max_pstate_physical * cpu->sample.aperf * - cpu->pstate.scaling, cpu->sample.mperf); + return mul_ext_fp(cpu->sample.core_avg_perf, + cpu->pstate.max_pstate_physical * cpu->pstate.scaling); +} + +static inline int32_t get_avg_pstate(struct cpudata *cpu) +{ + return mul_ext_fp(cpu->pstate.max_pstate_physical, + cpu->sample.core_avg_perf); } static inline int32_t get_target_pstate_use_cpu_load(struct cpudata *cpu) @@ -1093,51 +1254,43 @@ static inline int32_t get_target_pstate_use_cpu_load(struct cpudata *cpu) cpu_load = div64_u64(int_tofp(100) * mperf, sample->tsc); cpu->sample.busy_scaled = cpu_load; - return cpu->pstate.current_pstate - pid_calc(&cpu->pid, cpu_load); + return get_avg_pstate(cpu) - pid_calc(&cpu->pid, cpu_load); } static inline int32_t get_target_pstate_use_performance(struct cpudata *cpu) { - int32_t core_busy, max_pstate, current_pstate, sample_ratio; + int32_t perf_scaled, max_pstate, current_pstate, sample_ratio; u64 duration_ns; - intel_pstate_calc_busy(cpu); - /* - * core_busy is the ratio of actual performance to max - * max_pstate is the max non turbo pstate available - * current_pstate was the pstate that was requested during - * the last sample period. - * - * We normalize core_busy, which was our actual percent - * performance to what we requested during the last sample - * period. The result will be a percentage of busy at a - * specified pstate. + * perf_scaled is the average performance during the last sampling + * period scaled by the ratio of the maximum P-state to the P-state + * requested last time (in percent). That measures the system's + * response to the previous P-state selection. */ - core_busy = cpu->sample.core_pct_busy; - max_pstate = int_tofp(cpu->pstate.max_pstate_physical); - current_pstate = int_tofp(cpu->pstate.current_pstate); - core_busy = mul_fp(core_busy, div_fp(max_pstate, current_pstate)); + max_pstate = cpu->pstate.max_pstate_physical; + current_pstate = cpu->pstate.current_pstate; + perf_scaled = mul_ext_fp(cpu->sample.core_avg_perf, + div_fp(100 * max_pstate, current_pstate)); /* * Since our utilization update callback will not run unless we are * in C0, check if the actual elapsed time is significantly greater (3x) * than our sample interval. If it is, then we were idle for a long - * enough period of time to adjust our busyness. + * enough period of time to adjust our performance metric. */ duration_ns = cpu->sample.time - cpu->last_sample_time; if ((s64)duration_ns > pid_params.sample_rate_ns * 3) { - sample_ratio = div_fp(int_tofp(pid_params.sample_rate_ns), - int_tofp(duration_ns)); - core_busy = mul_fp(core_busy, sample_ratio); + sample_ratio = div_fp(pid_params.sample_rate_ns, duration_ns); + perf_scaled = mul_fp(perf_scaled, sample_ratio); } else { sample_ratio = div_fp(100 * cpu->sample.mperf, cpu->sample.tsc); if (sample_ratio < int_tofp(1)) - core_busy = 0; + perf_scaled = 0; } - cpu->sample.busy_scaled = core_busy; - return cpu->pstate.current_pstate - pid_calc(&cpu->pid, core_busy); + cpu->sample.busy_scaled = perf_scaled; + return cpu->pstate.current_pstate - pid_calc(&cpu->pid, perf_scaled); } static inline void intel_pstate_update_pstate(struct cpudata *cpu, int pstate) @@ -1167,7 +1320,7 @@ static inline void intel_pstate_adjust_busy_pstate(struct cpudata *cpu) intel_pstate_update_pstate(cpu, target_pstate); sample = &cpu->sample; - trace_pstate_sample(fp_toint(sample->core_pct_busy), + trace_pstate_sample(mul_ext_fp(100, sample->core_avg_perf), fp_toint(sample->busy_scaled), from, cpu->pstate.current_pstate, @@ -1186,8 +1339,11 @@ static void intel_pstate_update_util(struct update_util_data *data, u64 time, if ((s64)delta_ns >= pid_params.sample_rate_ns) { bool sample_taken = intel_pstate_sample(cpu, time); - if (sample_taken && !hwp_active) - intel_pstate_adjust_busy_pstate(cpu); + if (sample_taken) { + intel_pstate_calc_avg_perf(cpu); + if (!hwp_active) + intel_pstate_adjust_busy_pstate(cpu); + } } } @@ -1246,23 +1402,16 @@ static int intel_pstate_init_cpu(unsigned int cpunum) intel_pstate_busy_pid_reset(cpu); - cpu->update_util.func = intel_pstate_update_util; - - pr_debug("intel_pstate: controlling: cpu %d\n", cpunum); + pr_debug("controlling: cpu %d\n", cpunum); return 0; } static unsigned int intel_pstate_get(unsigned int cpu_num) { - struct sample *sample; - struct cpudata *cpu; + struct cpudata *cpu = all_cpu_data[cpu_num]; - cpu = all_cpu_data[cpu_num]; - if (!cpu) - return 0; - sample = &cpu->sample; - return get_avg_frequency(cpu); + return cpu ? get_avg_frequency(cpu) : 0; } static void intel_pstate_set_update_util_hook(unsigned int cpu_num) @@ -1271,12 +1420,20 @@ static void intel_pstate_set_update_util_hook(unsigned int cpu_num) /* Prevent intel_pstate_update_util() from using stale data. */ cpu->sample.time = 0; - cpufreq_set_update_util_data(cpu_num, &cpu->update_util); + cpufreq_add_update_util_hook(cpu_num, &cpu->update_util, + intel_pstate_update_util); + cpu->update_util_set = true; } static void intel_pstate_clear_update_util_hook(unsigned int cpu) { - cpufreq_set_update_util_data(cpu, NULL); + struct cpudata *cpu_data = all_cpu_data[cpu]; + + if (!cpu_data->update_util_set) + return; + + cpufreq_remove_update_util_hook(cpu); + cpu_data->update_util_set = false; synchronize_sched(); } @@ -1296,20 +1453,31 @@ static void intel_pstate_set_performance_limits(struct perf_limits *limits) static int intel_pstate_set_policy(struct cpufreq_policy *policy) { + struct cpudata *cpu; + if (!policy->cpuinfo.max_freq) return -ENODEV; intel_pstate_clear_update_util_hook(policy->cpu); + cpu = all_cpu_data[0]; + if (cpu->pstate.max_pstate_physical > cpu->pstate.max_pstate) { + if (policy->max < policy->cpuinfo.max_freq && + policy->max > cpu->pstate.max_pstate * cpu->pstate.scaling) { + pr_debug("policy->max > max non turbo frequency\n"); + policy->max = policy->cpuinfo.max_freq; + } + } + if (policy->policy == CPUFREQ_POLICY_PERFORMANCE) { limits = &performance_limits; if (policy->max >= policy->cpuinfo.max_freq) { - pr_debug("intel_pstate: set performance\n"); + pr_debug("set performance\n"); intel_pstate_set_performance_limits(limits); goto out; } } else { - pr_debug("intel_pstate: set powersave\n"); + pr_debug("set powersave\n"); limits = &powersave_limits; } @@ -1333,16 +1501,13 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) /* Make sure min_perf_pct <= max_perf_pct */ limits->min_perf_pct = min(limits->max_perf_pct, limits->min_perf_pct); - limits->min_perf = div_fp(int_tofp(limits->min_perf_pct), - int_tofp(100)); - limits->max_perf = div_fp(int_tofp(limits->max_perf_pct), - int_tofp(100)); + limits->min_perf = div_fp(limits->min_perf_pct, 100); + limits->max_perf = div_fp(limits->max_perf_pct, 100); out: intel_pstate_set_update_util_hook(policy->cpu); - if (hwp_active) - intel_pstate_hwp_set(policy->cpus); + intel_pstate_hwp_set_policy(policy); return 0; } @@ -1363,7 +1528,7 @@ static void intel_pstate_stop_cpu(struct cpufreq_policy *policy) int cpu_num = policy->cpu; struct cpudata *cpu = all_cpu_data[cpu_num]; - pr_debug("intel_pstate: CPU %d exiting\n", cpu_num); + pr_debug("CPU %d exiting\n", cpu_num); intel_pstate_clear_update_util_hook(cpu_num); @@ -1396,18 +1561,28 @@ static int intel_pstate_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.min_freq = cpu->pstate.min_pstate * cpu->pstate.scaling; policy->cpuinfo.max_freq = cpu->pstate.turbo_pstate * cpu->pstate.scaling; + intel_pstate_init_acpi_perf_limits(policy); policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; cpumask_set_cpu(policy->cpu, policy->cpus); return 0; } +static int intel_pstate_cpu_exit(struct cpufreq_policy *policy) +{ + intel_pstate_exit_perf_limits(policy); + + return 0; +} + static struct cpufreq_driver intel_pstate_driver = { .flags = CPUFREQ_CONST_LOOPS, .verify = intel_pstate_verify_policy, .setpolicy = intel_pstate_set_policy, + .resume = intel_pstate_hwp_set_policy, .get = intel_pstate_get, .init = intel_pstate_cpu_init, + .exit = intel_pstate_cpu_exit, .stop_cpu = intel_pstate_stop_cpu, .name = "intel_pstate", }; @@ -1451,8 +1626,7 @@ static void copy_cpu_funcs(struct pstate_funcs *funcs) } -#if IS_ENABLED(CONFIG_ACPI) -#include <acpi/processor.h> +#ifdef CONFIG_ACPI static bool intel_pstate_no_acpi_pss(void) { @@ -1608,7 +1782,7 @@ hwp_cpu_matched: if (intel_pstate_platform_pwr_mgmt_exists()) return -ENODEV; - pr_info("Intel P-state driver initializing.\n"); + pr_info("Intel P-state driver initializing\n"); all_cpu_data = vzalloc(sizeof(void *) * num_possible_cpus()); if (!all_cpu_data) @@ -1625,7 +1799,7 @@ hwp_cpu_matched: intel_pstate_sysfs_expose_params(); if (hwp_active) - pr_info("intel_pstate: HWP enabled\n"); + pr_info("HWP enabled\n"); return rc; out: @@ -1651,13 +1825,19 @@ static int __init intel_pstate_setup(char *str) if (!strcmp(str, "disable")) no_load = 1; if (!strcmp(str, "no_hwp")) { - pr_info("intel_pstate: HWP disabled\n"); + pr_info("HWP disabled\n"); no_hwp = 1; } if (!strcmp(str, "force")) force_load = 1; if (!strcmp(str, "hwp_only")) hwp_only = 1; + +#ifdef CONFIG_ACPI + if (!strcmp(str, "support_acpi_ppc")) + acpi_ppc = true; +#endif + return 0; } early_param("intel_pstate", intel_pstate_setup); diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c index 0f6b229afcb9..beae5cf5c62c 100644 --- a/drivers/cpufreq/longhaul.c +++ b/drivers/cpufreq/longhaul.c @@ -21,6 +21,8 @@ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -40,8 +42,6 @@ #include "longhaul.h" -#define PFX "longhaul: " - #define TYPE_LONGHAUL_V1 1 #define TYPE_LONGHAUL_V2 2 #define TYPE_POWERSAVER 3 @@ -347,14 +347,13 @@ retry_loop: freqs.new = calc_speed(longhaul_get_cpu_mult()); /* Check if requested frequency is set. */ if (unlikely(freqs.new != speed)) { - printk(KERN_INFO PFX "Failed to set requested frequency!\n"); + pr_info("Failed to set requested frequency!\n"); /* Revision ID = 1 but processor is expecting revision key * equal to 0. Jumpers at the bottom of processor will change * multiplier and FSB, but will not change bits in Longhaul * MSR nor enable voltage scaling. */ if (!revid_errata) { - printk(KERN_INFO PFX "Enabling \"Ignore Revision ID\" " - "option.\n"); + pr_info("Enabling \"Ignore Revision ID\" option\n"); revid_errata = 1; msleep(200); goto retry_loop; @@ -364,11 +363,10 @@ retry_loop: * but it doesn't change frequency. I tried poking various * bits in northbridge registers, but without success. */ if (longhaul_flags & USE_ACPI_C3) { - printk(KERN_INFO PFX "Disabling ACPI C3 support.\n"); + pr_info("Disabling ACPI C3 support\n"); longhaul_flags &= ~USE_ACPI_C3; if (revid_errata) { - printk(KERN_INFO PFX "Disabling \"Ignore " - "Revision ID\" option.\n"); + pr_info("Disabling \"Ignore Revision ID\" option\n"); revid_errata = 0; } msleep(200); @@ -379,7 +377,7 @@ retry_loop: * RevID = 1. RevID errata will make things right. Just * to be 100% sure. */ if (longhaul_version == TYPE_LONGHAUL_V2) { - printk(KERN_INFO PFX "Switching to Longhaul ver. 1\n"); + pr_info("Switching to Longhaul ver. 1\n"); longhaul_version = TYPE_LONGHAUL_V1; msleep(200); goto retry_loop; @@ -387,8 +385,7 @@ retry_loop: } if (!bm_timeout) { - printk(KERN_INFO PFX "Warning: Timeout while waiting for " - "idle PCI bus.\n"); + pr_info("Warning: Timeout while waiting for idle PCI bus\n"); return -EBUSY; } @@ -433,12 +430,12 @@ static int longhaul_get_ranges(void) /* Get current frequency */ mult = longhaul_get_cpu_mult(); if (mult == -1) { - printk(KERN_INFO PFX "Invalid (reserved) multiplier!\n"); + pr_info("Invalid (reserved) multiplier!\n"); return -EINVAL; } fsb = guess_fsb(mult); if (fsb == 0) { - printk(KERN_INFO PFX "Invalid (reserved) FSB!\n"); + pr_info("Invalid (reserved) FSB!\n"); return -EINVAL; } /* Get max multiplier - as we always did. @@ -468,11 +465,11 @@ static int longhaul_get_ranges(void) print_speed(highest_speed/1000)); if (lowest_speed == highest_speed) { - printk(KERN_INFO PFX "highestspeed == lowest, aborting.\n"); + pr_info("highestspeed == lowest, aborting\n"); return -EINVAL; } if (lowest_speed > highest_speed) { - printk(KERN_INFO PFX "nonsense! lowest (%d > %d) !\n", + pr_info("nonsense! lowest (%d > %d) !\n", lowest_speed, highest_speed); return -EINVAL; } @@ -538,16 +535,16 @@ static void longhaul_setup_voltagescaling(void) rdmsrl(MSR_VIA_LONGHAUL, longhaul.val); if (!(longhaul.bits.RevisionID & 1)) { - printk(KERN_INFO PFX "Voltage scaling not supported by CPU.\n"); + pr_info("Voltage scaling not supported by CPU\n"); return; } if (!longhaul.bits.VRMRev) { - printk(KERN_INFO PFX "VRM 8.5\n"); + pr_info("VRM 8.5\n"); vrm_mV_table = &vrm85_mV[0]; mV_vrm_table = &mV_vrm85[0]; } else { - printk(KERN_INFO PFX "Mobile VRM\n"); + pr_info("Mobile VRM\n"); if (cpu_model < CPU_NEHEMIAH) return; vrm_mV_table = &mobilevrm_mV[0]; @@ -558,27 +555,21 @@ static void longhaul_setup_voltagescaling(void) maxvid = vrm_mV_table[longhaul.bits.MaximumVID]; if (minvid.mV == 0 || maxvid.mV == 0 || minvid.mV > maxvid.mV) { - printk(KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. " - "Voltage scaling disabled.\n", - minvid.mV/1000, minvid.mV%1000, - maxvid.mV/1000, maxvid.mV%1000); + pr_info("Bogus values Min:%d.%03d Max:%d.%03d - Voltage scaling disabled\n", + minvid.mV/1000, minvid.mV%1000, + maxvid.mV/1000, maxvid.mV%1000); return; } if (minvid.mV == maxvid.mV) { - printk(KERN_INFO PFX "Claims to support voltage scaling but " - "min & max are both %d.%03d. " - "Voltage scaling disabled\n", - maxvid.mV/1000, maxvid.mV%1000); + pr_info("Claims to support voltage scaling but min & max are both %d.%03d - Voltage scaling disabled\n", + maxvid.mV/1000, maxvid.mV%1000); return; } /* How many voltage steps*/ numvscales = maxvid.pos - minvid.pos + 1; - printk(KERN_INFO PFX - "Max VID=%d.%03d " - "Min VID=%d.%03d, " - "%d possible voltage scales\n", + pr_info("Max VID=%d.%03d Min VID=%d.%03d, %d possible voltage scales\n", maxvid.mV/1000, maxvid.mV%1000, minvid.mV/1000, minvid.mV%1000, numvscales); @@ -617,12 +608,12 @@ static void longhaul_setup_voltagescaling(void) pos = minvid.pos; freq_pos->driver_data |= mV_vrm_table[pos] << 8; vid = vrm_mV_table[mV_vrm_table[pos]]; - printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n", + pr_info("f: %d kHz, index: %d, vid: %d mV\n", speed, (int)(freq_pos - longhaul_table), vid.mV); } can_scale_voltage = 1; - printk(KERN_INFO PFX "Voltage scaling enabled.\n"); + pr_info("Voltage scaling enabled\n"); } @@ -720,8 +711,7 @@ static int enable_arbiter_disable(void) pci_write_config_byte(dev, reg, pci_cmd); pci_read_config_byte(dev, reg, &pci_cmd); if (!(pci_cmd & 1<<7)) { - printk(KERN_ERR PFX - "Can't enable access to port 0x22.\n"); + pr_err("Can't enable access to port 0x22\n"); status = 0; } } @@ -758,8 +748,7 @@ static int longhaul_setup_southbridge(void) if (pci_cmd & 1 << 7) { pci_read_config_dword(dev, 0x88, &acpi_regs_addr); acpi_regs_addr &= 0xff00; - printk(KERN_INFO PFX "ACPI I/O at 0x%x\n", - acpi_regs_addr); + pr_info("ACPI I/O at 0x%x\n", acpi_regs_addr); } pci_dev_put(dev); @@ -853,14 +842,14 @@ static int longhaul_cpu_init(struct cpufreq_policy *policy) longhaul_version = TYPE_LONGHAUL_V1; } - printk(KERN_INFO PFX "VIA %s CPU detected. ", cpuname); + pr_info("VIA %s CPU detected. ", cpuname); switch (longhaul_version) { case TYPE_LONGHAUL_V1: case TYPE_LONGHAUL_V2: - printk(KERN_CONT "Longhaul v%d supported.\n", longhaul_version); + pr_cont("Longhaul v%d supported\n", longhaul_version); break; case TYPE_POWERSAVER: - printk(KERN_CONT "Powersaver supported.\n"); + pr_cont("Powersaver supported\n"); break; }; @@ -889,15 +878,14 @@ static int longhaul_cpu_init(struct cpufreq_policy *policy) if (!(longhaul_flags & USE_ACPI_C3 || longhaul_flags & USE_NORTHBRIDGE) && ((pr == NULL) || !(pr->flags.bm_control))) { - printk(KERN_ERR PFX - "No ACPI support. Unsupported northbridge.\n"); + pr_err("No ACPI support: Unsupported northbridge\n"); return -ENODEV; } if (longhaul_flags & USE_NORTHBRIDGE) - printk(KERN_INFO PFX "Using northbridge support.\n"); + pr_info("Using northbridge support\n"); if (longhaul_flags & USE_ACPI_C3) - printk(KERN_INFO PFX "Using ACPI support.\n"); + pr_info("Using ACPI support\n"); ret = longhaul_get_ranges(); if (ret != 0) @@ -934,20 +922,18 @@ static int __init longhaul_init(void) return -ENODEV; if (!enable) { - printk(KERN_ERR PFX "Option \"enable\" not set. Aborting.\n"); + pr_err("Option \"enable\" not set - Aborting\n"); return -ENODEV; } #ifdef CONFIG_SMP if (num_online_cpus() > 1) { - printk(KERN_ERR PFX "More than 1 CPU detected, " - "longhaul disabled.\n"); + pr_err("More than 1 CPU detected, longhaul disabled\n"); return -ENODEV; } #endif #ifdef CONFIG_X86_IO_APIC if (cpu_has_apic) { - printk(KERN_ERR PFX "APIC detected. Longhaul is currently " - "broken in this configuration.\n"); + pr_err("APIC detected. Longhaul is currently broken in this configuration.\n"); return -ENODEV; } #endif @@ -955,7 +941,7 @@ static int __init longhaul_init(void) case 6 ... 9: return cpufreq_register_driver(&longhaul_driver); case 10: - printk(KERN_ERR PFX "Use acpi-cpufreq driver for VIA C7\n"); + pr_err("Use acpi-cpufreq driver for VIA C7\n"); default: ; } diff --git a/drivers/cpufreq/loongson2_cpufreq.c b/drivers/cpufreq/loongson2_cpufreq.c index cd593c1f66dc..6bbdac1065ff 100644 --- a/drivers/cpufreq/loongson2_cpufreq.c +++ b/drivers/cpufreq/loongson2_cpufreq.c @@ -10,6 +10,9 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/cpufreq.h> #include <linux/module.h> #include <linux/err.h> @@ -76,7 +79,7 @@ static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy) cpuclk = clk_get(NULL, "cpu_clk"); if (IS_ERR(cpuclk)) { - printk(KERN_ERR "cpufreq: couldn't get CPU clk\n"); + pr_err("couldn't get CPU clk\n"); return PTR_ERR(cpuclk); } @@ -163,7 +166,7 @@ static int __init cpufreq_init(void) if (ret) return ret; - pr_info("cpufreq: Loongson-2F CPU frequency driver.\n"); + pr_info("Loongson-2F CPU frequency driver\n"); cpufreq_register_notifier(&loongson2_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); diff --git a/drivers/cpufreq/maple-cpufreq.c b/drivers/cpufreq/maple-cpufreq.c index cc3408fc073f..d9df89392b84 100644 --- a/drivers/cpufreq/maple-cpufreq.c +++ b/drivers/cpufreq/maple-cpufreq.c @@ -13,6 +13,8 @@ #undef DEBUG +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/types.h> #include <linux/errno.h> @@ -174,7 +176,7 @@ static int __init maple_cpufreq_init(void) /* Get first CPU node */ cpunode = of_cpu_device_node_get(0); if (cpunode == NULL) { - printk(KERN_ERR "cpufreq: Can't find any CPU 0 node\n"); + pr_err("Can't find any CPU 0 node\n"); goto bail_noprops; } @@ -182,8 +184,7 @@ static int __init maple_cpufreq_init(void) /* we actually don't care on which CPU to access PVR */ pvr_hi = PVR_VER(mfspr(SPRN_PVR)); if (pvr_hi != 0x3c && pvr_hi != 0x44) { - printk(KERN_ERR "cpufreq: Unsupported CPU version (%x)\n", - pvr_hi); + pr_err("Unsupported CPU version (%x)\n", pvr_hi); goto bail_noprops; } @@ -222,8 +223,8 @@ static int __init maple_cpufreq_init(void) maple_pmode_cur = -1; maple_scom_switch_freq(maple_scom_query_freq()); - printk(KERN_INFO "Registering Maple CPU frequency driver\n"); - printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n", + pr_info("Registering Maple CPU frequency driver\n"); + pr_info("Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n", maple_cpu_freqs[1].frequency/1000, maple_cpu_freqs[0].frequency/1000, maple_cpu_freqs[maple_pmode_cur].frequency/1000); diff --git a/drivers/cpufreq/mt8173-cpufreq.c b/drivers/cpufreq/mt8173-cpufreq.c index 2058e6d292ce..6f602c7a71bd 100644 --- a/drivers/cpufreq/mt8173-cpufreq.c +++ b/drivers/cpufreq/mt8173-cpufreq.c @@ -59,11 +59,8 @@ static LIST_HEAD(dvfs_info_list); static struct mtk_cpu_dvfs_info *mtk_cpu_dvfs_info_lookup(int cpu) { struct mtk_cpu_dvfs_info *info; - struct list_head *list; - - list_for_each(list, &dvfs_info_list) { - info = list_entry(list, struct mtk_cpu_dvfs_info, list_head); + list_for_each_entry(info, &dvfs_info_list, list_head) { if (cpumask_test_cpu(cpu, &info->cpus)) return info; } @@ -524,8 +521,7 @@ static struct cpufreq_driver mt8173_cpufreq_driver = { static int mt8173_cpufreq_probe(struct platform_device *pdev) { - struct mtk_cpu_dvfs_info *info; - struct list_head *list, *tmp; + struct mtk_cpu_dvfs_info *info, *tmp; int cpu, ret; for_each_possible_cpu(cpu) { @@ -559,11 +555,9 @@ static int mt8173_cpufreq_probe(struct platform_device *pdev) return 0; release_dvfs_info_list: - list_for_each_safe(list, tmp, &dvfs_info_list) { - info = list_entry(list, struct mtk_cpu_dvfs_info, list_head); - + list_for_each_entry_safe(info, tmp, &dvfs_info_list, list_head) { mtk_cpu_dvfs_info_release(info); - list_del(list); + list_del(&info->list_head); } return ret; diff --git a/drivers/cpufreq/mvebu-cpufreq.c b/drivers/cpufreq/mvebu-cpufreq.c new file mode 100644 index 000000000000..e920889b9ac2 --- /dev/null +++ b/drivers/cpufreq/mvebu-cpufreq.c @@ -0,0 +1,107 @@ +/* + * CPUFreq support for Armada 370/XP platforms. + * + * Copyright (C) 2012-2016 Marvell + * + * Yehuda Yitschak <yehuday@marvell.com> + * Gregory Clement <gregory.clement@free-electrons.com> + * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#define pr_fmt(fmt) "mvebu-pmsu: " fmt + +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/pm_opp.h> +#include <linux/resource.h> + +static int __init armada_xp_pmsu_cpufreq_init(void) +{ + struct device_node *np; + struct resource res; + int ret, cpu; + + if (!of_machine_is_compatible("marvell,armadaxp")) + return 0; + + /* + * In order to have proper cpufreq handling, we need to ensure + * that the Device Tree description of the CPU clock includes + * the definition of the PMU DFS registers. If not, we do not + * register the clock notifier and the cpufreq driver. This + * piece of code is only for compatibility with old Device + * Trees. + */ + np = of_find_compatible_node(NULL, NULL, "marvell,armada-xp-cpu-clock"); + if (!np) + return 0; + + ret = of_address_to_resource(np, 1, &res); + if (ret) { + pr_warn(FW_WARN "not enabling cpufreq, deprecated armada-xp-cpu-clock binding\n"); + of_node_put(np); + return 0; + } + + of_node_put(np); + + /* + * For each CPU, this loop registers the operating points + * supported (which are the nominal CPU frequency and half of + * it), and registers the clock notifier that will take care + * of doing the PMSU part of a frequency transition. + */ + for_each_possible_cpu(cpu) { + struct device *cpu_dev; + struct clk *clk; + int ret; + + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("Cannot get CPU %d\n", cpu); + continue; + } + + clk = clk_get(cpu_dev, 0); + if (IS_ERR(clk)) { + pr_err("Cannot get clock for CPU %d\n", cpu); + return PTR_ERR(clk); + } + + /* + * In case of a failure of dev_pm_opp_add(), we don't + * bother with cleaning up the registered OPP (there's + * no function to do so), and simply cancel the + * registration of the cpufreq device. + */ + ret = dev_pm_opp_add(cpu_dev, clk_get_rate(clk), 0); + if (ret) { + clk_put(clk); + return ret; + } + + ret = dev_pm_opp_add(cpu_dev, clk_get_rate(clk) / 2, 0); + if (ret) { + clk_put(clk); + return ret; + } + + ret = dev_pm_opp_set_sharing_cpus(cpu_dev, + cpumask_of(cpu_dev->id)); + if (ret) + dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", + __func__, ret); + } + + platform_device_register_simple("cpufreq-dt", -1, NULL, 0); + return 0; +} +device_initcall(armada_xp_pmsu_cpufreq_init); diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index e3866e0d5bf8..cead9bec4843 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -13,6 +13,9 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -163,13 +166,13 @@ static int omap_cpufreq_probe(struct platform_device *pdev) { mpu_dev = get_cpu_device(0); if (!mpu_dev) { - pr_warning("%s: unable to get the mpu device\n", __func__); + pr_warn("%s: unable to get the MPU device\n", __func__); return -EINVAL; } mpu_reg = regulator_get(mpu_dev, "vcc"); if (IS_ERR(mpu_reg)) { - pr_warning("%s: unable to get MPU regulator\n", __func__); + pr_warn("%s: unable to get MPU regulator\n", __func__); mpu_reg = NULL; } else { /* diff --git a/drivers/cpufreq/p4-clockmod.c b/drivers/cpufreq/p4-clockmod.c index 5dd95dab580d..fd77812313f3 100644 --- a/drivers/cpufreq/p4-clockmod.c +++ b/drivers/cpufreq/p4-clockmod.c @@ -20,6 +20,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -35,8 +37,6 @@ #include "speedstep-lib.h" -#define PFX "p4-clockmod: " - /* * Duty Cycle (3bits), note DC_DISABLE is not specified in * intel docs i just use it to mean disable @@ -124,11 +124,7 @@ static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c) { if (c->x86 == 0x06) { if (cpu_has(c, X86_FEATURE_EST)) - printk_once(KERN_WARNING PFX "Warning: EST-capable " - "CPU detected. The acpi-cpufreq module offers " - "voltage scaling in addition to frequency " - "scaling. You should use that instead of " - "p4-clockmod, if possible.\n"); + pr_warn_once("Warning: EST-capable CPU detected. The acpi-cpufreq module offers voltage scaling in addition to frequency scaling. You should use that instead of p4-clockmod, if possible.\n"); switch (c->x86_model) { case 0x0E: /* Core */ case 0x0F: /* Core Duo */ @@ -152,11 +148,7 @@ static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c) p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS; if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4M) { - printk(KERN_WARNING PFX "Warning: Pentium 4-M detected. " - "The speedstep-ich or acpi cpufreq modules offer " - "voltage scaling in addition of frequency scaling. " - "You should use either one instead of p4-clockmod, " - "if possible.\n"); + pr_warn("Warning: Pentium 4-M detected. The speedstep-ich or acpi cpufreq modules offer voltage scaling in addition of frequency scaling. You should use either one instead of p4-clockmod, if possible.\n"); return speedstep_get_frequency(SPEEDSTEP_CPU_P4M); } @@ -265,8 +257,7 @@ static int __init cpufreq_p4_init(void) ret = cpufreq_register_driver(&p4clockmod_driver); if (!ret) - printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock " - "Modulation available\n"); + pr_info("P4/Xeon(TM) CPU On-Demand Clock Modulation available\n"); return ret; } diff --git a/drivers/cpufreq/pmac32-cpufreq.c b/drivers/cpufreq/pmac32-cpufreq.c index 1f49d97a70ea..b7b576e53e92 100644 --- a/drivers/cpufreq/pmac32-cpufreq.c +++ b/drivers/cpufreq/pmac32-cpufreq.c @@ -13,6 +13,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/types.h> #include <linux/errno.h> @@ -481,13 +483,13 @@ static int pmac_cpufreq_init_MacRISC3(struct device_node *cpunode) freqs = of_get_property(cpunode, "bus-frequencies", &lenp); lenp /= sizeof(u32); if (freqs == NULL || lenp != 2) { - printk(KERN_ERR "cpufreq: bus-frequencies incorrect or missing\n"); + pr_err("bus-frequencies incorrect or missing\n"); return 1; } ratio = of_get_property(cpunode, "processor-to-bus-ratio*2", NULL); if (ratio == NULL) { - printk(KERN_ERR "cpufreq: processor-to-bus-ratio*2 missing\n"); + pr_err("processor-to-bus-ratio*2 missing\n"); return 1; } @@ -550,7 +552,7 @@ static int pmac_cpufreq_init_7447A(struct device_node *cpunode) if (volt_gpio_np) voltage_gpio = read_gpio(volt_gpio_np); if (!voltage_gpio){ - printk(KERN_ERR "cpufreq: missing cpu-vcore-select gpio\n"); + pr_err("missing cpu-vcore-select gpio\n"); return 1; } @@ -675,9 +677,9 @@ out: pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_freq; ppc_proc_freq = cur_freq * 1000ul; - printk(KERN_INFO "Registering PowerMac CPU frequency driver\n"); - printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n", - low_freq/1000, hi_freq/1000, cur_freq/1000); + pr_info("Registering PowerMac CPU frequency driver\n"); + pr_info("Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n", + low_freq/1000, hi_freq/1000, cur_freq/1000); return cpufreq_register_driver(&pmac_cpufreq_driver); } diff --git a/drivers/cpufreq/pmac64-cpufreq.c b/drivers/cpufreq/pmac64-cpufreq.c index 4ff86878727f..267e0894c62d 100644 --- a/drivers/cpufreq/pmac64-cpufreq.c +++ b/drivers/cpufreq/pmac64-cpufreq.c @@ -12,6 +12,8 @@ #undef DEBUG +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/types.h> #include <linux/errno.h> @@ -138,7 +140,7 @@ static void g5_vdnap_switch_volt(int speed_mode) usleep_range(1000, 1000); } if (done == 0) - printk(KERN_WARNING "cpufreq: Timeout in clock slewing !\n"); + pr_warn("Timeout in clock slewing !\n"); } @@ -266,7 +268,7 @@ static int g5_pfunc_switch_freq(int speed_mode) rc = pmf_call_one(pfunc_cpu_setfreq_low, NULL); if (rc) - printk(KERN_WARNING "cpufreq: pfunc switch error %d\n", rc); + pr_warn("pfunc switch error %d\n", rc); /* It's an irq GPIO so we should be able to just block here, * I'll do that later after I've properly tested the IRQ code for @@ -282,7 +284,7 @@ static int g5_pfunc_switch_freq(int speed_mode) usleep_range(500, 500); } if (done == 0) - printk(KERN_WARNING "cpufreq: Timeout in clock slewing !\n"); + pr_warn("Timeout in clock slewing !\n"); /* If frequency is going down, last ramp the voltage */ if (speed_mode > g5_pmode_cur) @@ -368,7 +370,7 @@ static int __init g5_neo2_cpufreq_init(struct device_node *cpunode) } pvr_hi = (*valp) >> 16; if (pvr_hi != 0x3c && pvr_hi != 0x44) { - printk(KERN_ERR "cpufreq: Unsupported CPU version\n"); + pr_err("Unsupported CPU version\n"); goto bail_noprops; } @@ -403,8 +405,7 @@ static int __init g5_neo2_cpufreq_init(struct device_node *cpunode) root = of_find_node_by_path("/"); if (root == NULL) { - printk(KERN_ERR "cpufreq: Can't find root of " - "device tree\n"); + pr_err("Can't find root of device tree\n"); goto bail_noprops; } pfunc_set_vdnap0 = pmf_find_function(root, "set-vdnap0"); @@ -412,8 +413,7 @@ static int __init g5_neo2_cpufreq_init(struct device_node *cpunode) pmf_find_function(root, "slewing-done"); if (pfunc_set_vdnap0 == NULL || pfunc_vdnap0_complete == NULL) { - printk(KERN_ERR "cpufreq: Can't find required " - "platform function\n"); + pr_err("Can't find required platform function\n"); goto bail_noprops; } @@ -453,10 +453,10 @@ static int __init g5_neo2_cpufreq_init(struct device_node *cpunode) g5_pmode_cur = -1; g5_switch_freq(g5_query_freq()); - printk(KERN_INFO "Registering G5 CPU frequency driver\n"); - printk(KERN_INFO "Frequency method: %s, Voltage method: %s\n", - freq_method, volt_method); - printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n", + pr_info("Registering G5 CPU frequency driver\n"); + pr_info("Frequency method: %s, Voltage method: %s\n", + freq_method, volt_method); + pr_info("Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n", g5_cpu_freqs[1].frequency/1000, g5_cpu_freqs[0].frequency/1000, g5_cpu_freqs[g5_pmode_cur].frequency/1000); @@ -493,7 +493,7 @@ static int __init g5_pm72_cpufreq_init(struct device_node *cpunode) if (cpuid != NULL) eeprom = of_get_property(cpuid, "cpuid", NULL); if (eeprom == NULL) { - printk(KERN_ERR "cpufreq: Can't find cpuid EEPROM !\n"); + pr_err("Can't find cpuid EEPROM !\n"); rc = -ENODEV; goto bail; } @@ -511,7 +511,7 @@ static int __init g5_pm72_cpufreq_init(struct device_node *cpunode) break; } if (hwclock == NULL) { - printk(KERN_ERR "cpufreq: Can't find i2c clock chip !\n"); + pr_err("Can't find i2c clock chip !\n"); rc = -ENODEV; goto bail; } @@ -539,7 +539,7 @@ static int __init g5_pm72_cpufreq_init(struct device_node *cpunode) /* Check we have minimum requirements */ if (pfunc_cpu_getfreq == NULL || pfunc_cpu_setfreq_high == NULL || pfunc_cpu_setfreq_low == NULL || pfunc_slewing_done == NULL) { - printk(KERN_ERR "cpufreq: Can't find platform functions !\n"); + pr_err("Can't find platform functions !\n"); rc = -ENODEV; goto bail; } @@ -567,7 +567,7 @@ static int __init g5_pm72_cpufreq_init(struct device_node *cpunode) /* Get max frequency from device-tree */ valp = of_get_property(cpunode, "clock-frequency", NULL); if (!valp) { - printk(KERN_ERR "cpufreq: Can't find CPU frequency !\n"); + pr_err("Can't find CPU frequency !\n"); rc = -ENODEV; goto bail; } @@ -583,8 +583,7 @@ static int __init g5_pm72_cpufreq_init(struct device_node *cpunode) /* Check for machines with no useful settings */ if (il == ih) { - printk(KERN_WARNING "cpufreq: No low frequency mode available" - " on this model !\n"); + pr_warn("No low frequency mode available on this model !\n"); rc = -ENODEV; goto bail; } @@ -595,7 +594,7 @@ static int __init g5_pm72_cpufreq_init(struct device_node *cpunode) /* Sanity check */ if (min_freq >= max_freq || min_freq < 1000) { - printk(KERN_ERR "cpufreq: Can't calculate low frequency !\n"); + pr_err("Can't calculate low frequency !\n"); rc = -ENXIO; goto bail; } @@ -619,10 +618,10 @@ static int __init g5_pm72_cpufreq_init(struct device_node *cpunode) g5_pmode_cur = -1; g5_switch_freq(g5_query_freq()); - printk(KERN_INFO "Registering G5 CPU frequency driver\n"); - printk(KERN_INFO "Frequency method: i2c/pfunc, " - "Voltage method: %s\n", has_volt ? "i2c/pfunc" : "none"); - printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n", + pr_info("Registering G5 CPU frequency driver\n"); + pr_info("Frequency method: i2c/pfunc, Voltage method: %s\n", + has_volt ? "i2c/pfunc" : "none"); + pr_info("Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n", g5_cpu_freqs[1].frequency/1000, g5_cpu_freqs[0].frequency/1000, g5_cpu_freqs[g5_pmode_cur].frequency/1000); @@ -654,7 +653,7 @@ static int __init g5_cpufreq_init(void) /* Get first CPU node */ cpunode = of_cpu_device_node_get(0); if (cpunode == NULL) { - pr_err("cpufreq: Can't find any CPU node\n"); + pr_err("Can't find any CPU node\n"); return -ENODEV; } diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c index e6f24b281e3e..dedd2568e852 100644 --- a/drivers/cpufreq/powernow-k6.c +++ b/drivers/cpufreq/powernow-k6.c @@ -8,6 +8,8 @@ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -22,7 +24,6 @@ #define POWERNOW_IOPORT 0xfff0 /* it doesn't matter where, as long as it is unused */ -#define PFX "powernow-k6: " static unsigned int busfreq; /* FSB, in 10 kHz */ static unsigned int max_multiplier; @@ -141,7 +142,7 @@ static int powernow_k6_target(struct cpufreq_policy *policy, { if (clock_ratio[best_i].driver_data > max_multiplier) { - printk(KERN_ERR PFX "invalid target frequency\n"); + pr_err("invalid target frequency\n"); return -EINVAL; } @@ -175,13 +176,14 @@ static int powernow_k6_cpu_init(struct cpufreq_policy *policy) max_multiplier = param_max_multiplier; goto have_max_multiplier; } - printk(KERN_ERR "powernow-k6: invalid max_multiplier parameter, valid parameters 20, 30, 35, 40, 45, 50, 55, 60\n"); + pr_err("invalid max_multiplier parameter, valid parameters 20, 30, 35, 40, 45, 50, 55, 60\n"); return -EINVAL; } if (!max_multiplier) { - printk(KERN_WARNING "powernow-k6: unknown frequency %u, cannot determine current multiplier\n", khz); - printk(KERN_WARNING "powernow-k6: use module parameters max_multiplier and bus_frequency\n"); + pr_warn("unknown frequency %u, cannot determine current multiplier\n", + khz); + pr_warn("use module parameters max_multiplier and bus_frequency\n"); return -EOPNOTSUPP; } @@ -193,7 +195,7 @@ have_max_multiplier: busfreq = param_busfreq / 10; goto have_busfreq; } - printk(KERN_ERR "powernow-k6: invalid bus_frequency parameter, allowed range 50000 - 150000 kHz\n"); + pr_err("invalid bus_frequency parameter, allowed range 50000 - 150000 kHz\n"); return -EINVAL; } @@ -275,7 +277,7 @@ static int __init powernow_k6_init(void) return -ENODEV; if (!request_region(POWERNOW_IOPORT, 16, "PowerNow!")) { - printk(KERN_INFO PFX "PowerNow IOPORT region already used.\n"); + pr_info("PowerNow IOPORT region already used\n"); return -EIO; } diff --git a/drivers/cpufreq/powernow-k7.c b/drivers/cpufreq/powernow-k7.c index c1ae1999770a..9f013ed42977 100644 --- a/drivers/cpufreq/powernow-k7.c +++ b/drivers/cpufreq/powernow-k7.c @@ -13,6 +13,8 @@ * - We disable half multipliers if ACPI is used on A0 stepping CPUs. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -35,9 +37,6 @@ #include "powernow-k7.h" -#define PFX "powernow: " - - struct psb_s { u8 signature[10]; u8 tableversion; @@ -127,14 +126,13 @@ static int check_powernow(void) maxei = cpuid_eax(0x80000000); if (maxei < 0x80000007) { /* Any powernow info ? */ #ifdef MODULE - printk(KERN_INFO PFX "No powernow capabilities detected\n"); + pr_info("No powernow capabilities detected\n"); #endif return 0; } if ((c->x86_model == 6) && (c->x86_mask == 0)) { - printk(KERN_INFO PFX "K7 660[A0] core detected, " - "enabling errata workarounds\n"); + pr_info("K7 660[A0] core detected, enabling errata workarounds\n"); have_a0 = 1; } @@ -144,22 +142,22 @@ static int check_powernow(void) if (!(edx & (1 << 1 | 1 << 2))) return 0; - printk(KERN_INFO PFX "PowerNOW! Technology present. Can scale: "); + pr_info("PowerNOW! Technology present. Can scale: "); if (edx & 1 << 1) { - printk("frequency"); + pr_cont("frequency"); can_scale_bus = 1; } if ((edx & (1 << 1 | 1 << 2)) == 0x6) - printk(" and "); + pr_cont(" and "); if (edx & 1 << 2) { - printk("voltage"); + pr_cont("voltage"); can_scale_vid = 1; } - printk(".\n"); + pr_cont("\n"); return 1; } @@ -427,16 +425,14 @@ err1: err05: kfree(acpi_processor_perf); err0: - printk(KERN_WARNING PFX "ACPI perflib can not be used on " - "this platform\n"); + pr_warn("ACPI perflib can not be used on this platform\n"); acpi_processor_perf = NULL; return retval; } #else static int powernow_acpi_init(void) { - printk(KERN_INFO PFX "no support for ACPI processor found." - " Please recompile your kernel with ACPI processor\n"); + pr_info("no support for ACPI processor found - please recompile your kernel with ACPI processor\n"); return -EINVAL; } #endif @@ -468,8 +464,7 @@ static int powernow_decode_bios(int maxfid, int startvid) psb = (struct psb_s *) p; pr_debug("Table version: 0x%x\n", psb->tableversion); if (psb->tableversion != 0x12) { - printk(KERN_INFO PFX "Sorry, only v1.2 tables" - " supported right now\n"); + pr_info("Sorry, only v1.2 tables supported right now\n"); return -ENODEV; } @@ -481,10 +476,8 @@ static int powernow_decode_bios(int maxfid, int startvid) latency = psb->settlingtime; if (latency < 100) { - printk(KERN_INFO PFX "BIOS set settling time " - "to %d microseconds. " - "Should be at least 100. " - "Correcting.\n", latency); + pr_info("BIOS set settling time to %d microseconds. Should be at least 100. Correcting.\n", + latency); latency = 100; } pr_debug("Settling Time: %d microseconds.\n", @@ -516,10 +509,9 @@ static int powernow_decode_bios(int maxfid, int startvid) p += 2; } } - printk(KERN_INFO PFX "No PST tables match this cpuid " - "(0x%x)\n", etuple); - printk(KERN_INFO PFX "This is indicative of a broken " - "BIOS.\n"); + pr_info("No PST tables match this cpuid (0x%x)\n", + etuple); + pr_info("This is indicative of a broken BIOS\n"); return -EINVAL; } @@ -552,7 +544,7 @@ static int fixup_sgtc(void) sgtc = 100 * m * latency; sgtc = sgtc / 3; if (sgtc > 0xfffff) { - printk(KERN_WARNING PFX "SGTC too large %d\n", sgtc); + pr_warn("SGTC too large %d\n", sgtc); sgtc = 0xfffff; } return sgtc; @@ -574,14 +566,10 @@ static unsigned int powernow_get(unsigned int cpu) static int acer_cpufreq_pst(const struct dmi_system_id *d) { - printk(KERN_WARNING PFX - "%s laptop with broken PST tables in BIOS detected.\n", + pr_warn("%s laptop with broken PST tables in BIOS detected\n", d->ident); - printk(KERN_WARNING PFX - "You need to downgrade to 3A21 (09/09/2002), or try a newer " - "BIOS than 3A71 (01/20/2003)\n"); - printk(KERN_WARNING PFX - "cpufreq scaling has been disabled as a result of this.\n"); + pr_warn("You need to downgrade to 3A21 (09/09/2002), or try a newer BIOS than 3A71 (01/20/2003)\n"); + pr_warn("cpufreq scaling has been disabled as a result of this\n"); return 0; } @@ -616,40 +604,38 @@ static int powernow_cpu_init(struct cpufreq_policy *policy) fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID]; if (!fsb) { - printk(KERN_WARNING PFX "can not determine bus frequency\n"); + pr_warn("can not determine bus frequency\n"); return -EINVAL; } pr_debug("FSB: %3dMHz\n", fsb/1000); if (dmi_check_system(powernow_dmi_table) || acpi_force) { - printk(KERN_INFO PFX "PSB/PST known to be broken. " - "Trying ACPI instead\n"); + pr_info("PSB/PST known to be broken - trying ACPI instead\n"); result = powernow_acpi_init(); } else { result = powernow_decode_bios(fidvidstatus.bits.MFID, fidvidstatus.bits.SVID); if (result) { - printk(KERN_INFO PFX "Trying ACPI perflib\n"); + pr_info("Trying ACPI perflib\n"); maximum_speed = 0; minimum_speed = -1; latency = 0; result = powernow_acpi_init(); if (result) { - printk(KERN_INFO PFX - "ACPI and legacy methods failed\n"); + pr_info("ACPI and legacy methods failed\n"); } } else { /* SGTC use the bus clock as timer */ latency = fixup_sgtc(); - printk(KERN_INFO PFX "SGTC: %d\n", latency); + pr_info("SGTC: %d\n", latency); } } if (result) return result; - printk(KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n", - minimum_speed/1000, maximum_speed/1000); + pr_info("Minimum speed %d MHz - Maximum speed %d MHz\n", + minimum_speed/1000, maximum_speed/1000); policy->cpuinfo.transition_latency = cpufreq_scale(2000000UL, fsb, latency); diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c index 39ac78c94be0..54c45368e3f1 100644 --- a/drivers/cpufreq/powernv-cpufreq.c +++ b/drivers/cpufreq/powernv-cpufreq.c @@ -36,12 +36,56 @@ #include <asm/reg.h> #include <asm/smp.h> /* Required for cpu_sibling_mask() in UP configs */ #include <asm/opal.h> +#include <linux/timer.h> #define POWERNV_MAX_PSTATES 256 #define PMSR_PSAFE_ENABLE (1UL << 30) #define PMSR_SPR_EM_DISABLE (1UL << 31) #define PMSR_MAX(x) ((x >> 32) & 0xFF) +#define MAX_RAMP_DOWN_TIME 5120 +/* + * On an idle system we want the global pstate to ramp-down from max value to + * min over a span of ~5 secs. Also we want it to initially ramp-down slowly and + * then ramp-down rapidly later on. + * + * This gives a percentage rampdown for time elapsed in milliseconds. + * ramp_down_percentage = ((ms * ms) >> 18) + * ~= 3.8 * (sec * sec) + * + * At 0 ms ramp_down_percent = 0 + * At 5120 ms ramp_down_percent = 100 + */ +#define ramp_down_percent(time) ((time * time) >> 18) + +/* Interval after which the timer is queued to bring down global pstate */ +#define GPSTATE_TIMER_INTERVAL 2000 + +/** + * struct global_pstate_info - Per policy data structure to maintain history of + * global pstates + * @highest_lpstate: The local pstate from which we are ramping down + * @elapsed_time: Time in ms spent in ramping down from + * highest_lpstate + * @last_sampled_time: Time from boot in ms when global pstates were + * last set + * @last_lpstate,last_gpstate: Last set values for local and global pstates + * @timer: Is used for ramping down if cpu goes idle for + * a long time with global pstate held high + * @gpstate_lock: A spinlock to maintain synchronization between + * routines called by the timer handler and + * governer's target_index calls + */ +struct global_pstate_info { + int highest_lpstate; + unsigned int elapsed_time; + unsigned int last_sampled_time; + int last_lpstate; + int last_gpstate; + spinlock_t gpstate_lock; + struct timer_list timer; +}; + static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1]; static bool rebooting, throttled, occ_reset; @@ -94,6 +138,17 @@ static struct powernv_pstate_info { int nr_pstates; } powernv_pstate_info; +static inline void reset_gpstates(struct cpufreq_policy *policy) +{ + struct global_pstate_info *gpstates = policy->driver_data; + + gpstates->highest_lpstate = 0; + gpstates->elapsed_time = 0; + gpstates->last_sampled_time = 0; + gpstates->last_lpstate = 0; + gpstates->last_gpstate = 0; +} + /* * Initialize the freq table based on data obtained * from the firmware passed via device-tree @@ -285,6 +340,7 @@ static inline void set_pmspr(unsigned long sprn, unsigned long val) struct powernv_smp_call_data { unsigned int freq; int pstate_id; + int gpstate_id; }; /* @@ -343,19 +399,21 @@ static unsigned int powernv_cpufreq_get(unsigned int cpu) * (struct powernv_smp_call_data *) and the pstate_id which needs to be set * on this CPU should be present in freq_data->pstate_id. */ -static void set_pstate(void *freq_data) +static void set_pstate(void *data) { unsigned long val; - unsigned long pstate_ul = - ((struct powernv_smp_call_data *) freq_data)->pstate_id; + struct powernv_smp_call_data *freq_data = data; + unsigned long pstate_ul = freq_data->pstate_id; + unsigned long gpstate_ul = freq_data->gpstate_id; val = get_pmspr(SPRN_PMCR); val = val & 0x0000FFFFFFFFFFFFULL; pstate_ul = pstate_ul & 0xFF; + gpstate_ul = gpstate_ul & 0xFF; /* Set both global(bits 56..63) and local(bits 48..55) PStates */ - val = val | (pstate_ul << 56) | (pstate_ul << 48); + val = val | (gpstate_ul << 56) | (pstate_ul << 48); pr_debug("Setting cpu %d pmcr to %016lX\n", raw_smp_processor_id(), val); @@ -424,6 +482,111 @@ next: } } +/** + * calc_global_pstate - Calculate global pstate + * @elapsed_time: Elapsed time in milliseconds + * @local_pstate: New local pstate + * @highest_lpstate: pstate from which its ramping down + * + * Finds the appropriate global pstate based on the pstate from which its + * ramping down and the time elapsed in ramping down. It follows a quadratic + * equation which ensures that it reaches ramping down to pmin in 5sec. + */ +static inline int calc_global_pstate(unsigned int elapsed_time, + int highest_lpstate, int local_pstate) +{ + int pstate_diff; + + /* + * Using ramp_down_percent we get the percentage of rampdown + * that we are expecting to be dropping. Difference between + * highest_lpstate and powernv_pstate_info.min will give a absolute + * number of how many pstates we will drop eventually by the end of + * 5 seconds, then just scale it get the number pstates to be dropped. + */ + pstate_diff = ((int)ramp_down_percent(elapsed_time) * + (highest_lpstate - powernv_pstate_info.min)) / 100; + + /* Ensure that global pstate is >= to local pstate */ + if (highest_lpstate - pstate_diff < local_pstate) + return local_pstate; + else + return highest_lpstate - pstate_diff; +} + +static inline void queue_gpstate_timer(struct global_pstate_info *gpstates) +{ + unsigned int timer_interval; + + /* + * Setting up timer to fire after GPSTATE_TIMER_INTERVAL ms, But + * if it exceeds MAX_RAMP_DOWN_TIME ms for ramp down time. + * Set timer such that it fires exactly at MAX_RAMP_DOWN_TIME + * seconds of ramp down time. + */ + if ((gpstates->elapsed_time + GPSTATE_TIMER_INTERVAL) + > MAX_RAMP_DOWN_TIME) + timer_interval = MAX_RAMP_DOWN_TIME - gpstates->elapsed_time; + else + timer_interval = GPSTATE_TIMER_INTERVAL; + + mod_timer_pinned(&gpstates->timer, jiffies + + msecs_to_jiffies(timer_interval)); +} + +/** + * gpstate_timer_handler + * + * @data: pointer to cpufreq_policy on which timer was queued + * + * This handler brings down the global pstate closer to the local pstate + * according quadratic equation. Queues a new timer if it is still not equal + * to local pstate + */ +void gpstate_timer_handler(unsigned long data) +{ + struct cpufreq_policy *policy = (struct cpufreq_policy *)data; + struct global_pstate_info *gpstates = policy->driver_data; + int gpstate_id; + unsigned int time_diff = jiffies_to_msecs(jiffies) + - gpstates->last_sampled_time; + struct powernv_smp_call_data freq_data; + + if (!spin_trylock(&gpstates->gpstate_lock)) + return; + + gpstates->last_sampled_time += time_diff; + gpstates->elapsed_time += time_diff; + freq_data.pstate_id = gpstates->last_lpstate; + + if ((gpstates->last_gpstate == freq_data.pstate_id) || + (gpstates->elapsed_time > MAX_RAMP_DOWN_TIME)) { + gpstate_id = freq_data.pstate_id; + reset_gpstates(policy); + gpstates->highest_lpstate = freq_data.pstate_id; + } else { + gpstate_id = calc_global_pstate(gpstates->elapsed_time, + gpstates->highest_lpstate, + freq_data.pstate_id); + } + + /* + * If local pstate is equal to global pstate, rampdown is over + * So timer is not required to be queued. + */ + if (gpstate_id != freq_data.pstate_id) + queue_gpstate_timer(gpstates); + + freq_data.gpstate_id = gpstate_id; + gpstates->last_gpstate = freq_data.gpstate_id; + gpstates->last_lpstate = freq_data.pstate_id; + + spin_unlock(&gpstates->gpstate_lock); + + /* Timer may get migrated to a different cpu on cpu hot unplug */ + smp_call_function_any(policy->cpus, set_pstate, &freq_data, 1); +} + /* * powernv_cpufreq_target_index: Sets the frequency corresponding to * the cpufreq table entry indexed by new_index on the cpus in the @@ -433,6 +596,8 @@ static int powernv_cpufreq_target_index(struct cpufreq_policy *policy, unsigned int new_index) { struct powernv_smp_call_data freq_data; + unsigned int cur_msec, gpstate_id; + struct global_pstate_info *gpstates = policy->driver_data; if (unlikely(rebooting) && new_index != get_nominal_index()) return 0; @@ -440,28 +605,81 @@ static int powernv_cpufreq_target_index(struct cpufreq_policy *policy, if (!throttled) powernv_cpufreq_throttle_check(NULL); + cur_msec = jiffies_to_msecs(get_jiffies_64()); + + spin_lock(&gpstates->gpstate_lock); freq_data.pstate_id = powernv_freqs[new_index].driver_data; + if (!gpstates->last_sampled_time) { + gpstate_id = freq_data.pstate_id; + gpstates->highest_lpstate = freq_data.pstate_id; + goto gpstates_done; + } + + if (gpstates->last_gpstate > freq_data.pstate_id) { + gpstates->elapsed_time += cur_msec - + gpstates->last_sampled_time; + + /* + * If its has been ramping down for more than MAX_RAMP_DOWN_TIME + * we should be resetting all global pstate related data. Set it + * equal to local pstate to start fresh. + */ + if (gpstates->elapsed_time > MAX_RAMP_DOWN_TIME) { + reset_gpstates(policy); + gpstates->highest_lpstate = freq_data.pstate_id; + gpstate_id = freq_data.pstate_id; + } else { + /* Elaspsed_time is less than 5 seconds, continue to rampdown */ + gpstate_id = calc_global_pstate(gpstates->elapsed_time, + gpstates->highest_lpstate, + freq_data.pstate_id); + } + } else { + reset_gpstates(policy); + gpstates->highest_lpstate = freq_data.pstate_id; + gpstate_id = freq_data.pstate_id; + } + + /* + * If local pstate is equal to global pstate, rampdown is over + * So timer is not required to be queued. + */ + if (gpstate_id != freq_data.pstate_id) + queue_gpstate_timer(gpstates); + else + del_timer_sync(&gpstates->timer); + +gpstates_done: + freq_data.gpstate_id = gpstate_id; + gpstates->last_sampled_time = cur_msec; + gpstates->last_gpstate = freq_data.gpstate_id; + gpstates->last_lpstate = freq_data.pstate_id; + + spin_unlock(&gpstates->gpstate_lock); + /* * Use smp_call_function to send IPI and execute the * mtspr on target CPU. We could do that without IPI * if current CPU is within policy->cpus (core) */ smp_call_function_any(policy->cpus, set_pstate, &freq_data, 1); - return 0; } static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy) { - int base, i; + int base, i, ret; + struct kernfs_node *kn; + struct global_pstate_info *gpstates; base = cpu_first_thread_sibling(policy->cpu); for (i = 0; i < threads_per_core; i++) cpumask_set_cpu(base + i, policy->cpus); - if (!policy->driver_data) { + kn = kernfs_find_and_get(policy->kobj.sd, throttle_attr_grp.name); + if (!kn) { int ret; ret = sysfs_create_group(&policy->kobj, &throttle_attr_grp); @@ -470,13 +688,37 @@ static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy) policy->cpu); return ret; } - /* - * policy->driver_data is used as a flag for one-time - * creation of throttle sysfs files. - */ - policy->driver_data = policy; + } else { + kernfs_put(kn); } - return cpufreq_table_validate_and_show(policy, powernv_freqs); + + gpstates = kzalloc(sizeof(*gpstates), GFP_KERNEL); + if (!gpstates) + return -ENOMEM; + + policy->driver_data = gpstates; + + /* initialize timer */ + init_timer_deferrable(&gpstates->timer); + gpstates->timer.data = (unsigned long)policy; + gpstates->timer.function = gpstate_timer_handler; + gpstates->timer.expires = jiffies + + msecs_to_jiffies(GPSTATE_TIMER_INTERVAL); + spin_lock_init(&gpstates->gpstate_lock); + ret = cpufreq_table_validate_and_show(policy, powernv_freqs); + + if (ret < 0) + kfree(policy->driver_data); + + return ret; +} + +static int powernv_cpufreq_cpu_exit(struct cpufreq_policy *policy) +{ + /* timer is deleted in cpufreq_cpu_stop() */ + kfree(policy->driver_data); + + return 0; } static int powernv_cpufreq_reboot_notifier(struct notifier_block *nb, @@ -604,15 +846,19 @@ static struct notifier_block powernv_cpufreq_opal_nb = { static void powernv_cpufreq_stop_cpu(struct cpufreq_policy *policy) { struct powernv_smp_call_data freq_data; + struct global_pstate_info *gpstates = policy->driver_data; freq_data.pstate_id = powernv_pstate_info.min; + freq_data.gpstate_id = powernv_pstate_info.min; smp_call_function_single(policy->cpu, set_pstate, &freq_data, 1); + del_timer_sync(&gpstates->timer); } static struct cpufreq_driver powernv_cpufreq_driver = { .name = "powernv-cpufreq", .flags = CPUFREQ_CONST_LOOPS, .init = powernv_cpufreq_cpu_init, + .exit = powernv_cpufreq_cpu_exit, .verify = cpufreq_generic_frequency_table_verify, .target_index = powernv_cpufreq_target_index, .get = powernv_cpufreq_get, diff --git a/drivers/cpufreq/ppc_cbe_cpufreq.h b/drivers/cpufreq/ppc_cbe_cpufreq.h index b4c00a5a6a59..3eace725ccd6 100644 --- a/drivers/cpufreq/ppc_cbe_cpufreq.h +++ b/drivers/cpufreq/ppc_cbe_cpufreq.h @@ -17,7 +17,7 @@ int cbe_cpufreq_get_pmode(int cpu); int cbe_cpufreq_set_pmode_pmi(int cpu, unsigned int pmode); -#if defined(CONFIG_CPU_FREQ_CBE_PMI) || defined(CONFIG_CPU_FREQ_CBE_PMI_MODULE) +#if IS_ENABLED(CONFIG_CPU_FREQ_CBE_PMI) extern bool cbe_cpufreq_has_pmi; #else #define cbe_cpufreq_has_pmi (0) diff --git a/drivers/cpufreq/ppc_cbe_cpufreq_pmi.c b/drivers/cpufreq/ppc_cbe_cpufreq_pmi.c index 7969f7690498..7c4cd5c634f2 100644 --- a/drivers/cpufreq/ppc_cbe_cpufreq_pmi.c +++ b/drivers/cpufreq/ppc_cbe_cpufreq_pmi.c @@ -23,7 +23,7 @@ #include <linux/kernel.h> #include <linux/types.h> #include <linux/timer.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/of_platform.h> #include <asm/processor.h> @@ -142,15 +142,4 @@ static int __init cbe_cpufreq_pmi_init(void) return 0; } - -static void __exit cbe_cpufreq_pmi_exit(void) -{ - cpufreq_unregister_notifier(&pmi_notifier_block, CPUFREQ_POLICY_NOTIFIER); - pmi_unregister_handler(&cbe_pmi_handler); -} - -module_init(cbe_cpufreq_pmi_init); -module_exit(cbe_cpufreq_pmi_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>"); +device_initcall(cbe_cpufreq_pmi_init); diff --git a/drivers/cpufreq/pxa2xx-cpufreq.c b/drivers/cpufreq/pxa2xx-cpufreq.c index 46fee1539cc8..ce345bf34d5d 100644 --- a/drivers/cpufreq/pxa2xx-cpufreq.c +++ b/drivers/cpufreq/pxa2xx-cpufreq.c @@ -29,6 +29,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/sched.h> @@ -186,8 +188,7 @@ static int pxa_cpufreq_change_voltage(const struct pxa_freqs *pxa_freq) ret = regulator_set_voltage(vcc_core, vmin, vmax); if (ret) - pr_err("cpufreq: Failed to set vcc_core in [%dmV..%dmV]\n", - vmin, vmax); + pr_err("Failed to set vcc_core in [%dmV..%dmV]\n", vmin, vmax); return ret; } @@ -195,10 +196,10 @@ static void __init pxa_cpufreq_init_voltages(void) { vcc_core = regulator_get(NULL, "vcc_core"); if (IS_ERR(vcc_core)) { - pr_info("cpufreq: Didn't find vcc_core regulator\n"); + pr_info("Didn't find vcc_core regulator\n"); vcc_core = NULL; } else { - pr_info("cpufreq: Found vcc_core regulator\n"); + pr_info("Found vcc_core regulator\n"); } } #else @@ -233,9 +234,8 @@ static void pxa27x_guess_max_freq(void) { if (!pxa27x_maxfreq) { pxa27x_maxfreq = 416000; - printk(KERN_INFO "PXA CPU 27x max frequency not defined " - "(pxa27x_maxfreq), assuming pxa271 with %dkHz maxfreq\n", - pxa27x_maxfreq); + pr_info("PXA CPU 27x max frequency not defined (pxa27x_maxfreq), assuming pxa271 with %dkHz maxfreq\n", + pxa27x_maxfreq); } else { pxa27x_maxfreq *= 1000; } @@ -408,7 +408,7 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy) */ if (cpu_is_pxa25x()) { find_freq_tables(&pxa255_freq_table, &pxa255_freqs); - pr_info("PXA255 cpufreq using %s frequency table\n", + pr_info("using %s frequency table\n", pxa255_turbo_table ? "turbo" : "run"); cpufreq_table_validate_and_show(policy, pxa255_freq_table); @@ -417,7 +417,7 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy) cpufreq_table_validate_and_show(policy, pxa27x_freq_table); } - printk(KERN_INFO "PXA CPU frequency change support initialized\n"); + pr_info("frequency change support initialized\n"); return 0; } diff --git a/drivers/cpufreq/qoriq-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c index b23e525a7af3..53d8c3fb16f6 100644 --- a/drivers/cpufreq/qoriq-cpufreq.c +++ b/drivers/cpufreq/qoriq-cpufreq.c @@ -301,10 +301,11 @@ err_np: return -ENODEV; } -static int __exit qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy) +static int qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy) { struct cpu_data *data = policy->driver_data; + cpufreq_cooling_unregister(data->cdev); kfree(data->pclk); kfree(data->table); kfree(data); @@ -333,8 +334,8 @@ static void qoriq_cpufreq_ready(struct cpufreq_policy *policy) cpud->cdev = of_cpufreq_cooling_register(np, policy->related_cpus); - if (IS_ERR(cpud->cdev)) { - pr_err("Failed to register cooling device cpu%d: %ld\n", + if (IS_ERR(cpud->cdev) && PTR_ERR(cpud->cdev) != -ENOSYS) { + pr_err("cpu%d is not running as cooling device: %ld\n", policy->cpu, PTR_ERR(cpud->cdev)); cpud->cdev = NULL; @@ -348,7 +349,7 @@ static struct cpufreq_driver qoriq_cpufreq_driver = { .name = "qoriq_cpufreq", .flags = CPUFREQ_CONST_LOOPS, .init = qoriq_cpufreq_cpu_init, - .exit = __exit_p(qoriq_cpufreq_cpu_exit), + .exit = qoriq_cpufreq_cpu_exit, .verify = cpufreq_generic_frequency_table_verify, .target_index = qoriq_cpufreq_target, .get = cpufreq_generic_get, diff --git a/drivers/cpufreq/s3c2412-cpufreq.c b/drivers/cpufreq/s3c2412-cpufreq.c index eb262133fef2..b04b6f02bbdc 100644 --- a/drivers/cpufreq/s3c2412-cpufreq.c +++ b/drivers/cpufreq/s3c2412-cpufreq.c @@ -10,6 +10,8 @@ * published by the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/module.h> #include <linux/interrupt.h> @@ -197,21 +199,20 @@ static int s3c2412_cpufreq_add(struct device *dev, hclk = clk_get(NULL, "hclk"); if (IS_ERR(hclk)) { - printk(KERN_ERR "%s: cannot find hclk clock\n", __func__); + pr_err("cannot find hclk clock\n"); return -ENOENT; } fclk = clk_get(NULL, "fclk"); if (IS_ERR(fclk)) { - printk(KERN_ERR "%s: cannot find fclk clock\n", __func__); + pr_err("cannot find fclk clock\n"); goto err_fclk; } fclk_rate = clk_get_rate(fclk); if (fclk_rate > 200000000) { - printk(KERN_INFO - "%s: fclk %ld MHz, assuming 266MHz capable part\n", - __func__, fclk_rate / 1000000); + pr_info("fclk %ld MHz, assuming 266MHz capable part\n", + fclk_rate / 1000000); s3c2412_cpufreq_info.max.fclk = 266000000; s3c2412_cpufreq_info.max.hclk = 133000000; s3c2412_cpufreq_info.max.pclk = 66000000; @@ -219,13 +220,13 @@ static int s3c2412_cpufreq_add(struct device *dev, armclk = clk_get(NULL, "armclk"); if (IS_ERR(armclk)) { - printk(KERN_ERR "%s: cannot find arm clock\n", __func__); + pr_err("cannot find arm clock\n"); goto err_armclk; } xtal = clk_get(NULL, "xtal"); if (IS_ERR(xtal)) { - printk(KERN_ERR "%s: cannot find xtal clock\n", __func__); + pr_err("cannot find xtal clock\n"); goto err_xtal; } diff --git a/drivers/cpufreq/s3c2440-cpufreq.c b/drivers/cpufreq/s3c2440-cpufreq.c index 0129f5c70a61..d0d75b65ddd6 100644 --- a/drivers/cpufreq/s3c2440-cpufreq.c +++ b/drivers/cpufreq/s3c2440-cpufreq.c @@ -11,6 +11,8 @@ * published by the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/module.h> #include <linux/interrupt.h> @@ -66,7 +68,7 @@ static int s3c2440_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg) __func__, fclk, armclk, hclk_max); if (armclk > fclk) { - printk(KERN_WARNING "%s: armclk > fclk\n", __func__); + pr_warn("%s: armclk > fclk\n", __func__); armclk = fclk; } @@ -273,7 +275,7 @@ static int s3c2440_cpufreq_add(struct device *dev, armclk = s3c_cpufreq_clk_get(NULL, "armclk"); if (IS_ERR(xtal) || IS_ERR(hclk) || IS_ERR(fclk) || IS_ERR(armclk)) { - printk(KERN_ERR "%s: failed to get clocks\n", __func__); + pr_err("%s: failed to get clocks\n", __func__); return -ENOENT; } diff --git a/drivers/cpufreq/s3c24xx-cpufreq-debugfs.c b/drivers/cpufreq/s3c24xx-cpufreq-debugfs.c index 9b7b4289d66c..4d976e8dbb2f 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq-debugfs.c +++ b/drivers/cpufreq/s3c24xx-cpufreq-debugfs.c @@ -10,6 +10,8 @@ * published by the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/export.h> #include <linux/interrupt.h> @@ -178,7 +180,7 @@ static int __init s3c_freq_debugfs_init(void) { dbgfs_root = debugfs_create_dir("s3c-cpufreq", NULL); if (IS_ERR(dbgfs_root)) { - printk(KERN_ERR "%s: error creating debugfs root\n", __func__); + pr_err("%s: error creating debugfs root\n", __func__); return PTR_ERR(dbgfs_root); } diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c index 68ef8fd9482f..ae8eaed77b70 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq.c +++ b/drivers/cpufreq/s3c24xx-cpufreq.c @@ -10,6 +10,8 @@ * published by the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/module.h> #include <linux/interrupt.h> @@ -175,7 +177,7 @@ static int s3c_cpufreq_settarget(struct cpufreq_policy *policy, cpu_new.freq.fclk = cpu_new.pll.frequency; if (s3c_cpufreq_calcdivs(&cpu_new) < 0) { - printk(KERN_ERR "no divisors for %d\n", target_freq); + pr_err("no divisors for %d\n", target_freq); goto err_notpossible; } @@ -187,7 +189,7 @@ static int s3c_cpufreq_settarget(struct cpufreq_policy *policy, if (cpu_new.freq.hclk != cpu_cur.freq.hclk) { if (s3c_cpufreq_calcio(&cpu_new) < 0) { - printk(KERN_ERR "%s: no IO timings\n", __func__); + pr_err("%s: no IO timings\n", __func__); goto err_notpossible; } } @@ -262,7 +264,7 @@ static int s3c_cpufreq_settarget(struct cpufreq_policy *policy, return 0; err_notpossible: - printk(KERN_ERR "no compatible settings for %d\n", target_freq); + pr_err("no compatible settings for %d\n", target_freq); return -EINVAL; } @@ -331,7 +333,7 @@ static int s3c_cpufreq_target(struct cpufreq_policy *policy, &index); if (ret < 0) { - printk(KERN_ERR "%s: no PLL available\n", __func__); + pr_err("%s: no PLL available\n", __func__); goto err_notpossible; } @@ -346,7 +348,7 @@ static int s3c_cpufreq_target(struct cpufreq_policy *policy, return s3c_cpufreq_settarget(policy, target_freq, pll); err_notpossible: - printk(KERN_ERR "no compatible settings for %d\n", target_freq); + pr_err("no compatible settings for %d\n", target_freq); return -EINVAL; } @@ -356,7 +358,7 @@ struct clk *s3c_cpufreq_clk_get(struct device *dev, const char *name) clk = clk_get(dev, name); if (IS_ERR(clk)) - printk(KERN_ERR "cpufreq: failed to get clock '%s'\n", name); + pr_err("failed to get clock '%s'\n", name); return clk; } @@ -378,15 +380,16 @@ static int __init s3c_cpufreq_initclks(void) if (IS_ERR(clk_fclk) || IS_ERR(clk_hclk) || IS_ERR(clk_pclk) || IS_ERR(_clk_mpll) || IS_ERR(clk_arm) || IS_ERR(_clk_xtal)) { - printk(KERN_ERR "%s: could not get clock(s)\n", __func__); + pr_err("%s: could not get clock(s)\n", __func__); return -ENOENT; } - printk(KERN_INFO "%s: clocks f=%lu,h=%lu,p=%lu,a=%lu\n", __func__, - clk_get_rate(clk_fclk) / 1000, - clk_get_rate(clk_hclk) / 1000, - clk_get_rate(clk_pclk) / 1000, - clk_get_rate(clk_arm) / 1000); + pr_info("%s: clocks f=%lu,h=%lu,p=%lu,a=%lu\n", + __func__, + clk_get_rate(clk_fclk) / 1000, + clk_get_rate(clk_hclk) / 1000, + clk_get_rate(clk_pclk) / 1000, + clk_get_rate(clk_arm) / 1000); return 0; } @@ -424,7 +427,7 @@ static int s3c_cpufreq_resume(struct cpufreq_policy *policy) ret = s3c_cpufreq_settarget(NULL, suspend_freq, &suspend_pll); if (ret) { - printk(KERN_ERR "%s: failed to reset pll/freq\n", __func__); + pr_err("%s: failed to reset pll/freq\n", __func__); return ret; } @@ -449,13 +452,12 @@ static struct cpufreq_driver s3c24xx_driver = { int s3c_cpufreq_register(struct s3c_cpufreq_info *info) { if (!info || !info->name) { - printk(KERN_ERR "%s: failed to pass valid information\n", - __func__); + pr_err("%s: failed to pass valid information\n", __func__); return -EINVAL; } - printk(KERN_INFO "S3C24XX CPU Frequency driver, %s cpu support\n", - info->name); + pr_info("S3C24XX CPU Frequency driver, %s cpu support\n", + info->name); /* check our driver info has valid data */ @@ -478,7 +480,7 @@ int __init s3c_cpufreq_setboard(struct s3c_cpufreq_board *board) struct s3c_cpufreq_board *ours; if (!board) { - printk(KERN_INFO "%s: no board data\n", __func__); + pr_info("%s: no board data\n", __func__); return -EINVAL; } @@ -487,7 +489,7 @@ int __init s3c_cpufreq_setboard(struct s3c_cpufreq_board *board) ours = kzalloc(sizeof(*ours), GFP_KERNEL); if (ours == NULL) { - printk(KERN_ERR "%s: no memory\n", __func__); + pr_err("%s: no memory\n", __func__); return -ENOMEM; } @@ -502,15 +504,15 @@ static int __init s3c_cpufreq_auto_io(void) int ret; if (!cpu_cur.info->get_iotiming) { - printk(KERN_ERR "%s: get_iotiming undefined\n", __func__); + pr_err("%s: get_iotiming undefined\n", __func__); return -ENOENT; } - printk(KERN_INFO "%s: working out IO settings\n", __func__); + pr_info("%s: working out IO settings\n", __func__); ret = (cpu_cur.info->get_iotiming)(&cpu_cur, &s3c24xx_iotiming); if (ret) - printk(KERN_ERR "%s: failed to get timings\n", __func__); + pr_err("%s: failed to get timings\n", __func__); return ret; } @@ -561,7 +563,7 @@ static void s3c_cpufreq_update_loctkime(void) val = calc_locktime(rate, cpu_cur.info->locktime_u) << bits; val |= calc_locktime(rate, cpu_cur.info->locktime_m); - printk(KERN_INFO "%s: new locktime is 0x%08x\n", __func__, val); + pr_info("%s: new locktime is 0x%08x\n", __func__, val); __raw_writel(val, S3C2410_LOCKTIME); } @@ -580,7 +582,7 @@ static int s3c_cpufreq_build_freq(void) ftab = kzalloc(sizeof(*ftab) * size, GFP_KERNEL); if (!ftab) { - printk(KERN_ERR "%s: no memory for tables\n", __func__); + pr_err("%s: no memory for tables\n", __func__); return -ENOMEM; } @@ -608,15 +610,14 @@ static int __init s3c_cpufreq_initcall(void) if (cpu_cur.board->auto_io) { ret = s3c_cpufreq_auto_io(); if (ret) { - printk(KERN_ERR "%s: failed to get io timing\n", + pr_err("%s: failed to get io timing\n", __func__); goto out; } } if (cpu_cur.board->need_io && !cpu_cur.info->set_iotiming) { - printk(KERN_ERR "%s: no IO support registered\n", - __func__); + pr_err("%s: no IO support registered\n", __func__); ret = -EINVAL; goto out; } @@ -666,9 +667,9 @@ int s3c_plltab_register(struct cpufreq_frequency_table *plls, vals += plls_no; vals->frequency = CPUFREQ_TABLE_END; - printk(KERN_INFO "cpufreq: %d PLL entries\n", plls_no); + pr_info("%d PLL entries\n", plls_no); } else - printk(KERN_ERR "cpufreq: no memory for PLL tables\n"); + pr_err("no memory for PLL tables\n"); return vals ? 0 : -ENOMEM; } diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c index a145b319d171..06d85917b6d5 100644 --- a/drivers/cpufreq/s5pv210-cpufreq.c +++ b/drivers/cpufreq/s5pv210-cpufreq.c @@ -9,6 +9,8 @@ * published by the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/types.h> #include <linux/kernel.h> #include <linux/init.h> @@ -205,7 +207,7 @@ static void s5pv210_set_refresh(enum s5pv210_dmc_port ch, unsigned long freq) } else if (ch == DMC1) { reg = (dmc_base[1] + 0x30); } else { - printk(KERN_ERR "Cannot find DMC port\n"); + pr_err("Cannot find DMC port\n"); return; } @@ -534,7 +536,7 @@ static int s5pv210_cpu_init(struct cpufreq_policy *policy) mem_type = check_mem_type(dmc_base[0]); if ((mem_type != LPDDR) && (mem_type != LPDDR2)) { - printk(KERN_ERR "CPUFreq doesn't support this memory type\n"); + pr_err("CPUFreq doesn't support this memory type\n"); ret = -EINVAL; goto out_dmc1; } @@ -635,13 +637,13 @@ static int s5pv210_cpufreq_probe(struct platform_device *pdev) arm_regulator = regulator_get(NULL, "vddarm"); if (IS_ERR(arm_regulator)) { - pr_err("failed to get regulator vddarm"); + pr_err("failed to get regulator vddarm\n"); return PTR_ERR(arm_regulator); } int_regulator = regulator_get(NULL, "vddint"); if (IS_ERR(int_regulator)) { - pr_err("failed to get regulator vddint"); + pr_err("failed to get regulator vddint\n"); regulator_put(arm_regulator); return PTR_ERR(int_regulator); } diff --git a/drivers/cpufreq/sc520_freq.c b/drivers/cpufreq/sc520_freq.c index ac84e4818014..4225501a4b78 100644 --- a/drivers/cpufreq/sc520_freq.c +++ b/drivers/cpufreq/sc520_freq.c @@ -13,6 +13,8 @@ * 2005-03-30: - initial revision */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -30,8 +32,6 @@ static __u8 __iomem *cpuctl; -#define PFX "sc520_freq: " - static struct cpufreq_frequency_table sc520_freq_table[] = { {0, 0x01, 100000}, {0, 0x02, 133000}, @@ -44,8 +44,8 @@ static unsigned int sc520_freq_get_cpu_frequency(unsigned int cpu) switch (clockspeed_reg & 0x03) { default: - printk(KERN_ERR PFX "error: cpuctl register has unexpected " - "value %02x\n", clockspeed_reg); + pr_err("error: cpuctl register has unexpected value %02x\n", + clockspeed_reg); case 0x01: return 100000; case 0x02: @@ -112,7 +112,7 @@ static int __init sc520_freq_init(void) cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1); if (!cpuctl) { - printk(KERN_ERR "sc520_freq: error: failed to remap memory\n"); + pr_err("sc520_freq: error: failed to remap memory\n"); return -ENOMEM; } diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c index de5e89b2eaaa..e8a7bf57b31b 100644 --- a/drivers/cpufreq/scpi-cpufreq.c +++ b/drivers/cpufreq/scpi-cpufreq.c @@ -18,6 +18,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/cpu.h> #include <linux/cpufreq.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -38,10 +39,20 @@ static struct scpi_dvfs_info *scpi_get_dvfs_info(struct device *cpu_dev) return scpi_ops->dvfs_get_info(domain); } -static int scpi_opp_table_ops(struct device *cpu_dev, bool remove) +static int scpi_get_transition_latency(struct device *cpu_dev) { - int idx, ret = 0; + struct scpi_dvfs_info *info = scpi_get_dvfs_info(cpu_dev); + + if (IS_ERR(info)) + return PTR_ERR(info); + return info->latency; +} + +static int scpi_init_opp_table(const struct cpumask *cpumask) +{ + int idx, ret; struct scpi_opp *opp; + struct device *cpu_dev = get_cpu_device(cpumask_first(cpumask)); struct scpi_dvfs_info *info = scpi_get_dvfs_info(cpu_dev); if (IS_ERR(info)) @@ -51,11 +62,7 @@ static int scpi_opp_table_ops(struct device *cpu_dev, bool remove) return -EIO; for (opp = info->opps, idx = 0; idx < info->count; idx++, opp++) { - if (remove) - dev_pm_opp_remove(cpu_dev, opp->freq); - else - ret = dev_pm_opp_add(cpu_dev, opp->freq, - opp->m_volt * 1000); + ret = dev_pm_opp_add(cpu_dev, opp->freq, opp->m_volt * 1000); if (ret) { dev_warn(cpu_dev, "failed to add opp %uHz %umV\n", opp->freq, opp->m_volt); @@ -64,33 +71,19 @@ static int scpi_opp_table_ops(struct device *cpu_dev, bool remove) return ret; } } - return ret; -} -static int scpi_get_transition_latency(struct device *cpu_dev) -{ - struct scpi_dvfs_info *info = scpi_get_dvfs_info(cpu_dev); - - if (IS_ERR(info)) - return PTR_ERR(info); - return info->latency; -} - -static int scpi_init_opp_table(struct device *cpu_dev) -{ - return scpi_opp_table_ops(cpu_dev, false); -} - -static void scpi_free_opp_table(struct device *cpu_dev) -{ - scpi_opp_table_ops(cpu_dev, true); + ret = dev_pm_opp_set_sharing_cpus(cpu_dev, cpumask); + if (ret) + dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", + __func__, ret); + return ret; } static struct cpufreq_arm_bL_ops scpi_cpufreq_ops = { .name = "scpi", .get_transition_latency = scpi_get_transition_latency, .init_opp_table = scpi_init_opp_table, - .free_opp_table = scpi_free_opp_table, + .free_opp_table = dev_pm_opp_cpumask_remove_table, }; static int scpi_cpufreq_probe(struct platform_device *pdev) diff --git a/drivers/cpufreq/speedstep-centrino.c b/drivers/cpufreq/speedstep-centrino.c index 7d4a31571608..41bc5397f4bb 100644 --- a/drivers/cpufreq/speedstep-centrino.c +++ b/drivers/cpufreq/speedstep-centrino.c @@ -13,6 +13,8 @@ * Copyright (C) 2003 Jeremy Fitzhardinge <jeremy@goop.org> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -27,7 +29,6 @@ #include <asm/cpufeature.h> #include <asm/cpu_device_id.h> -#define PFX "speedstep-centrino: " #define MAINTAINER "linux-pm@vger.kernel.org" #define INTEL_MSR_RANGE (0xffff) @@ -386,8 +387,7 @@ static int centrino_cpu_init(struct cpufreq_policy *policy) /* check to see if it stuck */ rdmsr(MSR_IA32_MISC_ENABLE, l, h); if (!(l & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) { - printk(KERN_INFO PFX - "couldn't enable Enhanced SpeedStep\n"); + pr_info("couldn't enable Enhanced SpeedStep\n"); return -ENODEV; } } diff --git a/drivers/cpufreq/speedstep-ich.c b/drivers/cpufreq/speedstep-ich.c index 37555c6b86a7..b86953a3ddc4 100644 --- a/drivers/cpufreq/speedstep-ich.c +++ b/drivers/cpufreq/speedstep-ich.c @@ -18,6 +18,8 @@ * SPEEDSTEP - DEFINITIONS * *********************************************************************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -68,13 +70,13 @@ static int speedstep_find_register(void) /* get PMBASE */ pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase); if (!(pmbase & 0x01)) { - printk(KERN_ERR "speedstep-ich: could not find speedstep register\n"); + pr_err("could not find speedstep register\n"); return -ENODEV; } pmbase &= 0xFFFFFFFE; if (!pmbase) { - printk(KERN_ERR "speedstep-ich: could not find speedstep register\n"); + pr_err("could not find speedstep register\n"); return -ENODEV; } @@ -136,7 +138,7 @@ static void speedstep_set_state(unsigned int state) pr_debug("change to %u MHz succeeded\n", speedstep_get_frequency(speedstep_processor) / 1000); else - printk(KERN_ERR "cpufreq: change failed - I/O error\n"); + pr_err("change failed - I/O error\n"); return; } diff --git a/drivers/cpufreq/speedstep-lib.c b/drivers/cpufreq/speedstep-lib.c index 15d3214aaa00..1b8062182c81 100644 --- a/drivers/cpufreq/speedstep-lib.c +++ b/drivers/cpufreq/speedstep-lib.c @@ -8,6 +8,8 @@ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -153,7 +155,7 @@ static unsigned int pentium_core_get_frequency(void) fsb = 333333; break; default: - printk(KERN_ERR "PCORE - MSR_FSB_FREQ undefined value"); + pr_err("PCORE - MSR_FSB_FREQ undefined value\n"); } rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp); @@ -453,11 +455,8 @@ unsigned int speedstep_get_freqs(enum speedstep_processor processor, */ if (*transition_latency > 10000000 || *transition_latency < 50000) { - printk(KERN_WARNING PFX "frequency transition " - "measured seems out of range (%u " - "nSec), falling back to a safe one of" - "%u nSec.\n", - *transition_latency, 500000); + pr_warn("frequency transition measured seems out of range (%u nSec), falling back to a safe one of %u nSec\n", + *transition_latency, 500000); *transition_latency = 500000; } } diff --git a/drivers/cpufreq/speedstep-smi.c b/drivers/cpufreq/speedstep-smi.c index 819229e824fb..770a9ae1999a 100644 --- a/drivers/cpufreq/speedstep-smi.c +++ b/drivers/cpufreq/speedstep-smi.c @@ -12,6 +12,8 @@ * SPEEDSTEP - DEFINITIONS * *********************************************************************/ +#define pr_fmt(fmt) "cpufreq: " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -204,9 +206,8 @@ static void speedstep_set_state(unsigned int state) (speedstep_freqs[new_state].frequency / 1000), retry, result); else - printk(KERN_ERR "cpufreq: change to state %u " - "failed with new_state %u and result %u\n", - state, new_state, result); + pr_err("change to state %u failed with new_state %u and result %u\n", + state, new_state, result); return; } diff --git a/drivers/cpufreq/sti-cpufreq.c b/drivers/cpufreq/sti-cpufreq.c index a9c659f58974..04042038ec4b 100644 --- a/drivers/cpufreq/sti-cpufreq.c +++ b/drivers/cpufreq/sti-cpufreq.c @@ -259,6 +259,10 @@ static int sti_cpufreq_init(void) { int ret; + if ((!of_machine_is_compatible("st,stih407")) && + (!of_machine_is_compatible("st,stih410"))) + return -ENODEV; + ddata.cpu = get_cpu_device(0); if (!ddata.cpu) { dev_err(ddata.cpu, "Failed to get device for CPU0\n"); diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c index 20bcceb58ccc..43530254201a 100644 --- a/drivers/cpufreq/tegra124-cpufreq.c +++ b/drivers/cpufreq/tegra124-cpufreq.c @@ -14,7 +14,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/clk.h> -#include <linux/cpufreq-dt.h> #include <linux/err.h> #include <linux/init.h> #include <linux/kernel.h> @@ -69,10 +68,6 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv) clk_set_parent(priv->cpu_clk, priv->pllx_clk); } -static struct cpufreq_dt_platform_data cpufreq_dt_pd = { - .independent_clocks = false, -}; - static int tegra124_cpufreq_probe(struct platform_device *pdev) { struct tegra124_cpufreq_priv *priv; @@ -129,8 +124,6 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev) cpufreq_dt_devinfo.name = "cpufreq-dt"; cpufreq_dt_devinfo.parent = &pdev->dev; - cpufreq_dt_devinfo.data = &cpufreq_dt_pd; - cpufreq_dt_devinfo.size_data = sizeof(cpufreq_dt_pd); priv->cpufreq_dt_pdev = platform_device_register_full(&cpufreq_dt_devinfo); diff --git a/drivers/cpufreq/vexpress-spc-cpufreq.c b/drivers/cpufreq/vexpress-spc-cpufreq.c index 433e93fd4900..87e5bdc5ec74 100644 --- a/drivers/cpufreq/vexpress-spc-cpufreq.c +++ b/drivers/cpufreq/vexpress-spc-cpufreq.c @@ -18,6 +18,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/cpu.h> #include <linux/cpufreq.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -26,8 +27,9 @@ #include "arm_big_little.h" -static int ve_spc_init_opp_table(struct device *cpu_dev) +static int ve_spc_init_opp_table(const struct cpumask *cpumask) { + struct device *cpu_dev = get_cpu_device(cpumask_first(cpumask)); /* * platform specific SPC code must initialise the opp table * so just check if the OPP count is non-zero diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c index 545069d5fdfb..e342565e8715 100644 --- a/drivers/cpuidle/cpuidle-arm.c +++ b/drivers/cpuidle/cpuidle-arm.c @@ -50,7 +50,7 @@ static int arm_enter_idle_state(struct cpuidle_device *dev, * call the CPU ops suspend protocol with idle index as a * parameter. */ - arm_cpuidle_suspend(idx); + ret = arm_cpuidle_suspend(idx); cpu_pm_exit(); } diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index f996efc56605..2b8e6ce62e81 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -173,7 +173,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, struct cpuidle_state *target_state = &drv->states[index]; bool broadcast = !!(target_state->flags & CPUIDLE_FLAG_TIMER_STOP); - ktime_t time_start, time_end; + u64 time_start, time_end; s64 diff; /* @@ -195,13 +195,13 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, sched_idle_set_state(target_state); trace_cpu_idle_rcuidle(index, dev->cpu); - time_start = ktime_get(); + time_start = local_clock(); stop_critical_timings(); entered_state = target_state->enter(dev, drv, index); start_critical_timings(); - time_end = ktime_get(); + time_end = local_clock(); trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); /* The cpu is no longer idle or about to enter idle. */ @@ -217,7 +217,11 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, if (!cpuidle_state_is_coupled(drv, entered_state)) local_irq_enable(); - diff = ktime_to_us(ktime_sub(time_end, time_start)); + /* + * local_clock() returns the time in nanosecond, let's shift + * by 10 (divide by 1024) to have microsecond based time. + */ + diff = (time_end - time_start) >> 10; if (diff > INT_MAX) diff = INT_MAX; @@ -433,6 +437,8 @@ static void __cpuidle_unregister_device(struct cpuidle_device *dev) list_del(&dev->device_list); per_cpu(cpuidle_devices, dev->cpu) = NULL; module_put(drv->owner); + + dev->registered = 0; } static void __cpuidle_device_init(struct cpuidle_device *dev) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index a0d4a08313ae..aae05547b924 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -63,6 +63,14 @@ static void to_talitos_ptr(struct talitos_ptr *ptr, dma_addr_t dma_addr, ptr->eptr = upper_32_bits(dma_addr); } +static void copy_talitos_ptr(struct talitos_ptr *dst_ptr, + struct talitos_ptr *src_ptr, bool is_sec1) +{ + dst_ptr->ptr = src_ptr->ptr; + if (!is_sec1) + dst_ptr->eptr = src_ptr->eptr; +} + static void to_talitos_ptr_len(struct talitos_ptr *ptr, unsigned int len, bool is_sec1) { @@ -1083,21 +1091,20 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq, sg_count = dma_map_sg(dev, areq->src, edesc->src_nents ?: 1, (areq->src == areq->dst) ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE); - /* hmac data */ desc->ptr[1].len = cpu_to_be16(areq->assoclen); if (sg_count > 1 && (ret = sg_to_link_tbl_offset(areq->src, sg_count, 0, areq->assoclen, &edesc->link_tbl[tbl_off])) > 1) { - tbl_off += ret; - to_talitos_ptr(&desc->ptr[1], edesc->dma_link_tbl + tbl_off * sizeof(struct talitos_ptr), 0); desc->ptr[1].j_extent = DESC_PTR_LNKTBL_JUMP; dma_sync_single_for_device(dev, edesc->dma_link_tbl, edesc->dma_len, DMA_BIDIRECTIONAL); + + tbl_off += ret; } else { to_talitos_ptr(&desc->ptr[1], sg_dma_address(areq->src), 0); desc->ptr[1].j_extent = 0; @@ -1126,11 +1133,13 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq, if (edesc->desc.hdr & DESC_HDR_MODE1_MDEU_CICV) sg_link_tbl_len += authsize; - if (sg_count > 1 && - (ret = sg_to_link_tbl_offset(areq->src, sg_count, areq->assoclen, - sg_link_tbl_len, - &edesc->link_tbl[tbl_off])) > 1) { - tbl_off += ret; + if (sg_count == 1) { + to_talitos_ptr(&desc->ptr[4], sg_dma_address(areq->src) + + areq->assoclen, 0); + } else if ((ret = sg_to_link_tbl_offset(areq->src, sg_count, + areq->assoclen, sg_link_tbl_len, + &edesc->link_tbl[tbl_off])) > + 1) { desc->ptr[4].j_extent |= DESC_PTR_LNKTBL_JUMP; to_talitos_ptr(&desc->ptr[4], edesc->dma_link_tbl + tbl_off * @@ -1138,8 +1147,10 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq, dma_sync_single_for_device(dev, edesc->dma_link_tbl, edesc->dma_len, DMA_BIDIRECTIONAL); - } else - to_talitos_ptr(&desc->ptr[4], sg_dma_address(areq->src), 0); + tbl_off += ret; + } else { + copy_talitos_ptr(&desc->ptr[4], &edesc->link_tbl[tbl_off], 0); + } /* cipher out */ desc->ptr[5].len = cpu_to_be16(cryptlen); @@ -1151,11 +1162,13 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq, edesc->icv_ool = false; - if (sg_count > 1 && - (sg_count = sg_to_link_tbl_offset(areq->dst, sg_count, + if (sg_count == 1) { + to_talitos_ptr(&desc->ptr[5], sg_dma_address(areq->dst) + + areq->assoclen, 0); + } else if ((sg_count = + sg_to_link_tbl_offset(areq->dst, sg_count, areq->assoclen, cryptlen, - &edesc->link_tbl[tbl_off])) > - 1) { + &edesc->link_tbl[tbl_off])) > 1) { struct talitos_ptr *tbl_ptr = &edesc->link_tbl[tbl_off]; to_talitos_ptr(&desc->ptr[5], edesc->dma_link_tbl + @@ -1178,8 +1191,9 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq, edesc->dma_len, DMA_BIDIRECTIONAL); edesc->icv_ool = true; - } else - to_talitos_ptr(&desc->ptr[5], sg_dma_address(areq->dst), 0); + } else { + copy_talitos_ptr(&desc->ptr[5], &edesc->link_tbl[tbl_off], 0); + } /* iv out */ map_single_talitos_ptr(dev, &desc->ptr[6], ivsize, ctx->iv, @@ -2629,21 +2643,11 @@ struct talitos_crypto_alg { struct talitos_alg_template algt; }; -static int talitos_cra_init(struct crypto_tfm *tfm) +static int talitos_init_common(struct talitos_ctx *ctx, + struct talitos_crypto_alg *talitos_alg) { - struct crypto_alg *alg = tfm->__crt_alg; - struct talitos_crypto_alg *talitos_alg; - struct talitos_ctx *ctx = crypto_tfm_ctx(tfm); struct talitos_private *priv; - if ((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_AHASH) - talitos_alg = container_of(__crypto_ahash_alg(alg), - struct talitos_crypto_alg, - algt.alg.hash); - else - talitos_alg = container_of(alg, struct talitos_crypto_alg, - algt.alg.crypto); - /* update context with ptr to dev */ ctx->dev = talitos_alg->dev; @@ -2661,10 +2665,33 @@ static int talitos_cra_init(struct crypto_tfm *tfm) return 0; } +static int talitos_cra_init(struct crypto_tfm *tfm) +{ + struct crypto_alg *alg = tfm->__crt_alg; + struct talitos_crypto_alg *talitos_alg; + struct talitos_ctx *ctx = crypto_tfm_ctx(tfm); + + if ((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_AHASH) + talitos_alg = container_of(__crypto_ahash_alg(alg), + struct talitos_crypto_alg, + algt.alg.hash); + else + talitos_alg = container_of(alg, struct talitos_crypto_alg, + algt.alg.crypto); + + return talitos_init_common(ctx, talitos_alg); +} + static int talitos_cra_init_aead(struct crypto_aead *tfm) { - talitos_cra_init(crypto_aead_tfm(tfm)); - return 0; + struct aead_alg *alg = crypto_aead_alg(tfm); + struct talitos_crypto_alg *talitos_alg; + struct talitos_ctx *ctx = crypto_aead_ctx(tfm); + + talitos_alg = container_of(alg, struct talitos_crypto_alg, + algt.alg.aead); + + return talitos_init_common(ctx, talitos_alg); } static int talitos_cra_init_ahash(struct crypto_tfm *tfm) diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 4de78c552251..78dac0e9da11 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -64,30 +64,32 @@ config DEVFREQ_GOV_USERSPACE Otherwise, the governor does not change the frequency given at the initialization. +config DEVFREQ_GOV_PASSIVE + tristate "Passive" + help + Sets the frequency based on the frequency of its parent devfreq + device. This governor does not change the frequency by itself + through sysfs entries. The passive governor recommends that + devfreq device uses the OPP table to get the frequency/voltage. + comment "DEVFREQ Drivers" -config ARM_EXYNOS4_BUS_DEVFREQ - bool "ARM Exynos4210/4212/4412 Memory Bus DEVFREQ Driver" - depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM +config ARM_EXYNOS_BUS_DEVFREQ + bool "ARM EXYNOS Generic Memory Bus DEVFREQ Driver" + depends on ARCH_EXYNOS select DEVFREQ_GOV_SIMPLE_ONDEMAND + select DEVFREQ_GOV_PASSIVE + select DEVFREQ_EVENT_EXYNOS_PPMU + select PM_DEVFREQ_EVENT select PM_OPP help - This adds the DEVFREQ driver for Exynos4210 memory bus (vdd_int) - and Exynos4212/4412 memory interface and bus (vdd_mif + vdd_int). - It reads PPMU counters of memory controllers and adjusts - the operating frequencies and voltages with OPP support. + This adds the common DEVFREQ driver for Exynos Memory bus. Exynos + Memory bus has one more group of memory bus (e.g, MIF and INT block). + Each memory bus group could contain many memoby bus block. It reads + PPMU counters of memory controllers by using DEVFREQ-event device + and adjusts the operating frequencies and voltages with OPP support. This does not yet operate with optimal voltages. -config ARM_EXYNOS5_BUS_DEVFREQ - tristate "ARM Exynos5250 Bus DEVFREQ Driver" - depends on SOC_EXYNOS5250 - select DEVFREQ_GOV_SIMPLE_ONDEMAND - select PM_OPP - help - This adds the DEVFREQ driver for Exynos5250 bus interface (vdd_int). - It reads PPMU counters of memory controllers and adjusts the - operating frequencies and voltages with OPP support. - config ARM_TEGRA_DEVFREQ tristate "Tegra DEVFREQ Driver" depends on ARCH_TEGRA_124_SOC diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 5134f9ee983d..09f11d9d40d5 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -4,10 +4,10 @@ obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) += governor_simpleondemand.o obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o +obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o # DEVFREQ Drivers -obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos/ -obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos/ +obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o # DEVFREQ Event Drivers diff --git a/drivers/devfreq/devfreq-event.c b/drivers/devfreq/devfreq-event.c index 38bf144ca147..39b048eda2ce 100644 --- a/drivers/devfreq/devfreq-event.c +++ b/drivers/devfreq/devfreq-event.c @@ -235,6 +235,11 @@ struct devfreq_event_dev *devfreq_event_get_edev_by_phandle(struct device *dev, mutex_lock(&devfreq_event_list_lock); list_for_each_entry(edev, &devfreq_event_list, node) { + if (edev->dev.parent && edev->dev.parent->of_node == node) + goto out; + } + + list_for_each_entry(edev, &devfreq_event_list, node) { if (!strcmp(edev->desc->name, node->name)) goto out; } diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 984c5e9e7bdd..1d6c803804d5 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -25,6 +25,7 @@ #include <linux/list.h> #include <linux/printk.h> #include <linux/hrtimer.h> +#include <linux/of.h> #include "governor.h" static struct class *devfreq_class; @@ -188,6 +189,29 @@ static struct devfreq_governor *find_devfreq_governor(const char *name) return ERR_PTR(-ENODEV); } +static int devfreq_notify_transition(struct devfreq *devfreq, + struct devfreq_freqs *freqs, unsigned int state) +{ + if (!devfreq) + return -EINVAL; + + switch (state) { + case DEVFREQ_PRECHANGE: + srcu_notifier_call_chain(&devfreq->transition_notifier_list, + DEVFREQ_PRECHANGE, freqs); + break; + + case DEVFREQ_POSTCHANGE: + srcu_notifier_call_chain(&devfreq->transition_notifier_list, + DEVFREQ_POSTCHANGE, freqs); + break; + default: + return -EINVAL; + } + + return 0; +} + /* Load monitoring helper functions for governors use */ /** @@ -199,7 +223,8 @@ static struct devfreq_governor *find_devfreq_governor(const char *name) */ int update_devfreq(struct devfreq *devfreq) { - unsigned long freq; + struct devfreq_freqs freqs; + unsigned long freq, cur_freq; int err = 0; u32 flags = 0; @@ -233,10 +258,22 @@ int update_devfreq(struct devfreq *devfreq) flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ } + if (devfreq->profile->get_cur_freq) + devfreq->profile->get_cur_freq(devfreq->dev.parent, &cur_freq); + else + cur_freq = devfreq->previous_freq; + + freqs.old = cur_freq; + freqs.new = freq; + devfreq_notify_transition(devfreq, &freqs, DEVFREQ_PRECHANGE); + err = devfreq->profile->target(devfreq->dev.parent, &freq, flags); if (err) return err; + freqs.new = freq; + devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); + if (devfreq->profile->freq_table) if (devfreq_update_status(devfreq, freq)) dev_err(&devfreq->dev, @@ -541,6 +578,8 @@ struct devfreq *devfreq_add_device(struct device *dev, goto err_out; } + srcu_init_notifier_head(&devfreq->transition_notifier_list); + mutex_unlock(&devfreq->lock); mutex_lock(&devfreq_list_lock); @@ -639,6 +678,49 @@ struct devfreq *devm_devfreq_add_device(struct device *dev, } EXPORT_SYMBOL(devm_devfreq_add_device); +#ifdef CONFIG_OF +/* + * devfreq_get_devfreq_by_phandle - Get the devfreq device from devicetree + * @dev - instance to the given device + * @index - index into list of devfreq + * + * return the instance of devfreq device + */ +struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, int index) +{ + struct device_node *node; + struct devfreq *devfreq; + + if (!dev) + return ERR_PTR(-EINVAL); + + if (!dev->of_node) + return ERR_PTR(-EINVAL); + + node = of_parse_phandle(dev->of_node, "devfreq", index); + if (!node) + return ERR_PTR(-ENODEV); + + mutex_lock(&devfreq_list_lock); + list_for_each_entry(devfreq, &devfreq_list, node) { + if (devfreq->dev.parent + && devfreq->dev.parent->of_node == node) { + mutex_unlock(&devfreq_list_lock); + return devfreq; + } + } + mutex_unlock(&devfreq_list_lock); + + return ERR_PTR(-EPROBE_DEFER); +} +#else +struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, int index) +{ + return ERR_PTR(-ENODEV); +} +#endif /* CONFIG_OF */ +EXPORT_SYMBOL_GPL(devfreq_get_devfreq_by_phandle); + /** * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device() * @dev: the device to add devfreq feature. @@ -1266,6 +1348,129 @@ void devm_devfreq_unregister_opp_notifier(struct device *dev, } EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); +/** + * devfreq_register_notifier() - Register a driver with devfreq + * @devfreq: The devfreq object. + * @nb: The notifier block to register. + * @list: DEVFREQ_TRANSITION_NOTIFIER. + */ +int devfreq_register_notifier(struct devfreq *devfreq, + struct notifier_block *nb, + unsigned int list) +{ + int ret = 0; + + if (!devfreq) + return -EINVAL; + + switch (list) { + case DEVFREQ_TRANSITION_NOTIFIER: + ret = srcu_notifier_chain_register( + &devfreq->transition_notifier_list, nb); + break; + default: + ret = -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL(devfreq_register_notifier); + +/* + * devfreq_unregister_notifier() - Unregister a driver with devfreq + * @devfreq: The devfreq object. + * @nb: The notifier block to be unregistered. + * @list: DEVFREQ_TRANSITION_NOTIFIER. + */ +int devfreq_unregister_notifier(struct devfreq *devfreq, + struct notifier_block *nb, + unsigned int list) +{ + int ret = 0; + + if (!devfreq) + return -EINVAL; + + switch (list) { + case DEVFREQ_TRANSITION_NOTIFIER: + ret = srcu_notifier_chain_unregister( + &devfreq->transition_notifier_list, nb); + break; + default: + ret = -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL(devfreq_unregister_notifier); + +struct devfreq_notifier_devres { + struct devfreq *devfreq; + struct notifier_block *nb; + unsigned int list; +}; + +static void devm_devfreq_notifier_release(struct device *dev, void *res) +{ + struct devfreq_notifier_devres *this = res; + + devfreq_unregister_notifier(this->devfreq, this->nb, this->list); +} + +/** + * devm_devfreq_register_notifier() + - Resource-managed devfreq_register_notifier() + * @dev: The devfreq user device. (parent of devfreq) + * @devfreq: The devfreq object. + * @nb: The notifier block to be unregistered. + * @list: DEVFREQ_TRANSITION_NOTIFIER. + */ +int devm_devfreq_register_notifier(struct device *dev, + struct devfreq *devfreq, + struct notifier_block *nb, + unsigned int list) +{ + struct devfreq_notifier_devres *ptr; + int ret; + + ptr = devres_alloc(devm_devfreq_notifier_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = devfreq_register_notifier(devfreq, nb, list); + if (ret) { + devres_free(ptr); + return ret; + } + + ptr->devfreq = devfreq; + ptr->nb = nb; + ptr->list = list; + devres_add(dev, ptr); + + return 0; +} +EXPORT_SYMBOL(devm_devfreq_register_notifier); + +/** + * devm_devfreq_unregister_notifier() + - Resource-managed devfreq_unregister_notifier() + * @dev: The devfreq user device. (parent of devfreq) + * @devfreq: The devfreq object. + * @nb: The notifier block to be unregistered. + * @list: DEVFREQ_TRANSITION_NOTIFIER. + */ +void devm_devfreq_unregister_notifier(struct device *dev, + struct devfreq *devfreq, + struct notifier_block *nb, + unsigned int list) +{ + WARN_ON(devres_release(dev, devm_devfreq_notifier_release, + devm_devfreq_dev_match, devfreq)); +} +EXPORT_SYMBOL(devm_devfreq_unregister_notifier); + MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); MODULE_DESCRIPTION("devfreq class support"); MODULE_LICENSE("GPL"); diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig index a11720affc31..1e8b4f469f38 100644 --- a/drivers/devfreq/event/Kconfig +++ b/drivers/devfreq/event/Kconfig @@ -13,6 +13,14 @@ menuconfig PM_DEVFREQ_EVENT if PM_DEVFREQ_EVENT +config DEVFREQ_EVENT_EXYNOS_NOCP + bool "EXYNOS NoC (Network On Chip) Probe DEVFREQ event Driver" + depends on ARCH_EXYNOS + select PM_OPP + help + This add the devfreq-event driver for Exynos SoC. It provides NoC + (Network on Chip) Probe counters to measure the bandwidth of AXI bus. + config DEVFREQ_EVENT_EXYNOS_PPMU bool "EXYNOS PPMU (Platform Performance Monitoring Unit) DEVFREQ event Driver" depends on ARCH_EXYNOS diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile index be146ead79cf..3d6afd352253 100644 --- a/drivers/devfreq/event/Makefile +++ b/drivers/devfreq/event/Makefile @@ -1,2 +1,4 @@ # Exynos DEVFREQ Event Drivers + +obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP) += exynos-nocp.o obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o diff --git a/drivers/devfreq/event/exynos-nocp.c b/drivers/devfreq/event/exynos-nocp.c new file mode 100644 index 000000000000..6b6a5f310486 --- /dev/null +++ b/drivers/devfreq/event/exynos-nocp.c @@ -0,0 +1,304 @@ +/* + * exynos-nocp.c - EXYNOS NoC (Network On Chip) Probe support + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Author : Chanwoo Choi <cw00.choi@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/devfreq-event.h> +#include <linux/kernel.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include "exynos-nocp.h" + +struct exynos_nocp { + struct devfreq_event_dev *edev; + struct devfreq_event_desc desc; + + struct device *dev; + + struct regmap *regmap; + struct clk *clk; +}; + +/* + * The devfreq-event ops structure for nocp probe. + */ +static int exynos_nocp_set_event(struct devfreq_event_dev *edev) +{ + struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev); + int ret; + + /* Disable NoC probe */ + ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL, + NOCP_MAIN_CTL_STATEN_MASK, 0); + if (ret < 0) { + dev_err(nocp->dev, "failed to disable the NoC probe device\n"); + return ret; + } + + /* Set a statistics dump period to 0 */ + ret = regmap_write(nocp->regmap, NOCP_STAT_PERIOD, 0x0); + if (ret < 0) + goto out; + + /* Set the IntEvent fields of *_SRC */ + ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_SRC, + NOCP_CNT_SRC_INTEVENT_MASK, + NOCP_CNT_SRC_INTEVENT_BYTE_MASK); + if (ret < 0) + goto out; + + ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_SRC, + NOCP_CNT_SRC_INTEVENT_MASK, + NOCP_CNT_SRC_INTEVENT_CHAIN_MASK); + if (ret < 0) + goto out; + + ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_SRC, + NOCP_CNT_SRC_INTEVENT_MASK, + NOCP_CNT_SRC_INTEVENT_CYCLE_MASK); + if (ret < 0) + goto out; + + ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_SRC, + NOCP_CNT_SRC_INTEVENT_MASK, + NOCP_CNT_SRC_INTEVENT_CHAIN_MASK); + if (ret < 0) + goto out; + + + /* Set an alarm with a max/min value of 0 to generate StatALARM */ + ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MIN, 0x0); + if (ret < 0) + goto out; + + ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MAX, 0x0); + if (ret < 0) + goto out; + + /* Set AlarmMode */ + ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_ALARM_MODE, + NOCP_CNT_ALARM_MODE_MASK, + NOCP_CNT_ALARM_MODE_MIN_MAX_MASK); + if (ret < 0) + goto out; + + ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_ALARM_MODE, + NOCP_CNT_ALARM_MODE_MASK, + NOCP_CNT_ALARM_MODE_MIN_MAX_MASK); + if (ret < 0) + goto out; + + ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_ALARM_MODE, + NOCP_CNT_ALARM_MODE_MASK, + NOCP_CNT_ALARM_MODE_MIN_MAX_MASK); + if (ret < 0) + goto out; + + ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_ALARM_MODE, + NOCP_CNT_ALARM_MODE_MASK, + NOCP_CNT_ALARM_MODE_MIN_MAX_MASK); + if (ret < 0) + goto out; + + /* Enable the measurements by setting AlarmEn and StatEn */ + ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL, + NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK, + NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK); + if (ret < 0) + goto out; + + /* Set GlobalEN */ + ret = regmap_update_bits(nocp->regmap, NOCP_CFG_CTL, + NOCP_CFG_CTL_GLOBALEN_MASK, + NOCP_CFG_CTL_GLOBALEN_MASK); + if (ret < 0) + goto out; + + /* Enable NoC probe */ + ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL, + NOCP_MAIN_CTL_STATEN_MASK, + NOCP_MAIN_CTL_STATEN_MASK); + if (ret < 0) + goto out; + + return 0; + +out: + /* Reset NoC probe */ + if (regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL, + NOCP_MAIN_CTL_STATEN_MASK, 0)) { + dev_err(nocp->dev, "Failed to reset NoC probe device\n"); + } + + return ret; +} + +static int exynos_nocp_get_event(struct devfreq_event_dev *edev, + struct devfreq_event_data *edata) +{ + struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev); + unsigned int counter[4]; + int ret; + + /* Read cycle count */ + ret = regmap_read(nocp->regmap, NOCP_COUNTERS_0_VAL, &counter[0]); + if (ret < 0) + goto out; + + ret = regmap_read(nocp->regmap, NOCP_COUNTERS_1_VAL, &counter[1]); + if (ret < 0) + goto out; + + ret = regmap_read(nocp->regmap, NOCP_COUNTERS_2_VAL, &counter[2]); + if (ret < 0) + goto out; + + ret = regmap_read(nocp->regmap, NOCP_COUNTERS_3_VAL, &counter[3]); + if (ret < 0) + goto out; + + edata->load_count = ((counter[1] << 16) | counter[0]); + edata->total_count = ((counter[3] << 16) | counter[2]); + + dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name, + edata->load_count, edata->total_count); + + return 0; + +out: + edata->load_count = 0; + edata->total_count = 0; + + dev_err(nocp->dev, "Failed to read the counter of NoC probe device\n"); + + return ret; +} + +static const struct devfreq_event_ops exynos_nocp_ops = { + .set_event = exynos_nocp_set_event, + .get_event = exynos_nocp_get_event, +}; + +static const struct of_device_id exynos_nocp_id_match[] = { + { .compatible = "samsung,exynos5420-nocp", }, + { /* sentinel */ }, +}; + +static struct regmap_config exynos_nocp_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = NOCP_COUNTERS_3_VAL, +}; + +static int exynos_nocp_parse_dt(struct platform_device *pdev, + struct exynos_nocp *nocp) +{ + struct device *dev = nocp->dev; + struct device_node *np = dev->of_node; + struct resource *res; + void __iomem *base; + + if (!np) { + dev_err(dev, "failed to find devicetree node\n"); + return -EINVAL; + } + + nocp->clk = devm_clk_get(dev, "nocp"); + if (IS_ERR(nocp->clk)) + nocp->clk = NULL; + + /* Maps the memory mapped IO to control nocp register */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (IS_ERR(res)) + return PTR_ERR(res); + + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + exynos_nocp_regmap_config.max_register = resource_size(res) - 4; + + nocp->regmap = devm_regmap_init_mmio(dev, base, + &exynos_nocp_regmap_config); + if (IS_ERR(nocp->regmap)) { + dev_err(dev, "failed to initialize regmap\n"); + return PTR_ERR(nocp->regmap); + } + + return 0; +} + +static int exynos_nocp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct exynos_nocp *nocp; + int ret; + + nocp = devm_kzalloc(&pdev->dev, sizeof(*nocp), GFP_KERNEL); + if (!nocp) + return -ENOMEM; + + nocp->dev = &pdev->dev; + + /* Parse dt data to get resource */ + ret = exynos_nocp_parse_dt(pdev, nocp); + if (ret < 0) { + dev_err(&pdev->dev, + "failed to parse devicetree for resource\n"); + return ret; + } + + /* Add devfreq-event device to measure the bandwidth of NoC */ + nocp->desc.ops = &exynos_nocp_ops; + nocp->desc.driver_data = nocp; + nocp->desc.name = np->full_name; + nocp->edev = devm_devfreq_event_add_edev(&pdev->dev, &nocp->desc); + if (IS_ERR(nocp->edev)) { + dev_err(&pdev->dev, + "failed to add devfreq-event device\n"); + return PTR_ERR(nocp->edev); + } + platform_set_drvdata(pdev, nocp); + + clk_prepare_enable(nocp->clk); + + pr_info("exynos-nocp: new NoC Probe device registered: %s\n", + dev_name(dev)); + + return 0; +} + +static int exynos_nocp_remove(struct platform_device *pdev) +{ + struct exynos_nocp *nocp = platform_get_drvdata(pdev); + + clk_disable_unprepare(nocp->clk); + + return 0; +} + +static struct platform_driver exynos_nocp_driver = { + .probe = exynos_nocp_probe, + .remove = exynos_nocp_remove, + .driver = { + .name = "exynos-nocp", + .of_match_table = exynos_nocp_id_match, + }, +}; +module_platform_driver(exynos_nocp_driver); + +MODULE_DESCRIPTION("Exynos NoC (Network on Chip) Probe driver"); +MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/devfreq/event/exynos-nocp.h b/drivers/devfreq/event/exynos-nocp.h new file mode 100644 index 000000000000..28564db0edb8 --- /dev/null +++ b/drivers/devfreq/event/exynos-nocp.h @@ -0,0 +1,78 @@ +/* + * exynos-nocp.h - EXYNOS NoC (Network on Chip) Probe header file + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Author : Chanwoo Choi <cw00.choi@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __EXYNOS_NOCP_H__ +#define __EXYNOS_NOCP_H__ + +enum nocp_reg { + NOCP_ID_REVISION_ID = 0x04, + NOCP_MAIN_CTL = 0x08, + NOCP_CFG_CTL = 0x0C, + + NOCP_STAT_PERIOD = 0x24, + NOCP_STAT_GO = 0x28, + NOCP_STAT_ALARM_MIN = 0x2C, + NOCP_STAT_ALARM_MAX = 0x30, + NOCP_STAT_ALARM_STATUS = 0x34, + NOCP_STAT_ALARM_CLR = 0x38, + + NOCP_COUNTERS_0_SRC = 0x138, + NOCP_COUNTERS_0_ALARM_MODE = 0x13C, + NOCP_COUNTERS_0_VAL = 0x140, + + NOCP_COUNTERS_1_SRC = 0x14C, + NOCP_COUNTERS_1_ALARM_MODE = 0x150, + NOCP_COUNTERS_1_VAL = 0x154, + + NOCP_COUNTERS_2_SRC = 0x160, + NOCP_COUNTERS_2_ALARM_MODE = 0x164, + NOCP_COUNTERS_2_VAL = 0x168, + + NOCP_COUNTERS_3_SRC = 0x174, + NOCP_COUNTERS_3_ALARM_MODE = 0x178, + NOCP_COUNTERS_3_VAL = 0x17C, +}; + +/* NOCP_MAIN_CTL register */ +#define NOCP_MAIN_CTL_ERREN_MASK BIT(0) +#define NOCP_MAIN_CTL_TRACEEN_MASK BIT(1) +#define NOCP_MAIN_CTL_PAYLOADEN_MASK BIT(2) +#define NOCP_MAIN_CTL_STATEN_MASK BIT(3) +#define NOCP_MAIN_CTL_ALARMEN_MASK BIT(4) +#define NOCP_MAIN_CTL_STATCONDDUMP_MASK BIT(5) +#define NOCP_MAIN_CTL_INTRUSIVEMODE_MASK BIT(6) + +/* NOCP_CFG_CTL register */ +#define NOCP_CFG_CTL_GLOBALEN_MASK BIT(0) +#define NOCP_CFG_CTL_ACTIVE_MASK BIT(1) + +/* NOCP_COUNTERS_x_SRC register */ +#define NOCP_CNT_SRC_INTEVENT_SHIFT 0 +#define NOCP_CNT_SRC_INTEVENT_MASK (0x1F << NOCP_CNT_SRC_INTEVENT_SHIFT) +#define NOCP_CNT_SRC_INTEVENT_OFF_MASK (0x0 << NOCP_CNT_SRC_INTEVENT_SHIFT) +#define NOCP_CNT_SRC_INTEVENT_CYCLE_MASK (0x1 << NOCP_CNT_SRC_INTEVENT_SHIFT) +#define NOCP_CNT_SRC_INTEVENT_IDLE_MASK (0x2 << NOCP_CNT_SRC_INTEVENT_SHIFT) +#define NOCP_CNT_SRC_INTEVENT_XFER_MASK (0x3 << NOCP_CNT_SRC_INTEVENT_SHIFT) +#define NOCP_CNT_SRC_INTEVENT_BUSY_MASK (0x4 << NOCP_CNT_SRC_INTEVENT_SHIFT) +#define NOCP_CNT_SRC_INTEVENT_WAIT_MASK (0x5 << NOCP_CNT_SRC_INTEVENT_SHIFT) +#define NOCP_CNT_SRC_INTEVENT_PKT_MASK (0x6 << NOCP_CNT_SRC_INTEVENT_SHIFT) +#define NOCP_CNT_SRC_INTEVENT_BYTE_MASK (0x8 << NOCP_CNT_SRC_INTEVENT_SHIFT) +#define NOCP_CNT_SRC_INTEVENT_CHAIN_MASK (0x10 << NOCP_CNT_SRC_INTEVENT_SHIFT) + +/* NOCP_COUNTERS_x_ALARM_MODE register */ +#define NOCP_CNT_ALARM_MODE_SHIFT 0 +#define NOCP_CNT_ALARM_MODE_MASK (0x3 << NOCP_CNT_ALARM_MODE_SHIFT) +#define NOCP_CNT_ALARM_MODE_OFF_MASK (0x0 << NOCP_CNT_ALARM_MODE_SHIFT) +#define NOCP_CNT_ALARM_MODE_MIN_MASK (0x1 << NOCP_CNT_ALARM_MODE_SHIFT) +#define NOCP_CNT_ALARM_MODE_MAX_MASK (0x2 << NOCP_CNT_ALARM_MODE_SHIFT) +#define NOCP_CNT_ALARM_MODE_MIN_MAX_MASK (0x3 << NOCP_CNT_ALARM_MODE_SHIFT) + +#endif /* __EXYNOS_NOCP_H__ */ diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c new file mode 100644 index 000000000000..2363d0a189b7 --- /dev/null +++ b/drivers/devfreq/exynos-bus.c @@ -0,0 +1,570 @@ +/* + * Generic Exynos Bus frequency driver with DEVFREQ Framework + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Author : Chanwoo Choi <cw00.choi@samsung.com> + * + * This driver support Exynos Bus frequency feature by using + * DEVFREQ framework and is based on drivers/devfreq/exynos/exynos4_bus.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/devfreq.h> +#include <linux/devfreq-event.h> +#include <linux/device.h> +#include <linux/export.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/pm_opp.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +#define DEFAULT_SATURATION_RATIO 40 +#define DEFAULT_VOLTAGE_TOLERANCE 2 + +struct exynos_bus { + struct device *dev; + + struct devfreq *devfreq; + struct devfreq_event_dev **edev; + unsigned int edev_count; + struct mutex lock; + + struct dev_pm_opp *curr_opp; + + struct regulator *regulator; + struct clk *clk; + unsigned int voltage_tolerance; + unsigned int ratio; +}; + +/* + * Control the devfreq-event device to get the current state of bus + */ +#define exynos_bus_ops_edev(ops) \ +static int exynos_bus_##ops(struct exynos_bus *bus) \ +{ \ + int i, ret; \ + \ + for (i = 0; i < bus->edev_count; i++) { \ + if (!bus->edev[i]) \ + continue; \ + ret = devfreq_event_##ops(bus->edev[i]); \ + if (ret < 0) \ + return ret; \ + } \ + \ + return 0; \ +} +exynos_bus_ops_edev(enable_edev); +exynos_bus_ops_edev(disable_edev); +exynos_bus_ops_edev(set_event); + +static int exynos_bus_get_event(struct exynos_bus *bus, + struct devfreq_event_data *edata) +{ + struct devfreq_event_data event_data; + unsigned long load_count = 0, total_count = 0; + int i, ret = 0; + + for (i = 0; i < bus->edev_count; i++) { + if (!bus->edev[i]) + continue; + + ret = devfreq_event_get_event(bus->edev[i], &event_data); + if (ret < 0) + return ret; + + if (i == 0 || event_data.load_count > load_count) { + load_count = event_data.load_count; + total_count = event_data.total_count; + } + } + + edata->load_count = load_count; + edata->total_count = total_count; + + return ret; +} + +/* + * Must necessary function for devfreq simple-ondemand governor + */ +static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags) +{ + struct exynos_bus *bus = dev_get_drvdata(dev); + struct dev_pm_opp *new_opp; + unsigned long old_freq, new_freq, old_volt, new_volt, tol; + int ret = 0; + + /* Get new opp-bus instance according to new bus clock */ + rcu_read_lock(); + new_opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(new_opp)) { + dev_err(dev, "failed to get recommended opp instance\n"); + rcu_read_unlock(); + return PTR_ERR(new_opp); + } + + new_freq = dev_pm_opp_get_freq(new_opp); + new_volt = dev_pm_opp_get_voltage(new_opp); + old_freq = dev_pm_opp_get_freq(bus->curr_opp); + old_volt = dev_pm_opp_get_voltage(bus->curr_opp); + rcu_read_unlock(); + + if (old_freq == new_freq) + return 0; + tol = new_volt * bus->voltage_tolerance / 100; + + /* Change voltage and frequency according to new OPP level */ + mutex_lock(&bus->lock); + + if (old_freq < new_freq) { + ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol); + if (ret < 0) { + dev_err(bus->dev, "failed to set voltage\n"); + goto out; + } + } + + ret = clk_set_rate(bus->clk, new_freq); + if (ret < 0) { + dev_err(dev, "failed to change clock of bus\n"); + clk_set_rate(bus->clk, old_freq); + goto out; + } + + if (old_freq > new_freq) { + ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol); + if (ret < 0) { + dev_err(bus->dev, "failed to set voltage\n"); + goto out; + } + } + bus->curr_opp = new_opp; + + dev_dbg(dev, "Set the frequency of bus (%lukHz -> %lukHz)\n", + old_freq/1000, new_freq/1000); +out: + mutex_unlock(&bus->lock); + + return ret; +} + +static int exynos_bus_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) +{ + struct exynos_bus *bus = dev_get_drvdata(dev); + struct devfreq_event_data edata; + int ret; + + rcu_read_lock(); + stat->current_frequency = dev_pm_opp_get_freq(bus->curr_opp); + rcu_read_unlock(); + + ret = exynos_bus_get_event(bus, &edata); + if (ret < 0) { + stat->total_time = stat->busy_time = 0; + goto err; + } + + stat->busy_time = (edata.load_count * 100) / bus->ratio; + stat->total_time = edata.total_count; + + dev_dbg(dev, "Usage of devfreq-event : %lu/%lu\n", stat->busy_time, + stat->total_time); + +err: + ret = exynos_bus_set_event(bus); + if (ret < 0) { + dev_err(dev, "failed to set event to devfreq-event devices\n"); + return ret; + } + + return ret; +} + +static void exynos_bus_exit(struct device *dev) +{ + struct exynos_bus *bus = dev_get_drvdata(dev); + int ret; + + ret = exynos_bus_disable_edev(bus); + if (ret < 0) + dev_warn(dev, "failed to disable the devfreq-event devices\n"); + + if (bus->regulator) + regulator_disable(bus->regulator); + + dev_pm_opp_of_remove_table(dev); + clk_disable_unprepare(bus->clk); +} + +/* + * Must necessary function for devfreq passive governor + */ +static int exynos_bus_passive_target(struct device *dev, unsigned long *freq, + u32 flags) +{ + struct exynos_bus *bus = dev_get_drvdata(dev); + struct dev_pm_opp *new_opp; + unsigned long old_freq, new_freq; + int ret = 0; + + /* Get new opp-bus instance according to new bus clock */ + rcu_read_lock(); + new_opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(new_opp)) { + dev_err(dev, "failed to get recommended opp instance\n"); + rcu_read_unlock(); + return PTR_ERR(new_opp); + } + + new_freq = dev_pm_opp_get_freq(new_opp); + old_freq = dev_pm_opp_get_freq(bus->curr_opp); + rcu_read_unlock(); + + if (old_freq == new_freq) + return 0; + + /* Change the frequency according to new OPP level */ + mutex_lock(&bus->lock); + + ret = clk_set_rate(bus->clk, new_freq); + if (ret < 0) { + dev_err(dev, "failed to set the clock of bus\n"); + goto out; + } + + *freq = new_freq; + bus->curr_opp = new_opp; + + dev_dbg(dev, "Set the frequency of bus (%lukHz -> %lukHz)\n", + old_freq/1000, new_freq/1000); +out: + mutex_unlock(&bus->lock); + + return ret; +} + +static void exynos_bus_passive_exit(struct device *dev) +{ + struct exynos_bus *bus = dev_get_drvdata(dev); + + dev_pm_opp_of_remove_table(dev); + clk_disable_unprepare(bus->clk); +} + +static int exynos_bus_parent_parse_of(struct device_node *np, + struct exynos_bus *bus) +{ + struct device *dev = bus->dev; + int i, ret, count, size; + + /* Get the regulator to provide each bus with the power */ + bus->regulator = devm_regulator_get(dev, "vdd"); + if (IS_ERR(bus->regulator)) { + dev_err(dev, "failed to get VDD regulator\n"); + return PTR_ERR(bus->regulator); + } + + ret = regulator_enable(bus->regulator); + if (ret < 0) { + dev_err(dev, "failed to enable VDD regulator\n"); + return ret; + } + + /* + * Get the devfreq-event devices to get the current utilization of + * buses. This raw data will be used in devfreq ondemand governor. + */ + count = devfreq_event_get_edev_count(dev); + if (count < 0) { + dev_err(dev, "failed to get the count of devfreq-event dev\n"); + ret = count; + goto err_regulator; + } + bus->edev_count = count; + + size = sizeof(*bus->edev) * count; + bus->edev = devm_kzalloc(dev, size, GFP_KERNEL); + if (!bus->edev) { + ret = -ENOMEM; + goto err_regulator; + } + + for (i = 0; i < count; i++) { + bus->edev[i] = devfreq_event_get_edev_by_phandle(dev, i); + if (IS_ERR(bus->edev[i])) { + ret = -EPROBE_DEFER; + goto err_regulator; + } + } + + /* + * Optionally, Get the saturation ratio according to Exynos SoC + * When measuring the utilization of each AXI bus with devfreq-event + * devices, the measured real cycle might be much lower than the + * total cycle of bus during sampling rate. In result, the devfreq + * simple-ondemand governor might not decide to change the current + * frequency due to too utilization (= real cycle/total cycle). + * So, this property is used to adjust the utilization when calculating + * the busy_time in exynos_bus_get_dev_status(). + */ + if (of_property_read_u32(np, "exynos,saturation-ratio", &bus->ratio)) + bus->ratio = DEFAULT_SATURATION_RATIO; + + if (of_property_read_u32(np, "exynos,voltage-tolerance", + &bus->voltage_tolerance)) + bus->voltage_tolerance = DEFAULT_VOLTAGE_TOLERANCE; + + return 0; + +err_regulator: + regulator_disable(bus->regulator); + + return ret; +} + +static int exynos_bus_parse_of(struct device_node *np, + struct exynos_bus *bus) +{ + struct device *dev = bus->dev; + unsigned long rate; + int ret; + + /* Get the clock to provide each bus with source clock */ + bus->clk = devm_clk_get(dev, "bus"); + if (IS_ERR(bus->clk)) { + dev_err(dev, "failed to get bus clock\n"); + return PTR_ERR(bus->clk); + } + + ret = clk_prepare_enable(bus->clk); + if (ret < 0) { + dev_err(dev, "failed to get enable clock\n"); + return ret; + } + + /* Get the freq and voltage from OPP table to scale the bus freq */ + rcu_read_lock(); + ret = dev_pm_opp_of_add_table(dev); + if (ret < 0) { + dev_err(dev, "failed to get OPP table\n"); + rcu_read_unlock(); + goto err_clk; + } + + rate = clk_get_rate(bus->clk); + bus->curr_opp = devfreq_recommended_opp(dev, &rate, 0); + if (IS_ERR(bus->curr_opp)) { + dev_err(dev, "failed to find dev_pm_opp\n"); + rcu_read_unlock(); + ret = PTR_ERR(bus->curr_opp); + goto err_opp; + } + rcu_read_unlock(); + + return 0; + +err_opp: + dev_pm_opp_of_remove_table(dev); +err_clk: + clk_disable_unprepare(bus->clk); + + return ret; +} + +static int exynos_bus_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct devfreq_dev_profile *profile; + struct devfreq_simple_ondemand_data *ondemand_data; + struct devfreq_passive_data *passive_data; + struct devfreq *parent_devfreq; + struct exynos_bus *bus; + int ret, max_state; + unsigned long min_freq, max_freq; + + if (!np) { + dev_err(dev, "failed to find devicetree node\n"); + return -EINVAL; + } + + bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + mutex_init(&bus->lock); + bus->dev = &pdev->dev; + platform_set_drvdata(pdev, bus); + + /* Parse the device-tree to get the resource information */ + ret = exynos_bus_parse_of(np, bus); + if (ret < 0) + goto err; + + profile = devm_kzalloc(dev, sizeof(*profile), GFP_KERNEL); + if (!profile) { + ret = -ENOMEM; + goto err; + } + + if (of_parse_phandle(dev->of_node, "devfreq", 0)) + goto passive; + else + ret = exynos_bus_parent_parse_of(np, bus); + + if (ret < 0) + goto err; + + /* Initialize the struct profile and governor data for parent device */ + profile->polling_ms = 50; + profile->target = exynos_bus_target; + profile->get_dev_status = exynos_bus_get_dev_status; + profile->exit = exynos_bus_exit; + + ondemand_data = devm_kzalloc(dev, sizeof(*ondemand_data), GFP_KERNEL); + if (!ondemand_data) { + ret = -ENOMEM; + goto err; + } + ondemand_data->upthreshold = 40; + ondemand_data->downdifferential = 5; + + /* Add devfreq device to monitor and handle the exynos bus */ + bus->devfreq = devm_devfreq_add_device(dev, profile, "simple_ondemand", + ondemand_data); + if (IS_ERR(bus->devfreq)) { + dev_err(dev, "failed to add devfreq device\n"); + ret = PTR_ERR(bus->devfreq); + goto err; + } + + /* Register opp_notifier to catch the change of OPP */ + ret = devm_devfreq_register_opp_notifier(dev, bus->devfreq); + if (ret < 0) { + dev_err(dev, "failed to register opp notifier\n"); + goto err; + } + + /* + * Enable devfreq-event to get raw data which is used to determine + * current bus load. + */ + ret = exynos_bus_enable_edev(bus); + if (ret < 0) { + dev_err(dev, "failed to enable devfreq-event devices\n"); + goto err; + } + + ret = exynos_bus_set_event(bus); + if (ret < 0) { + dev_err(dev, "failed to set event to devfreq-event devices\n"); + goto err; + } + + goto out; +passive: + /* Initialize the struct profile and governor data for passive device */ + profile->target = exynos_bus_passive_target; + profile->exit = exynos_bus_passive_exit; + + /* Get the instance of parent devfreq device */ + parent_devfreq = devfreq_get_devfreq_by_phandle(dev, 0); + if (IS_ERR(parent_devfreq)) { + ret = -EPROBE_DEFER; + goto err; + } + + passive_data = devm_kzalloc(dev, sizeof(*passive_data), GFP_KERNEL); + if (!passive_data) { + ret = -ENOMEM; + goto err; + } + passive_data->parent = parent_devfreq; + + /* Add devfreq device for exynos bus with passive governor */ + bus->devfreq = devm_devfreq_add_device(dev, profile, "passive", + passive_data); + if (IS_ERR(bus->devfreq)) { + dev_err(dev, + "failed to add devfreq dev with passive governor\n"); + ret = -EPROBE_DEFER; + goto err; + } + +out: + max_state = bus->devfreq->profile->max_state; + min_freq = (bus->devfreq->profile->freq_table[0] / 1000); + max_freq = (bus->devfreq->profile->freq_table[max_state - 1] / 1000); + pr_info("exynos-bus: new bus device registered: %s (%6ld KHz ~ %6ld KHz)\n", + dev_name(dev), min_freq, max_freq); + + return 0; + +err: + dev_pm_opp_of_remove_table(dev); + clk_disable_unprepare(bus->clk); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int exynos_bus_resume(struct device *dev) +{ + struct exynos_bus *bus = dev_get_drvdata(dev); + int ret; + + ret = exynos_bus_enable_edev(bus); + if (ret < 0) { + dev_err(dev, "failed to enable the devfreq-event devices\n"); + return ret; + } + + return 0; +} + +static int exynos_bus_suspend(struct device *dev) +{ + struct exynos_bus *bus = dev_get_drvdata(dev); + int ret; + + ret = exynos_bus_disable_edev(bus); + if (ret < 0) { + dev_err(dev, "failed to disable the devfreq-event devices\n"); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops exynos_bus_pm = { + SET_SYSTEM_SLEEP_PM_OPS(exynos_bus_suspend, exynos_bus_resume) +}; + +static const struct of_device_id exynos_bus_of_match[] = { + { .compatible = "samsung,exynos-bus", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, exynos_bus_of_match); + +static struct platform_driver exynos_bus_platdrv = { + .probe = exynos_bus_probe, + .driver = { + .name = "exynos-bus", + .pm = &exynos_bus_pm, + .of_match_table = of_match_ptr(exynos_bus_of_match), + }, +}; +module_platform_driver(exynos_bus_platdrv); + +MODULE_DESCRIPTION("Generic Exynos Bus frequency driver"); +MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/devfreq/exynos/Makefile b/drivers/devfreq/exynos/Makefile deleted file mode 100644 index 49bc9175f923..000000000000 --- a/drivers/devfreq/exynos/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# Exynos DEVFREQ Drivers -obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos_ppmu.o exynos4_bus.o -obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos_ppmu.o exynos5_bus.o diff --git a/drivers/devfreq/exynos/exynos4_bus.c b/drivers/devfreq/exynos/exynos4_bus.c deleted file mode 100644 index da9509205169..000000000000 --- a/drivers/devfreq/exynos/exynos4_bus.c +++ /dev/null @@ -1,1055 +0,0 @@ -/* drivers/devfreq/exynos4210_memorybus.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * MyungJoo Ham <myungjoo.ham@samsung.com> - * - * EXYNOS4 - Memory/Bus clock frequency scaling support in DEVFREQ framework - * This version supports EXYNOS4210 only. This changes bus frequencies - * and vddint voltages. Exynos4412/4212 should be able to be supported - * with minor modifications. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/mutex.h> -#include <linux/suspend.h> -#include <linux/pm_opp.h> -#include <linux/devfreq.h> -#include <linux/platform_device.h> -#include <linux/regulator/consumer.h> -#include <linux/module.h> - -#include <mach/map.h> - -#include "exynos_ppmu.h" -#include "exynos4_bus.h" - -#define MAX_SAFEVOLT 1200000 /* 1.2V */ - -enum exynos4_busf_type { - TYPE_BUSF_EXYNOS4210, - TYPE_BUSF_EXYNOS4x12, -}; - -/* Assume that the bus is saturated if the utilization is 40% */ -#define BUS_SATURATION_RATIO 40 - -enum busclk_level_idx { - LV_0 = 0, - LV_1, - LV_2, - LV_3, - LV_4, - _LV_END -}; - -enum exynos_ppmu_idx { - PPMU_DMC0, - PPMU_DMC1, - PPMU_END, -}; - -#define EX4210_LV_MAX LV_2 -#define EX4x12_LV_MAX LV_4 -#define EX4210_LV_NUM (LV_2 + 1) -#define EX4x12_LV_NUM (LV_4 + 1) - -/** - * struct busfreq_opp_info - opp information for bus - * @rate: Frequency in hertz - * @volt: Voltage in microvolts corresponding to this OPP - */ -struct busfreq_opp_info { - unsigned long rate; - unsigned long volt; -}; - -struct busfreq_data { - enum exynos4_busf_type type; - struct device *dev; - struct devfreq *devfreq; - bool disabled; - struct regulator *vdd_int; - struct regulator *vdd_mif; /* Exynos4412/4212 only */ - struct busfreq_opp_info curr_oppinfo; - struct busfreq_ppmu_data ppmu_data; - - struct notifier_block pm_notifier; - struct mutex lock; - - /* Dividers calculated at boot/probe-time */ - unsigned int dmc_divtable[_LV_END]; /* DMC0 */ - unsigned int top_divtable[_LV_END]; -}; - -/* 4210 controls clock of mif and voltage of int */ -static struct bus_opp_table exynos4210_busclk_table[] = { - {LV_0, 400000, 1150000}, - {LV_1, 267000, 1050000}, - {LV_2, 133000, 1025000}, - {0, 0, 0}, -}; - -/* - * MIF is the main control knob clock for Exynos4x12 MIF/INT - * clock and voltage of both mif/int are controlled. - */ -static struct bus_opp_table exynos4x12_mifclk_table[] = { - {LV_0, 400000, 1100000}, - {LV_1, 267000, 1000000}, - {LV_2, 160000, 950000}, - {LV_3, 133000, 950000}, - {LV_4, 100000, 950000}, - {0, 0, 0}, -}; - -/* - * INT is not the control knob of 4x12. LV_x is not meant to represent - * the current performance. (MIF does) - */ -static struct bus_opp_table exynos4x12_intclk_table[] = { - {LV_0, 200000, 1000000}, - {LV_1, 160000, 950000}, - {LV_2, 133000, 925000}, - {LV_3, 100000, 900000}, - {0, 0, 0}, -}; - -/* TODO: asv volt definitions are "__initdata"? */ -/* Some chips have different operating voltages */ -static unsigned int exynos4210_asv_volt[][EX4210_LV_NUM] = { - {1150000, 1050000, 1050000}, - {1125000, 1025000, 1025000}, - {1100000, 1000000, 1000000}, - {1075000, 975000, 975000}, - {1050000, 950000, 950000}, -}; - -static unsigned int exynos4x12_mif_step_50[][EX4x12_LV_NUM] = { - /* 400 267 160 133 100 */ - {1050000, 950000, 900000, 900000, 900000}, /* ASV0 */ - {1050000, 950000, 900000, 900000, 900000}, /* ASV1 */ - {1050000, 950000, 900000, 900000, 900000}, /* ASV2 */ - {1050000, 900000, 900000, 900000, 900000}, /* ASV3 */ - {1050000, 900000, 900000, 900000, 850000}, /* ASV4 */ - {1050000, 900000, 900000, 850000, 850000}, /* ASV5 */ - {1050000, 900000, 850000, 850000, 850000}, /* ASV6 */ - {1050000, 900000, 850000, 850000, 850000}, /* ASV7 */ - {1050000, 900000, 850000, 850000, 850000}, /* ASV8 */ -}; - -static unsigned int exynos4x12_int_volt[][EX4x12_LV_NUM] = { - /* 200 160 133 100 */ - {1000000, 950000, 925000, 900000}, /* ASV0 */ - {975000, 925000, 925000, 900000}, /* ASV1 */ - {950000, 925000, 900000, 875000}, /* ASV2 */ - {950000, 900000, 900000, 875000}, /* ASV3 */ - {925000, 875000, 875000, 875000}, /* ASV4 */ - {900000, 850000, 850000, 850000}, /* ASV5 */ - {900000, 850000, 850000, 850000}, /* ASV6 */ - {900000, 850000, 850000, 850000}, /* ASV7 */ - {900000, 850000, 850000, 850000}, /* ASV8 */ -}; - -/*** Clock Divider Data for Exynos4210 ***/ -static unsigned int exynos4210_clkdiv_dmc0[][8] = { - /* - * Clock divider value for following - * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD - * DIVDMCP, DIVCOPY2, DIVCORE_TIMERS } - */ - - /* DMC L0: 400MHz */ - { 3, 1, 1, 1, 1, 1, 3, 1 }, - /* DMC L1: 266.7MHz */ - { 4, 1, 1, 2, 1, 1, 3, 1 }, - /* DMC L2: 133MHz */ - { 5, 1, 1, 5, 1, 1, 3, 1 }, -}; -static unsigned int exynos4210_clkdiv_top[][5] = { - /* - * Clock divider value for following - * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND } - */ - /* ACLK200 L0: 200MHz */ - { 3, 7, 4, 5, 1 }, - /* ACLK200 L1: 160MHz */ - { 4, 7, 5, 6, 1 }, - /* ACLK200 L2: 133MHz */ - { 5, 7, 7, 7, 1 }, -}; -static unsigned int exynos4210_clkdiv_lr_bus[][2] = { - /* - * Clock divider value for following - * { DIVGDL/R, DIVGPL/R } - */ - /* ACLK_GDL/R L1: 200MHz */ - { 3, 1 }, - /* ACLK_GDL/R L2: 160MHz */ - { 4, 1 }, - /* ACLK_GDL/R L3: 133MHz */ - { 5, 1 }, -}; - -/*** Clock Divider Data for Exynos4212/4412 ***/ -static unsigned int exynos4x12_clkdiv_dmc0[][6] = { - /* - * Clock divider value for following - * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD - * DIVDMCP} - */ - - /* DMC L0: 400MHz */ - {3, 1, 1, 1, 1, 1}, - /* DMC L1: 266.7MHz */ - {4, 1, 1, 2, 1, 1}, - /* DMC L2: 160MHz */ - {5, 1, 1, 4, 1, 1}, - /* DMC L3: 133MHz */ - {5, 1, 1, 5, 1, 1}, - /* DMC L4: 100MHz */ - {7, 1, 1, 7, 1, 1}, -}; -static unsigned int exynos4x12_clkdiv_dmc1[][6] = { - /* - * Clock divider value for following - * { G2DACP, DIVC2C, DIVC2C_ACLK } - */ - - /* DMC L0: 400MHz */ - {3, 1, 1}, - /* DMC L1: 266.7MHz */ - {4, 2, 1}, - /* DMC L2: 160MHz */ - {5, 4, 1}, - /* DMC L3: 133MHz */ - {5, 5, 1}, - /* DMC L4: 100MHz */ - {7, 7, 1}, -}; -static unsigned int exynos4x12_clkdiv_top[][5] = { - /* - * Clock divider value for following - * { DIVACLK266_GPS, DIVACLK100, DIVACLK160, - DIVACLK133, DIVONENAND } - */ - - /* ACLK_GDL/R L0: 200MHz */ - {2, 7, 4, 5, 1}, - /* ACLK_GDL/R L1: 200MHz */ - {2, 7, 4, 5, 1}, - /* ACLK_GDL/R L2: 160MHz */ - {4, 7, 5, 7, 1}, - /* ACLK_GDL/R L3: 133MHz */ - {4, 7, 5, 7, 1}, - /* ACLK_GDL/R L4: 100MHz */ - {7, 7, 7, 7, 1}, -}; -static unsigned int exynos4x12_clkdiv_lr_bus[][2] = { - /* - * Clock divider value for following - * { DIVGDL/R, DIVGPL/R } - */ - - /* ACLK_GDL/R L0: 200MHz */ - {3, 1}, - /* ACLK_GDL/R L1: 200MHz */ - {3, 1}, - /* ACLK_GDL/R L2: 160MHz */ - {4, 1}, - /* ACLK_GDL/R L3: 133MHz */ - {5, 1}, - /* ACLK_GDL/R L4: 100MHz */ - {7, 1}, -}; -static unsigned int exynos4x12_clkdiv_sclkip[][3] = { - /* - * Clock divider value for following - * { DIVMFC, DIVJPEG, DIVFIMC0~3} - */ - - /* SCLK_MFC: 200MHz */ - {3, 3, 4}, - /* SCLK_MFC: 200MHz */ - {3, 3, 4}, - /* SCLK_MFC: 160MHz */ - {4, 4, 5}, - /* SCLK_MFC: 133MHz */ - {5, 5, 5}, - /* SCLK_MFC: 100MHz */ - {7, 7, 7}, -}; - - -static int exynos4210_set_busclk(struct busfreq_data *data, - struct busfreq_opp_info *oppi) -{ - unsigned int index; - unsigned int tmp; - - for (index = LV_0; index < EX4210_LV_NUM; index++) - if (oppi->rate == exynos4210_busclk_table[index].clk) - break; - - if (index == EX4210_LV_NUM) - return -EINVAL; - - /* Change Divider - DMC0 */ - tmp = data->dmc_divtable[index]; - - __raw_writel(tmp, EXYNOS4_CLKDIV_DMC0); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC0); - } while (tmp & 0x11111111); - - /* Change Divider - TOP */ - tmp = data->top_divtable[index]; - - __raw_writel(tmp, EXYNOS4_CLKDIV_TOP); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_TOP); - } while (tmp & 0x11111); - - /* Change Divider - LEFTBUS */ - tmp = __raw_readl(EXYNOS4_CLKDIV_LEFTBUS); - - tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); - - tmp |= ((exynos4210_clkdiv_lr_bus[index][0] << - EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | - (exynos4210_clkdiv_lr_bus[index][1] << - EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_LEFTBUS); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_LEFTBUS); - } while (tmp & 0x11); - - /* Change Divider - RIGHTBUS */ - tmp = __raw_readl(EXYNOS4_CLKDIV_RIGHTBUS); - - tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); - - tmp |= ((exynos4210_clkdiv_lr_bus[index][0] << - EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | - (exynos4210_clkdiv_lr_bus[index][1] << - EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_RIGHTBUS); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_RIGHTBUS); - } while (tmp & 0x11); - - return 0; -} - -static int exynos4x12_set_busclk(struct busfreq_data *data, - struct busfreq_opp_info *oppi) -{ - unsigned int index; - unsigned int tmp; - - for (index = LV_0; index < EX4x12_LV_NUM; index++) - if (oppi->rate == exynos4x12_mifclk_table[index].clk) - break; - - if (index == EX4x12_LV_NUM) - return -EINVAL; - - /* Change Divider - DMC0 */ - tmp = data->dmc_divtable[index]; - - __raw_writel(tmp, EXYNOS4_CLKDIV_DMC0); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC0); - } while (tmp & 0x11111111); - - /* Change Divider - DMC1 */ - tmp = __raw_readl(EXYNOS4_CLKDIV_DMC1); - - tmp &= ~(EXYNOS4_CLKDIV_DMC1_G2D_ACP_MASK | - EXYNOS4_CLKDIV_DMC1_C2C_MASK | - EXYNOS4_CLKDIV_DMC1_C2CACLK_MASK); - - tmp |= ((exynos4x12_clkdiv_dmc1[index][0] << - EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT) | - (exynos4x12_clkdiv_dmc1[index][1] << - EXYNOS4_CLKDIV_DMC1_C2C_SHIFT) | - (exynos4x12_clkdiv_dmc1[index][2] << - EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_DMC1); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC1); - } while (tmp & 0x111111); - - /* Change Divider - TOP */ - tmp = __raw_readl(EXYNOS4_CLKDIV_TOP); - - tmp &= ~(EXYNOS4_CLKDIV_TOP_ACLK266_GPS_MASK | - EXYNOS4_CLKDIV_TOP_ACLK100_MASK | - EXYNOS4_CLKDIV_TOP_ACLK160_MASK | - EXYNOS4_CLKDIV_TOP_ACLK133_MASK | - EXYNOS4_CLKDIV_TOP_ONENAND_MASK); - - tmp |= ((exynos4x12_clkdiv_top[index][0] << - EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT) | - (exynos4x12_clkdiv_top[index][1] << - EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) | - (exynos4x12_clkdiv_top[index][2] << - EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) | - (exynos4x12_clkdiv_top[index][3] << - EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) | - (exynos4x12_clkdiv_top[index][4] << - EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_TOP); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_TOP); - } while (tmp & 0x11111); - - /* Change Divider - LEFTBUS */ - tmp = __raw_readl(EXYNOS4_CLKDIV_LEFTBUS); - - tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); - - tmp |= ((exynos4x12_clkdiv_lr_bus[index][0] << - EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | - (exynos4x12_clkdiv_lr_bus[index][1] << - EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_LEFTBUS); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_LEFTBUS); - } while (tmp & 0x11); - - /* Change Divider - RIGHTBUS */ - tmp = __raw_readl(EXYNOS4_CLKDIV_RIGHTBUS); - - tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); - - tmp |= ((exynos4x12_clkdiv_lr_bus[index][0] << - EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | - (exynos4x12_clkdiv_lr_bus[index][1] << - EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_RIGHTBUS); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_RIGHTBUS); - } while (tmp & 0x11); - - /* Change Divider - MFC */ - tmp = __raw_readl(EXYNOS4_CLKDIV_MFC); - - tmp &= ~(EXYNOS4_CLKDIV_MFC_MASK); - - tmp |= ((exynos4x12_clkdiv_sclkip[index][0] << - EXYNOS4_CLKDIV_MFC_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_MFC); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_MFC); - } while (tmp & 0x1); - - /* Change Divider - JPEG */ - tmp = __raw_readl(EXYNOS4_CLKDIV_CAM1); - - tmp &= ~(EXYNOS4_CLKDIV_CAM1_JPEG_MASK); - - tmp |= ((exynos4x12_clkdiv_sclkip[index][1] << - EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_CAM1); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_CAM1); - } while (tmp & 0x1); - - /* Change Divider - FIMC0~3 */ - tmp = __raw_readl(EXYNOS4_CLKDIV_CAM); - - tmp &= ~(EXYNOS4_CLKDIV_CAM_FIMC0_MASK | EXYNOS4_CLKDIV_CAM_FIMC1_MASK | - EXYNOS4_CLKDIV_CAM_FIMC2_MASK | EXYNOS4_CLKDIV_CAM_FIMC3_MASK); - - tmp |= ((exynos4x12_clkdiv_sclkip[index][2] << - EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT) | - (exynos4x12_clkdiv_sclkip[index][2] << - EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT) | - (exynos4x12_clkdiv_sclkip[index][2] << - EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT) | - (exynos4x12_clkdiv_sclkip[index][2] << - EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT)); - - __raw_writel(tmp, EXYNOS4_CLKDIV_CAM); - - do { - tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_CAM1); - } while (tmp & 0x1111); - - return 0; -} - -static int exynos4x12_get_intspec(unsigned long mifclk) -{ - int i = 0; - - while (exynos4x12_intclk_table[i].clk) { - if (exynos4x12_intclk_table[i].clk <= mifclk) - return i; - i++; - } - - return -EINVAL; -} - -static int exynos4_bus_setvolt(struct busfreq_data *data, - struct busfreq_opp_info *oppi, - struct busfreq_opp_info *oldoppi) -{ - int err = 0, tmp; - unsigned long volt = oppi->volt; - - switch (data->type) { - case TYPE_BUSF_EXYNOS4210: - /* OPP represents DMC clock + INT voltage */ - err = regulator_set_voltage(data->vdd_int, volt, - MAX_SAFEVOLT); - break; - case TYPE_BUSF_EXYNOS4x12: - /* OPP represents MIF clock + MIF voltage */ - err = regulator_set_voltage(data->vdd_mif, volt, - MAX_SAFEVOLT); - if (err) - break; - - tmp = exynos4x12_get_intspec(oppi->rate); - if (tmp < 0) { - err = tmp; - regulator_set_voltage(data->vdd_mif, - oldoppi->volt, - MAX_SAFEVOLT); - break; - } - err = regulator_set_voltage(data->vdd_int, - exynos4x12_intclk_table[tmp].volt, - MAX_SAFEVOLT); - /* Try to recover */ - if (err) - regulator_set_voltage(data->vdd_mif, - oldoppi->volt, - MAX_SAFEVOLT); - break; - default: - err = -EINVAL; - } - - return err; -} - -static int exynos4_bus_target(struct device *dev, unsigned long *_freq, - u32 flags) -{ - int err = 0; - struct platform_device *pdev = container_of(dev, struct platform_device, - dev); - struct busfreq_data *data = platform_get_drvdata(pdev); - struct dev_pm_opp *opp; - unsigned long freq; - unsigned long old_freq = data->curr_oppinfo.rate; - struct busfreq_opp_info new_oppinfo; - - rcu_read_lock(); - opp = devfreq_recommended_opp(dev, _freq, flags); - if (IS_ERR(opp)) { - rcu_read_unlock(); - return PTR_ERR(opp); - } - new_oppinfo.rate = dev_pm_opp_get_freq(opp); - new_oppinfo.volt = dev_pm_opp_get_voltage(opp); - rcu_read_unlock(); - freq = new_oppinfo.rate; - - if (old_freq == freq) - return 0; - - dev_dbg(dev, "targeting %lukHz %luuV\n", freq, new_oppinfo.volt); - - mutex_lock(&data->lock); - - if (data->disabled) - goto out; - - if (old_freq < freq) - err = exynos4_bus_setvolt(data, &new_oppinfo, - &data->curr_oppinfo); - if (err) - goto out; - - if (old_freq != freq) { - switch (data->type) { - case TYPE_BUSF_EXYNOS4210: - err = exynos4210_set_busclk(data, &new_oppinfo); - break; - case TYPE_BUSF_EXYNOS4x12: - err = exynos4x12_set_busclk(data, &new_oppinfo); - break; - default: - err = -EINVAL; - } - } - if (err) - goto out; - - if (old_freq > freq) - err = exynos4_bus_setvolt(data, &new_oppinfo, - &data->curr_oppinfo); - if (err) - goto out; - - data->curr_oppinfo = new_oppinfo; -out: - mutex_unlock(&data->lock); - return err; -} - -static int exynos4_bus_get_dev_status(struct device *dev, - struct devfreq_dev_status *stat) -{ - struct busfreq_data *data = dev_get_drvdata(dev); - struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; - int busier; - - exynos_read_ppmu(ppmu_data); - busier = exynos_get_busier_ppmu(ppmu_data); - stat->current_frequency = data->curr_oppinfo.rate; - - /* Number of cycles spent on memory access */ - stat->busy_time = ppmu_data->ppmu[busier].count[PPMU_PMNCNT3]; - stat->busy_time *= 100 / BUS_SATURATION_RATIO; - stat->total_time = ppmu_data->ppmu[busier].ccnt; - - /* If the counters have overflown, retry */ - if (ppmu_data->ppmu[busier].ccnt_overflow || - ppmu_data->ppmu[busier].count_overflow[0]) - return -EAGAIN; - - return 0; -} - -static struct devfreq_dev_profile exynos4_devfreq_profile = { - .initial_freq = 400000, - .polling_ms = 50, - .target = exynos4_bus_target, - .get_dev_status = exynos4_bus_get_dev_status, -}; - -static int exynos4210_init_tables(struct busfreq_data *data) -{ - u32 tmp; - int mgrp; - int i, err = 0; - - tmp = __raw_readl(EXYNOS4_CLKDIV_DMC0); - for (i = LV_0; i < EX4210_LV_NUM; i++) { - tmp &= ~(EXYNOS4_CLKDIV_DMC0_ACP_MASK | - EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK | - EXYNOS4_CLKDIV_DMC0_DPHY_MASK | - EXYNOS4_CLKDIV_DMC0_DMC_MASK | - EXYNOS4_CLKDIV_DMC0_DMCD_MASK | - EXYNOS4_CLKDIV_DMC0_DMCP_MASK | - EXYNOS4_CLKDIV_DMC0_COPY2_MASK | - EXYNOS4_CLKDIV_DMC0_CORETI_MASK); - - tmp |= ((exynos4210_clkdiv_dmc0[i][0] << - EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) | - (exynos4210_clkdiv_dmc0[i][1] << - EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) | - (exynos4210_clkdiv_dmc0[i][2] << - EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) | - (exynos4210_clkdiv_dmc0[i][3] << - EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) | - (exynos4210_clkdiv_dmc0[i][4] << - EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) | - (exynos4210_clkdiv_dmc0[i][5] << - EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT) | - (exynos4210_clkdiv_dmc0[i][6] << - EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT) | - (exynos4210_clkdiv_dmc0[i][7] << - EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT)); - - data->dmc_divtable[i] = tmp; - } - - tmp = __raw_readl(EXYNOS4_CLKDIV_TOP); - for (i = LV_0; i < EX4210_LV_NUM; i++) { - tmp &= ~(EXYNOS4_CLKDIV_TOP_ACLK200_MASK | - EXYNOS4_CLKDIV_TOP_ACLK100_MASK | - EXYNOS4_CLKDIV_TOP_ACLK160_MASK | - EXYNOS4_CLKDIV_TOP_ACLK133_MASK | - EXYNOS4_CLKDIV_TOP_ONENAND_MASK); - - tmp |= ((exynos4210_clkdiv_top[i][0] << - EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT) | - (exynos4210_clkdiv_top[i][1] << - EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) | - (exynos4210_clkdiv_top[i][2] << - EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) | - (exynos4210_clkdiv_top[i][3] << - EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) | - (exynos4210_clkdiv_top[i][4] << - EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT)); - - data->top_divtable[i] = tmp; - } - - /* - * TODO: init tmp based on busfreq_data - * (device-tree or platform-data) - */ - tmp = 0; /* Max voltages for the reliability of the unknown */ - - pr_debug("ASV Group of Exynos4 is %d\n", tmp); - /* Use merged grouping for voltage */ - switch (tmp) { - case 0: - mgrp = 0; - break; - case 1: - case 2: - mgrp = 1; - break; - case 3: - case 4: - mgrp = 2; - break; - case 5: - case 6: - mgrp = 3; - break; - case 7: - mgrp = 4; - break; - default: - pr_warn("Unknown ASV Group. Use max voltage.\n"); - mgrp = 0; - } - - for (i = LV_0; i < EX4210_LV_NUM; i++) - exynos4210_busclk_table[i].volt = exynos4210_asv_volt[mgrp][i]; - - for (i = LV_0; i < EX4210_LV_NUM; i++) { - err = dev_pm_opp_add(data->dev, exynos4210_busclk_table[i].clk, - exynos4210_busclk_table[i].volt); - if (err) { - dev_err(data->dev, "Cannot add opp entries.\n"); - return err; - } - } - - - return 0; -} - -static int exynos4x12_init_tables(struct busfreq_data *data) -{ - unsigned int i; - unsigned int tmp; - int ret; - - /* Enable pause function for DREX2 DVFS */ - tmp = __raw_readl(EXYNOS4_DMC_PAUSE_CTRL); - tmp |= EXYNOS4_DMC_PAUSE_ENABLE; - __raw_writel(tmp, EXYNOS4_DMC_PAUSE_CTRL); - - tmp = __raw_readl(EXYNOS4_CLKDIV_DMC0); - - for (i = 0; i < EX4x12_LV_NUM; i++) { - tmp &= ~(EXYNOS4_CLKDIV_DMC0_ACP_MASK | - EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK | - EXYNOS4_CLKDIV_DMC0_DPHY_MASK | - EXYNOS4_CLKDIV_DMC0_DMC_MASK | - EXYNOS4_CLKDIV_DMC0_DMCD_MASK | - EXYNOS4_CLKDIV_DMC0_DMCP_MASK); - - tmp |= ((exynos4x12_clkdiv_dmc0[i][0] << - EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) | - (exynos4x12_clkdiv_dmc0[i][1] << - EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) | - (exynos4x12_clkdiv_dmc0[i][2] << - EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) | - (exynos4x12_clkdiv_dmc0[i][3] << - EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) | - (exynos4x12_clkdiv_dmc0[i][4] << - EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) | - (exynos4x12_clkdiv_dmc0[i][5] << - EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT)); - - data->dmc_divtable[i] = tmp; - } - - tmp = 0; /* Max voltages for the reliability of the unknown */ - - if (tmp > 8) - tmp = 0; - pr_debug("ASV Group of Exynos4x12 is %d\n", tmp); - - for (i = 0; i < EX4x12_LV_NUM; i++) { - exynos4x12_mifclk_table[i].volt = - exynos4x12_mif_step_50[tmp][i]; - exynos4x12_intclk_table[i].volt = - exynos4x12_int_volt[tmp][i]; - } - - for (i = 0; i < EX4x12_LV_NUM; i++) { - ret = dev_pm_opp_add(data->dev, exynos4x12_mifclk_table[i].clk, - exynos4x12_mifclk_table[i].volt); - if (ret) { - dev_err(data->dev, "Fail to add opp entries.\n"); - return ret; - } - } - - return 0; -} - -static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct busfreq_data *data = container_of(this, struct busfreq_data, - pm_notifier); - struct dev_pm_opp *opp; - struct busfreq_opp_info new_oppinfo; - unsigned long maxfreq = ULONG_MAX; - int err = 0; - - switch (event) { - case PM_SUSPEND_PREPARE: - /* Set Fastest and Deactivate DVFS */ - mutex_lock(&data->lock); - - data->disabled = true; - - rcu_read_lock(); - opp = dev_pm_opp_find_freq_floor(data->dev, &maxfreq); - if (IS_ERR(opp)) { - rcu_read_unlock(); - dev_err(data->dev, "%s: unable to find a min freq\n", - __func__); - mutex_unlock(&data->lock); - return PTR_ERR(opp); - } - new_oppinfo.rate = dev_pm_opp_get_freq(opp); - new_oppinfo.volt = dev_pm_opp_get_voltage(opp); - rcu_read_unlock(); - - err = exynos4_bus_setvolt(data, &new_oppinfo, - &data->curr_oppinfo); - if (err) - goto unlock; - - switch (data->type) { - case TYPE_BUSF_EXYNOS4210: - err = exynos4210_set_busclk(data, &new_oppinfo); - break; - case TYPE_BUSF_EXYNOS4x12: - err = exynos4x12_set_busclk(data, &new_oppinfo); - break; - default: - err = -EINVAL; - } - if (err) - goto unlock; - - data->curr_oppinfo = new_oppinfo; -unlock: - mutex_unlock(&data->lock); - if (err) - return err; - return NOTIFY_OK; - case PM_POST_RESTORE: - case PM_POST_SUSPEND: - /* Reactivate */ - mutex_lock(&data->lock); - data->disabled = false; - mutex_unlock(&data->lock); - return NOTIFY_OK; - } - - return NOTIFY_DONE; -} - -static int exynos4_busfreq_probe(struct platform_device *pdev) -{ - struct busfreq_data *data; - struct busfreq_ppmu_data *ppmu_data; - struct dev_pm_opp *opp; - struct device *dev = &pdev->dev; - int err = 0; - - data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data), GFP_KERNEL); - if (data == NULL) { - dev_err(dev, "Cannot allocate memory.\n"); - return -ENOMEM; - } - - ppmu_data = &data->ppmu_data; - ppmu_data->ppmu_end = PPMU_END; - ppmu_data->ppmu = devm_kzalloc(dev, - sizeof(struct exynos_ppmu) * PPMU_END, - GFP_KERNEL); - if (!ppmu_data->ppmu) { - dev_err(dev, "Failed to allocate memory for exynos_ppmu\n"); - return -ENOMEM; - } - - data->type = pdev->id_entry->driver_data; - ppmu_data->ppmu[PPMU_DMC0].hw_base = S5P_VA_DMC0; - ppmu_data->ppmu[PPMU_DMC1].hw_base = S5P_VA_DMC1; - data->pm_notifier.notifier_call = exynos4_busfreq_pm_notifier_event; - data->dev = dev; - mutex_init(&data->lock); - - switch (data->type) { - case TYPE_BUSF_EXYNOS4210: - err = exynos4210_init_tables(data); - break; - case TYPE_BUSF_EXYNOS4x12: - err = exynos4x12_init_tables(data); - break; - default: - dev_err(dev, "Cannot determine the device id %d\n", data->type); - err = -EINVAL; - } - if (err) { - dev_err(dev, "Cannot initialize busfreq table %d\n", - data->type); - return err; - } - - data->vdd_int = devm_regulator_get(dev, "vdd_int"); - if (IS_ERR(data->vdd_int)) { - dev_err(dev, "Cannot get the regulator \"vdd_int\"\n"); - return PTR_ERR(data->vdd_int); - } - if (data->type == TYPE_BUSF_EXYNOS4x12) { - data->vdd_mif = devm_regulator_get(dev, "vdd_mif"); - if (IS_ERR(data->vdd_mif)) { - dev_err(dev, "Cannot get the regulator \"vdd_mif\"\n"); - return PTR_ERR(data->vdd_mif); - } - } - - rcu_read_lock(); - opp = dev_pm_opp_find_freq_floor(dev, - &exynos4_devfreq_profile.initial_freq); - if (IS_ERR(opp)) { - rcu_read_unlock(); - dev_err(dev, "Invalid initial frequency %lu kHz.\n", - exynos4_devfreq_profile.initial_freq); - return PTR_ERR(opp); - } - data->curr_oppinfo.rate = dev_pm_opp_get_freq(opp); - data->curr_oppinfo.volt = dev_pm_opp_get_voltage(opp); - rcu_read_unlock(); - - platform_set_drvdata(pdev, data); - - data->devfreq = devm_devfreq_add_device(dev, &exynos4_devfreq_profile, - "simple_ondemand", NULL); - if (IS_ERR(data->devfreq)) - return PTR_ERR(data->devfreq); - - /* - * Start PPMU (Performance Profiling Monitoring Unit) to check - * utilization of each IP in the Exynos4 SoC. - */ - busfreq_mon_reset(ppmu_data); - - /* Register opp_notifier for Exynos4 busfreq */ - err = devm_devfreq_register_opp_notifier(dev, data->devfreq); - if (err < 0) { - dev_err(dev, "Failed to register opp notifier\n"); - return err; - } - - /* Register pm_notifier for Exynos4 busfreq */ - err = register_pm_notifier(&data->pm_notifier); - if (err) { - dev_err(dev, "Failed to setup pm notifier\n"); - return err; - } - - return 0; -} - -static int exynos4_busfreq_remove(struct platform_device *pdev) -{ - struct busfreq_data *data = platform_get_drvdata(pdev); - - /* Unregister all of notifier chain */ - unregister_pm_notifier(&data->pm_notifier); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int exynos4_busfreq_resume(struct device *dev) -{ - struct busfreq_data *data = dev_get_drvdata(dev); - struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; - - busfreq_mon_reset(ppmu_data); - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(exynos4_busfreq_pm_ops, NULL, exynos4_busfreq_resume); - -static const struct platform_device_id exynos4_busfreq_id[] = { - { "exynos4210-busfreq", TYPE_BUSF_EXYNOS4210 }, - { "exynos4412-busfreq", TYPE_BUSF_EXYNOS4x12 }, - { "exynos4212-busfreq", TYPE_BUSF_EXYNOS4x12 }, - { }, -}; - -static struct platform_driver exynos4_busfreq_driver = { - .probe = exynos4_busfreq_probe, - .remove = exynos4_busfreq_remove, - .id_table = exynos4_busfreq_id, - .driver = { - .name = "exynos4-busfreq", - .pm = &exynos4_busfreq_pm_ops, - }, -}; - -static int __init exynos4_busfreq_init(void) -{ - return platform_driver_register(&exynos4_busfreq_driver); -} -late_initcall(exynos4_busfreq_init); - -static void __exit exynos4_busfreq_exit(void) -{ - platform_driver_unregister(&exynos4_busfreq_driver); -} -module_exit(exynos4_busfreq_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("EXYNOS4 busfreq driver with devfreq framework"); -MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); diff --git a/drivers/devfreq/exynos/exynos4_bus.h b/drivers/devfreq/exynos/exynos4_bus.h deleted file mode 100644 index 94c73c18d28c..000000000000 --- a/drivers/devfreq/exynos/exynos4_bus.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * EXYNOS4 BUS header - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef __DEVFREQ_EXYNOS4_BUS_H -#define __DEVFREQ_EXYNOS4_BUS_H __FILE__ - -#include <mach/map.h> - -#define EXYNOS4_CLKDIV_LEFTBUS (S5P_VA_CMU + 0x04500) -#define EXYNOS4_CLKDIV_STAT_LEFTBUS (S5P_VA_CMU + 0x04600) - -#define EXYNOS4_CLKDIV_RIGHTBUS (S5P_VA_CMU + 0x08500) -#define EXYNOS4_CLKDIV_STAT_RIGHTBUS (S5P_VA_CMU + 0x08600) - -#define EXYNOS4_CLKDIV_TOP (S5P_VA_CMU + 0x0C510) -#define EXYNOS4_CLKDIV_CAM (S5P_VA_CMU + 0x0C520) -#define EXYNOS4_CLKDIV_MFC (S5P_VA_CMU + 0x0C528) - -#define EXYNOS4_CLKDIV_STAT_TOP (S5P_VA_CMU + 0x0C610) -#define EXYNOS4_CLKDIV_STAT_MFC (S5P_VA_CMU + 0x0C628) - -#define EXYNOS4210_CLKGATE_IP_IMAGE (S5P_VA_CMU + 0x0C930) -#define EXYNOS4212_CLKGATE_IP_IMAGE (S5P_VA_CMU + 0x04930) - -#define EXYNOS4_CLKDIV_DMC0 (S5P_VA_CMU + 0x10500) -#define EXYNOS4_CLKDIV_DMC1 (S5P_VA_CMU + 0x10504) -#define EXYNOS4_CLKDIV_STAT_DMC0 (S5P_VA_CMU + 0x10600) -#define EXYNOS4_CLKDIV_STAT_DMC1 (S5P_VA_CMU + 0x10604) - -#define EXYNOS4_DMC_PAUSE_CTRL (S5P_VA_CMU + 0x11094) -#define EXYNOS4_DMC_PAUSE_ENABLE (1 << 0) - -#define EXYNOS4_CLKDIV_DMC0_ACP_SHIFT (0) -#define EXYNOS4_CLKDIV_DMC0_ACP_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) -#define EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT (4) -#define EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) -#define EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT (8) -#define EXYNOS4_CLKDIV_DMC0_DPHY_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) -#define EXYNOS4_CLKDIV_DMC0_DMC_SHIFT (12) -#define EXYNOS4_CLKDIV_DMC0_DMC_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) -#define EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT (16) -#define EXYNOS4_CLKDIV_DMC0_DMCD_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) -#define EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT (20) -#define EXYNOS4_CLKDIV_DMC0_DMCP_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT) -#define EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT (24) -#define EXYNOS4_CLKDIV_DMC0_COPY2_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT) -#define EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT (28) -#define EXYNOS4_CLKDIV_DMC0_CORETI_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT) - -#define EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT (0) -#define EXYNOS4_CLKDIV_DMC1_G2D_ACP_MASK (0xf << EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT) -#define EXYNOS4_CLKDIV_DMC1_C2C_SHIFT (4) -#define EXYNOS4_CLKDIV_DMC1_C2C_MASK (0x7 << EXYNOS4_CLKDIV_DMC1_C2C_SHIFT) -#define EXYNOS4_CLKDIV_DMC1_PWI_SHIFT (8) -#define EXYNOS4_CLKDIV_DMC1_PWI_MASK (0xf << EXYNOS4_CLKDIV_DMC1_PWI_SHIFT) -#define EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT (12) -#define EXYNOS4_CLKDIV_DMC1_C2CACLK_MASK (0x7 << EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT) -#define EXYNOS4_CLKDIV_DMC1_DVSEM_SHIFT (16) -#define EXYNOS4_CLKDIV_DMC1_DVSEM_MASK (0x7f << EXYNOS4_CLKDIV_DMC1_DVSEM_SHIFT) -#define EXYNOS4_CLKDIV_DMC1_DPM_SHIFT (24) -#define EXYNOS4_CLKDIV_DMC1_DPM_MASK (0x7f << EXYNOS4_CLKDIV_DMC1_DPM_SHIFT) - -#define EXYNOS4_CLKDIV_MFC_SHIFT (0) -#define EXYNOS4_CLKDIV_MFC_MASK (0x7 << EXYNOS4_CLKDIV_MFC_SHIFT) - -#define EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT (0) -#define EXYNOS4_CLKDIV_TOP_ACLK200_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT) -#define EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT (4) -#define EXYNOS4_CLKDIV_TOP_ACLK100_MASK (0xF << EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) -#define EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT (8) -#define EXYNOS4_CLKDIV_TOP_ACLK160_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) -#define EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT (12) -#define EXYNOS4_CLKDIV_TOP_ACLK133_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) -#define EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT (16) -#define EXYNOS4_CLKDIV_TOP_ONENAND_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT) -#define EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT (20) -#define EXYNOS4_CLKDIV_TOP_ACLK266_GPS_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT) -#define EXYNOS4_CLKDIV_TOP_ACLK400_MCUISP_SHIFT (24) -#define EXYNOS4_CLKDIV_TOP_ACLK400_MCUISP_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK400_MCUISP_SHIFT) - -#define EXYNOS4_CLKDIV_BUS_GDLR_SHIFT (0) -#define EXYNOS4_CLKDIV_BUS_GDLR_MASK (0x7 << EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) -#define EXYNOS4_CLKDIV_BUS_GPLR_SHIFT (4) -#define EXYNOS4_CLKDIV_BUS_GPLR_MASK (0x7 << EXYNOS4_CLKDIV_BUS_GPLR_SHIFT) - -#define EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT (0) -#define EXYNOS4_CLKDIV_CAM_FIMC0_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT) -#define EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT (4) -#define EXYNOS4_CLKDIV_CAM_FIMC1_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT) -#define EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT (8) -#define EXYNOS4_CLKDIV_CAM_FIMC2_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT) -#define EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT (12) -#define EXYNOS4_CLKDIV_CAM_FIMC3_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT) - -#define EXYNOS4_CLKDIV_CAM1 (S5P_VA_CMU + 0x0C568) - -#define EXYNOS4_CLKDIV_STAT_CAM1 (S5P_VA_CMU + 0x0C668) - -#define EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT (0) -#define EXYNOS4_CLKDIV_CAM1_JPEG_MASK (0xf << EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT) - -#endif /* __DEVFREQ_EXYNOS4_BUS_H */ diff --git a/drivers/devfreq/exynos/exynos5_bus.c b/drivers/devfreq/exynos/exynos5_bus.c deleted file mode 100644 index 297ea30d4159..000000000000 --- a/drivers/devfreq/exynos/exynos5_bus.c +++ /dev/null @@ -1,431 +0,0 @@ -/* - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * EXYNOS5 INT clock frequency scaling support using DEVFREQ framework - * Based on work done by Jonghwan Choi <jhbird.choi@samsung.com> - * Support for only EXYNOS5250 is present. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include <linux/module.h> -#include <linux/devfreq.h> -#include <linux/io.h> -#include <linux/pm_opp.h> -#include <linux/slab.h> -#include <linux/suspend.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/platform_device.h> -#include <linux/pm_qos.h> -#include <linux/regulator/consumer.h> -#include <linux/of_address.h> -#include <linux/of_platform.h> - -#include "exynos_ppmu.h" - -#define MAX_SAFEVOLT 1100000 /* 1.10V */ -/* Assume that the bus is saturated if the utilization is 25% */ -#define INT_BUS_SATURATION_RATIO 25 - -enum int_level_idx { - LV_0, - LV_1, - LV_2, - LV_3, - LV_4, - _LV_END -}; - -enum exynos_ppmu_list { - PPMU_RIGHT, - PPMU_END, -}; - -struct busfreq_data_int { - struct device *dev; - struct devfreq *devfreq; - struct regulator *vdd_int; - struct busfreq_ppmu_data ppmu_data; - unsigned long curr_freq; - bool disabled; - - struct notifier_block pm_notifier; - struct mutex lock; - struct pm_qos_request int_req; - struct clk *int_clk; -}; - -struct int_bus_opp_table { - unsigned int idx; - unsigned long clk; - unsigned long volt; -}; - -static struct int_bus_opp_table exynos5_int_opp_table[] = { - {LV_0, 266000, 1025000}, - {LV_1, 200000, 1025000}, - {LV_2, 160000, 1025000}, - {LV_3, 133000, 1025000}, - {LV_4, 100000, 1025000}, - {0, 0, 0}, -}; - -static int exynos5_int_setvolt(struct busfreq_data_int *data, - unsigned long volt) -{ - return regulator_set_voltage(data->vdd_int, volt, MAX_SAFEVOLT); -} - -static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq, - u32 flags) -{ - int err = 0; - struct platform_device *pdev = container_of(dev, struct platform_device, - dev); - struct busfreq_data_int *data = platform_get_drvdata(pdev); - struct dev_pm_opp *opp; - unsigned long old_freq, freq; - unsigned long volt; - - rcu_read_lock(); - opp = devfreq_recommended_opp(dev, _freq, flags); - if (IS_ERR(opp)) { - rcu_read_unlock(); - dev_err(dev, "%s: Invalid OPP.\n", __func__); - return PTR_ERR(opp); - } - - freq = dev_pm_opp_get_freq(opp); - volt = dev_pm_opp_get_voltage(opp); - rcu_read_unlock(); - - old_freq = data->curr_freq; - - if (old_freq == freq) - return 0; - - dev_dbg(dev, "targeting %lukHz %luuV\n", freq, volt); - - mutex_lock(&data->lock); - - if (data->disabled) - goto out; - - if (freq > exynos5_int_opp_table[0].clk) - pm_qos_update_request(&data->int_req, freq * 16 / 1000); - else - pm_qos_update_request(&data->int_req, -1); - - if (old_freq < freq) - err = exynos5_int_setvolt(data, volt); - if (err) - goto out; - - err = clk_set_rate(data->int_clk, freq * 1000); - - if (err) - goto out; - - if (old_freq > freq) - err = exynos5_int_setvolt(data, volt); - if (err) - goto out; - - data->curr_freq = freq; -out: - mutex_unlock(&data->lock); - return err; -} - -static int exynos5_int_get_dev_status(struct device *dev, - struct devfreq_dev_status *stat) -{ - struct platform_device *pdev = container_of(dev, struct platform_device, - dev); - struct busfreq_data_int *data = platform_get_drvdata(pdev); - struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; - int busier_dmc; - - exynos_read_ppmu(ppmu_data); - busier_dmc = exynos_get_busier_ppmu(ppmu_data); - - stat->current_frequency = data->curr_freq; - - /* Number of cycles spent on memory access */ - stat->busy_time = ppmu_data->ppmu[busier_dmc].count[PPMU_PMNCNT3]; - stat->busy_time *= 100 / INT_BUS_SATURATION_RATIO; - stat->total_time = ppmu_data->ppmu[busier_dmc].ccnt; - - return 0; -} - -static struct devfreq_dev_profile exynos5_devfreq_int_profile = { - .initial_freq = 160000, - .polling_ms = 100, - .target = exynos5_busfreq_int_target, - .get_dev_status = exynos5_int_get_dev_status, -}; - -static int exynos5250_init_int_tables(struct busfreq_data_int *data) -{ - int i, err = 0; - - for (i = LV_0; i < _LV_END; i++) { - err = dev_pm_opp_add(data->dev, exynos5_int_opp_table[i].clk, - exynos5_int_opp_table[i].volt); - if (err) { - dev_err(data->dev, "Cannot add opp entries.\n"); - return err; - } - } - - return 0; -} - -static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct busfreq_data_int *data = container_of(this, - struct busfreq_data_int, pm_notifier); - struct dev_pm_opp *opp; - unsigned long maxfreq = ULONG_MAX; - unsigned long freq; - unsigned long volt; - int err = 0; - - switch (event) { - case PM_SUSPEND_PREPARE: - /* Set Fastest and Deactivate DVFS */ - mutex_lock(&data->lock); - - data->disabled = true; - - rcu_read_lock(); - opp = dev_pm_opp_find_freq_floor(data->dev, &maxfreq); - if (IS_ERR(opp)) { - rcu_read_unlock(); - err = PTR_ERR(opp); - goto unlock; - } - freq = dev_pm_opp_get_freq(opp); - volt = dev_pm_opp_get_voltage(opp); - rcu_read_unlock(); - - err = exynos5_int_setvolt(data, volt); - if (err) - goto unlock; - - err = clk_set_rate(data->int_clk, freq * 1000); - - if (err) - goto unlock; - - data->curr_freq = freq; -unlock: - mutex_unlock(&data->lock); - if (err) - return NOTIFY_BAD; - return NOTIFY_OK; - case PM_POST_RESTORE: - case PM_POST_SUSPEND: - /* Reactivate */ - mutex_lock(&data->lock); - data->disabled = false; - mutex_unlock(&data->lock); - return NOTIFY_OK; - } - - return NOTIFY_DONE; -} - -static int exynos5_busfreq_int_probe(struct platform_device *pdev) -{ - struct busfreq_data_int *data; - struct busfreq_ppmu_data *ppmu_data; - struct dev_pm_opp *opp; - struct device *dev = &pdev->dev; - struct device_node *np; - unsigned long initial_freq; - unsigned long initial_volt; - int err = 0; - int i; - - data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data_int), - GFP_KERNEL); - if (data == NULL) { - dev_err(dev, "Cannot allocate memory.\n"); - return -ENOMEM; - } - - ppmu_data = &data->ppmu_data; - ppmu_data->ppmu_end = PPMU_END; - ppmu_data->ppmu = devm_kzalloc(dev, - sizeof(struct exynos_ppmu) * PPMU_END, - GFP_KERNEL); - if (!ppmu_data->ppmu) { - dev_err(dev, "Failed to allocate memory for exynos_ppmu\n"); - return -ENOMEM; - } - - np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-ppmu"); - if (np == NULL) { - pr_err("Unable to find PPMU node\n"); - return -ENOENT; - } - - for (i = 0; i < ppmu_data->ppmu_end; i++) { - /* map PPMU memory region */ - ppmu_data->ppmu[i].hw_base = of_iomap(np, i); - if (ppmu_data->ppmu[i].hw_base == NULL) { - dev_err(&pdev->dev, "failed to map memory region\n"); - return -ENOMEM; - } - } - data->pm_notifier.notifier_call = exynos5_busfreq_int_pm_notifier_event; - data->dev = dev; - mutex_init(&data->lock); - - err = exynos5250_init_int_tables(data); - if (err) - return err; - - data->vdd_int = devm_regulator_get(dev, "vdd_int"); - if (IS_ERR(data->vdd_int)) { - dev_err(dev, "Cannot get the regulator \"vdd_int\"\n"); - return PTR_ERR(data->vdd_int); - } - - data->int_clk = devm_clk_get(dev, "int_clk"); - if (IS_ERR(data->int_clk)) { - dev_err(dev, "Cannot get clock \"int_clk\"\n"); - return PTR_ERR(data->int_clk); - } - - rcu_read_lock(); - opp = dev_pm_opp_find_freq_floor(dev, - &exynos5_devfreq_int_profile.initial_freq); - if (IS_ERR(opp)) { - rcu_read_unlock(); - dev_err(dev, "Invalid initial frequency %lu kHz.\n", - exynos5_devfreq_int_profile.initial_freq); - return PTR_ERR(opp); - } - initial_freq = dev_pm_opp_get_freq(opp); - initial_volt = dev_pm_opp_get_voltage(opp); - rcu_read_unlock(); - data->curr_freq = initial_freq; - - err = clk_set_rate(data->int_clk, initial_freq * 1000); - if (err) { - dev_err(dev, "Failed to set initial frequency\n"); - return err; - } - - err = exynos5_int_setvolt(data, initial_volt); - if (err) - return err; - - platform_set_drvdata(pdev, data); - - busfreq_mon_reset(ppmu_data); - - data->devfreq = devm_devfreq_add_device(dev, &exynos5_devfreq_int_profile, - "simple_ondemand", NULL); - if (IS_ERR(data->devfreq)) - return PTR_ERR(data->devfreq); - - err = devm_devfreq_register_opp_notifier(dev, data->devfreq); - if (err < 0) { - dev_err(dev, "Failed to register opp notifier\n"); - return err; - } - - err = register_pm_notifier(&data->pm_notifier); - if (err) { - dev_err(dev, "Failed to setup pm notifier\n"); - return err; - } - - /* TODO: Add a new QOS class for int/mif bus */ - pm_qos_add_request(&data->int_req, PM_QOS_NETWORK_THROUGHPUT, -1); - - return 0; -} - -static int exynos5_busfreq_int_remove(struct platform_device *pdev) -{ - struct busfreq_data_int *data = platform_get_drvdata(pdev); - - pm_qos_remove_request(&data->int_req); - unregister_pm_notifier(&data->pm_notifier); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int exynos5_busfreq_int_resume(struct device *dev) -{ - struct platform_device *pdev = container_of(dev, struct platform_device, - dev); - struct busfreq_data_int *data = platform_get_drvdata(pdev); - struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; - - busfreq_mon_reset(ppmu_data); - return 0; -} -static const struct dev_pm_ops exynos5_busfreq_int_pm = { - .resume = exynos5_busfreq_int_resume, -}; -#endif -static SIMPLE_DEV_PM_OPS(exynos5_busfreq_int_pm_ops, NULL, - exynos5_busfreq_int_resume); - -/* platform device pointer for exynos5 devfreq device. */ -static struct platform_device *exynos5_devfreq_pdev; - -static struct platform_driver exynos5_busfreq_int_driver = { - .probe = exynos5_busfreq_int_probe, - .remove = exynos5_busfreq_int_remove, - .driver = { - .name = "exynos5-bus-int", - .pm = &exynos5_busfreq_int_pm_ops, - }, -}; - -static int __init exynos5_busfreq_int_init(void) -{ - int ret; - - ret = platform_driver_register(&exynos5_busfreq_int_driver); - if (ret < 0) - goto out; - - exynos5_devfreq_pdev = - platform_device_register_simple("exynos5-bus-int", -1, NULL, 0); - if (IS_ERR(exynos5_devfreq_pdev)) { - ret = PTR_ERR(exynos5_devfreq_pdev); - goto out1; - } - - return 0; -out1: - platform_driver_unregister(&exynos5_busfreq_int_driver); -out: - return ret; -} -late_initcall(exynos5_busfreq_int_init); - -static void __exit exynos5_busfreq_int_exit(void) -{ - platform_device_unregister(exynos5_devfreq_pdev); - platform_driver_unregister(&exynos5_busfreq_int_driver); -} -module_exit(exynos5_busfreq_int_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("EXYNOS5 busfreq driver with devfreq framework"); diff --git a/drivers/devfreq/exynos/exynos_ppmu.c b/drivers/devfreq/exynos/exynos_ppmu.c deleted file mode 100644 index 97b75e513d29..000000000000 --- a/drivers/devfreq/exynos/exynos_ppmu.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * EXYNOS - PPMU support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/io.h> - -#include "exynos_ppmu.h" - -void exynos_ppmu_reset(void __iomem *ppmu_base) -{ - __raw_writel(PPMU_CYCLE_RESET | PPMU_COUNTER_RESET, ppmu_base); - __raw_writel(PPMU_ENABLE_CYCLE | - PPMU_ENABLE_COUNT0 | - PPMU_ENABLE_COUNT1 | - PPMU_ENABLE_COUNT2 | - PPMU_ENABLE_COUNT3, - ppmu_base + PPMU_CNTENS); -} - -void exynos_ppmu_setevent(void __iomem *ppmu_base, unsigned int ch, - unsigned int evt) -{ - __raw_writel(evt, ppmu_base + PPMU_BEVTSEL(ch)); -} - -void exynos_ppmu_start(void __iomem *ppmu_base) -{ - __raw_writel(PPMU_ENABLE, ppmu_base); -} - -void exynos_ppmu_stop(void __iomem *ppmu_base) -{ - __raw_writel(PPMU_DISABLE, ppmu_base); -} - -unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch) -{ - unsigned int total; - - if (ch == PPMU_PMNCNT3) - total = ((__raw_readl(ppmu_base + PMCNT_OFFSET(ch)) << 8) | - __raw_readl(ppmu_base + PMCNT_OFFSET(ch + 1))); - else - total = __raw_readl(ppmu_base + PMCNT_OFFSET(ch)); - - return total; -} - -void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data) -{ - unsigned int i; - - for (i = 0; i < ppmu_data->ppmu_end; i++) { - void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base; - - /* Reset the performance and cycle counters */ - exynos_ppmu_reset(ppmu_base); - - /* Setup count registers to monitor read/write transactions */ - ppmu_data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT; - exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3, - ppmu_data->ppmu[i].event[PPMU_PMNCNT3]); - - exynos_ppmu_start(ppmu_base); - } -} -EXPORT_SYMBOL(busfreq_mon_reset); - -void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data) -{ - int i, j; - - for (i = 0; i < ppmu_data->ppmu_end; i++) { - void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base; - - exynos_ppmu_stop(ppmu_base); - - /* Update local data from PPMU */ - ppmu_data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT); - - for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { - if (ppmu_data->ppmu[i].event[j] == 0) - ppmu_data->ppmu[i].count[j] = 0; - else - ppmu_data->ppmu[i].count[j] = - exynos_ppmu_read(ppmu_base, j); - } - } - - busfreq_mon_reset(ppmu_data); -} -EXPORT_SYMBOL(exynos_read_ppmu); - -int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data) -{ - unsigned int count = 0; - int i, j, busy = 0; - - for (i = 0; i < ppmu_data->ppmu_end; i++) { - for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { - if (ppmu_data->ppmu[i].count[j] > count) { - count = ppmu_data->ppmu[i].count[j]; - busy = i; - } - } - } - - return busy; -} -EXPORT_SYMBOL(exynos_get_busier_ppmu); diff --git a/drivers/devfreq/exynos/exynos_ppmu.h b/drivers/devfreq/exynos/exynos_ppmu.h deleted file mode 100644 index 71f17ba3563c..000000000000 --- a/drivers/devfreq/exynos/exynos_ppmu.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * EXYNOS PPMU header - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef __DEVFREQ_EXYNOS_PPMU_H -#define __DEVFREQ_EXYNOS_PPMU_H __FILE__ - -#include <linux/ktime.h> - -/* For PPMU Control */ -#define PPMU_ENABLE BIT(0) -#define PPMU_DISABLE 0x0 -#define PPMU_CYCLE_RESET BIT(1) -#define PPMU_COUNTER_RESET BIT(2) - -#define PPMU_ENABLE_COUNT0 BIT(0) -#define PPMU_ENABLE_COUNT1 BIT(1) -#define PPMU_ENABLE_COUNT2 BIT(2) -#define PPMU_ENABLE_COUNT3 BIT(3) -#define PPMU_ENABLE_CYCLE BIT(31) - -#define PPMU_CNTENS 0x10 -#define PPMU_FLAG 0x50 -#define PPMU_CCNT_OVERFLOW BIT(31) -#define PPMU_CCNT 0x100 - -#define PPMU_PMCNT0 0x110 -#define PPMU_PMCNT_OFFSET 0x10 -#define PMCNT_OFFSET(x) (PPMU_PMCNT0 + (PPMU_PMCNT_OFFSET * x)) - -#define PPMU_BEVT0SEL 0x1000 -#define PPMU_BEVTSEL_OFFSET 0x100 -#define PPMU_BEVTSEL(x) (PPMU_BEVT0SEL + (ch * PPMU_BEVTSEL_OFFSET)) - -/* For Event Selection */ -#define RD_DATA_COUNT 0x5 -#define WR_DATA_COUNT 0x6 -#define RDWR_DATA_COUNT 0x7 - -enum ppmu_counter { - PPMU_PMNCNT0, - PPMU_PMCCNT1, - PPMU_PMNCNT2, - PPMU_PMNCNT3, - PPMU_PMNCNT_MAX, -}; - -struct bus_opp_table { - unsigned int idx; - unsigned long clk; - unsigned long volt; -}; - -struct exynos_ppmu { - void __iomem *hw_base; - unsigned int ccnt; - unsigned int event[PPMU_PMNCNT_MAX]; - unsigned int count[PPMU_PMNCNT_MAX]; - unsigned long long ns; - ktime_t reset_time; - bool ccnt_overflow; - bool count_overflow[PPMU_PMNCNT_MAX]; -}; - -struct busfreq_ppmu_data { - struct exynos_ppmu *ppmu; - int ppmu_end; -}; - -void exynos_ppmu_reset(void __iomem *ppmu_base); -void exynos_ppmu_setevent(void __iomem *ppmu_base, unsigned int ch, - unsigned int evt); -void exynos_ppmu_start(void __iomem *ppmu_base); -void exynos_ppmu_stop(void __iomem *ppmu_base); -unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch); -void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data); -void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data); -int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data); -#endif /* __DEVFREQ_EXYNOS_PPMU_H */ diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c new file mode 100644 index 000000000000..9ef46e2592c4 --- /dev/null +++ b/drivers/devfreq/governor_passive.c @@ -0,0 +1,205 @@ +/* + * linux/drivers/devfreq/governor_passive.c + * + * Copyright (C) 2016 Samsung Electronics + * Author: Chanwoo Choi <cw00.choi@samsung.com> + * Author: MyungJoo Ham <myungjoo.ham@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/devfreq.h> +#include "governor.h" + +static int devfreq_passive_get_target_freq(struct devfreq *devfreq, + unsigned long *freq) +{ + struct devfreq_passive_data *p_data + = (struct devfreq_passive_data *)devfreq->data; + struct devfreq *parent_devfreq = (struct devfreq *)p_data->parent; + unsigned long child_freq = ULONG_MAX; + struct dev_pm_opp *opp; + int i, count, ret = 0; + + /* + * If the devfreq device with passive governor has the specific method + * to determine the next frequency, should use the get_target_freq() + * of struct devfreq_passive_data. + */ + if (p_data->get_target_freq) { + ret = p_data->get_target_freq(devfreq, freq); + goto out; + } + + /* + * If the parent and passive devfreq device uses the OPP table, + * get the next frequency by using the OPP table. + */ + + /* + * - parent devfreq device uses the governors except for passive. + * - passive devfreq device uses the passive governor. + * + * Each devfreq has the OPP table. After deciding the new frequency + * from the governor of parent devfreq device, the passive governor + * need to get the index of new frequency on OPP table of parent + * device. And then the index is used for getting the suitable + * new frequency for passive devfreq device. + */ + if (!devfreq->profile || !devfreq->profile->freq_table + || devfreq->profile->max_state <= 0) + return -EINVAL; + + /* + * The passive governor have to get the correct frequency from OPP + * list of parent device. Because in this case, *freq is temporary + * value which is decided by ondemand governor. + */ + rcu_read_lock(); + opp = devfreq_recommended_opp(parent_devfreq->dev.parent, freq, 0); + rcu_read_unlock(); + if (IS_ERR(opp)) { + ret = PTR_ERR(opp); + goto out; + } + + /* + * Get the OPP table's index of decided freqeuncy by governor + * of parent device. + */ + for (i = 0; i < parent_devfreq->profile->max_state; i++) + if (parent_devfreq->profile->freq_table[i] == *freq) + break; + + if (i == parent_devfreq->profile->max_state) { + ret = -EINVAL; + goto out; + } + + /* Get the suitable frequency by using index of parent device. */ + if (i < devfreq->profile->max_state) { + child_freq = devfreq->profile->freq_table[i]; + } else { + count = devfreq->profile->max_state; + child_freq = devfreq->profile->freq_table[count - 1]; + } + + /* Return the suitable frequency for passive device. */ + *freq = child_freq; + +out: + return ret; +} + +static int update_devfreq_passive(struct devfreq *devfreq, unsigned long freq) +{ + int ret; + + if (!devfreq->governor) + return -EINVAL; + + mutex_lock_nested(&devfreq->lock, SINGLE_DEPTH_NESTING); + + ret = devfreq->governor->get_target_freq(devfreq, &freq); + if (ret < 0) + goto out; + + ret = devfreq->profile->target(devfreq->dev.parent, &freq, 0); + if (ret < 0) + goto out; + + devfreq->previous_freq = freq; + +out: + mutex_unlock(&devfreq->lock); + + return 0; +} + +static int devfreq_passive_notifier_call(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct devfreq_passive_data *data + = container_of(nb, struct devfreq_passive_data, nb); + struct devfreq *devfreq = (struct devfreq *)data->this; + struct devfreq *parent = (struct devfreq *)data->parent; + struct devfreq_freqs *freqs = (struct devfreq_freqs *)ptr; + unsigned long freq = freqs->new; + + switch (event) { + case DEVFREQ_PRECHANGE: + if (parent->previous_freq > freq) + update_devfreq_passive(devfreq, freq); + break; + case DEVFREQ_POSTCHANGE: + if (parent->previous_freq < freq) + update_devfreq_passive(devfreq, freq); + break; + } + + return NOTIFY_DONE; +} + +static int devfreq_passive_event_handler(struct devfreq *devfreq, + unsigned int event, void *data) +{ + struct device *dev = devfreq->dev.parent; + struct devfreq_passive_data *p_data + = (struct devfreq_passive_data *)devfreq->data; + struct devfreq *parent = (struct devfreq *)p_data->parent; + struct notifier_block *nb = &p_data->nb; + int ret = 0; + + if (!parent) + return -EPROBE_DEFER; + + switch (event) { + case DEVFREQ_GOV_START: + if (!p_data->this) + p_data->this = devfreq; + + nb->notifier_call = devfreq_passive_notifier_call; + ret = devm_devfreq_register_notifier(dev, parent, nb, + DEVFREQ_TRANSITION_NOTIFIER); + break; + case DEVFREQ_GOV_STOP: + devm_devfreq_unregister_notifier(dev, parent, nb, + DEVFREQ_TRANSITION_NOTIFIER); + break; + default: + break; + } + + return ret; +} + +static struct devfreq_governor devfreq_passive = { + .name = "passive", + .get_target_freq = devfreq_passive_get_target_freq, + .event_handler = devfreq_passive_event_handler, +}; + +static int __init devfreq_passive_init(void) +{ + return devfreq_add_governor(&devfreq_passive); +} +subsys_initcall(devfreq_passive_init); + +static void __exit devfreq_passive_exit(void) +{ + int ret; + + ret = devfreq_remove_governor(&devfreq_passive); + if (ret) + pr_err("%s: failed remove governor %d\n", __func__, ret); +} +module_exit(devfreq_passive_exit); + +MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); +MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); +MODULE_DESCRIPTION("DEVFREQ Passive governor"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 01087a38da22..792bdae2b91d 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1866,7 +1866,7 @@ static int i7core_mce_check_error(struct notifier_block *nb, unsigned long val, i7_dev = get_i7core_dev(mce->socketid); if (!i7_dev) - return NOTIFY_BAD; + return NOTIFY_DONE; mci = i7_dev->mci; pvt = mci->pvt_info; diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index 468447aff8eb..8bf745d2da7e 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -3168,7 +3168,7 @@ static int sbridge_mce_check_error(struct notifier_block *nb, unsigned long val, mci = get_mci_for_node_id(mce->socketid); if (!mci) - return NOTIFY_BAD; + return NOTIFY_DONE; pvt = mci->pvt_info; /* diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index 0ac594c0a234..34b741940494 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c @@ -202,29 +202,44 @@ static const struct variable_validate variable_validate[] = { { NULL_GUID, "", NULL }, }; +/* + * Check if @var_name matches the pattern given in @match_name. + * + * @var_name: an array of @len non-NUL characters. + * @match_name: a NUL-terminated pattern string, optionally ending in "*". A + * final "*" character matches any trailing characters @var_name, + * including the case when there are none left in @var_name. + * @match: on output, the number of non-wildcard characters in @match_name + * that @var_name matches, regardless of the return value. + * @return: whether @var_name fully matches @match_name. + */ static bool variable_matches(const char *var_name, size_t len, const char *match_name, int *match) { for (*match = 0; ; (*match)++) { char c = match_name[*match]; - char u = var_name[*match]; - /* Wildcard in the matching name means we've matched */ - if (c == '*') + switch (c) { + case '*': + /* Wildcard in @match_name means we've matched. */ return true; - /* Case sensitive match */ - if (!c && *match == len) - return true; + case '\0': + /* @match_name has ended. Has @var_name too? */ + return (*match == len); - if (c != u) + default: + /* + * We've reached a non-wildcard char in @match_name. + * Continue only if there's an identical character in + * @var_name. + */ + if (*match < len && c == var_name[*match]) + continue; return false; - - if (!c) - return true; + } } - return true; } bool diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c index 11bfee8b79a9..fa4ea22ca12e 100644 --- a/drivers/firmware/psci.c +++ b/drivers/firmware/psci.c @@ -355,12 +355,12 @@ int psci_cpu_suspend_enter(unsigned long index) /* ARM specific CPU idle operations */ #ifdef CONFIG_ARM -static struct cpuidle_ops psci_cpuidle_ops __initdata = { +static const struct cpuidle_ops psci_cpuidle_ops __initconst = { .suspend = psci_cpu_suspend_enter, .init = psci_dt_cpu_init_idle, }; -CPUIDLE_METHOD_OF_DECLARE(psci, "arm,psci", &psci_cpuidle_ops); +CPUIDLE_METHOD_OF_DECLARE(psci, "psci", &psci_cpuidle_ops); #endif #endif diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 815c4a5cae54..1b95475b6aef 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -77,7 +77,7 @@ static inline u16 fw_cfg_sel_endianness(u16 key) static inline void fw_cfg_read_blob(u16 key, void *buf, loff_t pos, size_t count) { - u32 glk; + u32 glk = -1U; acpi_status status; /* If we have ACPI, ensure mutual exclusion against any potential diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index d9ab0cd1d205..4d9a315cfd43 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -196,44 +196,6 @@ static int gpio_rcar_irq_set_wake(struct irq_data *d, unsigned int on) return 0; } -static void gpio_rcar_irq_bus_lock(struct irq_data *d) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct gpio_rcar_priv *p = gpiochip_get_data(gc); - - pm_runtime_get_sync(&p->pdev->dev); -} - -static void gpio_rcar_irq_bus_sync_unlock(struct irq_data *d) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct gpio_rcar_priv *p = gpiochip_get_data(gc); - - pm_runtime_put(&p->pdev->dev); -} - - -static int gpio_rcar_irq_request_resources(struct irq_data *d) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct gpio_rcar_priv *p = gpiochip_get_data(gc); - int error; - - error = pm_runtime_get_sync(&p->pdev->dev); - if (error < 0) - return error; - - return 0; -} - -static void gpio_rcar_irq_release_resources(struct irq_data *d) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct gpio_rcar_priv *p = gpiochip_get_data(gc); - - pm_runtime_put(&p->pdev->dev); -} - static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id) { struct gpio_rcar_priv *p = dev_id; @@ -280,32 +242,18 @@ static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip, static int gpio_rcar_request(struct gpio_chip *chip, unsigned offset) { - struct gpio_rcar_priv *p = gpiochip_get_data(chip); - int error; - - error = pm_runtime_get_sync(&p->pdev->dev); - if (error < 0) - return error; - - error = pinctrl_request_gpio(chip->base + offset); - if (error) - pm_runtime_put(&p->pdev->dev); - - return error; + return pinctrl_request_gpio(chip->base + offset); } static void gpio_rcar_free(struct gpio_chip *chip, unsigned offset) { - struct gpio_rcar_priv *p = gpiochip_get_data(chip); - pinctrl_free_gpio(chip->base + offset); - /* Set the GPIO as an input to ensure that the next GPIO request won't + /* + * Set the GPIO as an input to ensure that the next GPIO request won't * drive the GPIO pin as an output. */ gpio_rcar_config_general_input_output_mode(chip, offset, false); - - pm_runtime_put(&p->pdev->dev); } static int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset) @@ -452,6 +400,7 @@ static int gpio_rcar_probe(struct platform_device *pdev) } pm_runtime_enable(dev); + pm_runtime_get_sync(dev); io = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); @@ -488,10 +437,6 @@ static int gpio_rcar_probe(struct platform_device *pdev) irq_chip->irq_unmask = gpio_rcar_irq_enable; irq_chip->irq_set_type = gpio_rcar_irq_set_type; irq_chip->irq_set_wake = gpio_rcar_irq_set_wake; - irq_chip->irq_bus_lock = gpio_rcar_irq_bus_lock; - irq_chip->irq_bus_sync_unlock = gpio_rcar_irq_bus_sync_unlock; - irq_chip->irq_request_resources = gpio_rcar_irq_request_resources; - irq_chip->irq_release_resources = gpio_rcar_irq_release_resources; irq_chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND; ret = gpiochip_add_data(gpio_chip, p); @@ -522,6 +467,7 @@ static int gpio_rcar_probe(struct platform_device *pdev) err1: gpiochip_remove(gpio_chip); err0: + pm_runtime_put(dev); pm_runtime_disable(dev); return ret; } @@ -532,6 +478,7 @@ static int gpio_rcar_remove(struct platform_device *pdev) gpiochip_remove(&p->gpio_chip); + pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); return 0; } diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 682070d20f00..2dc52585e3f2 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -977,7 +977,7 @@ bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id) lookup = kmalloc(sizeof(*lookup), GFP_KERNEL); if (lookup) { lookup->adev = adev; - lookup->con_id = con_id; + lookup->con_id = kstrdup(con_id, GFP_KERNEL); list_add_tail(&lookup->node, &acpi_crs_lookup_list); } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c index 0020a0ea43ff..35a1248aaa77 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c @@ -63,10 +63,6 @@ bool amdgpu_has_atpx(void) { return amdgpu_atpx_priv.atpx_detected; } -bool amdgpu_has_atpx_dgpu_power_cntl(void) { - return amdgpu_atpx_priv.atpx.functions.power_cntl; -} - /** * amdgpu_atpx_call - call an ATPX method * @@ -146,6 +142,13 @@ static void amdgpu_atpx_parse_functions(struct amdgpu_atpx_functions *f, u32 mas */ static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx) { + /* make sure required functions are enabled */ + /* dGPU power control is required */ + if (atpx->functions.power_cntl == false) { + printk("ATPX dGPU power cntl not present, forcing\n"); + atpx->functions.power_cntl = true; + } + if (atpx->functions.px_params) { union acpi_object *info; struct atpx_px_params output; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 612117478b57..2139da773da6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -62,12 +62,6 @@ static const char *amdgpu_asic_name[] = { "LAST", }; -#if defined(CONFIG_VGA_SWITCHEROO) -bool amdgpu_has_atpx_dgpu_power_cntl(void); -#else -static inline bool amdgpu_has_atpx_dgpu_power_cntl(void) { return false; } -#endif - bool amdgpu_device_is_px(struct drm_device *dev) { struct amdgpu_device *adev = dev->dev_private; @@ -1485,7 +1479,7 @@ int amdgpu_device_init(struct amdgpu_device *adev, if (amdgpu_runtime_pm == 1) runtime = true; - if (amdgpu_device_is_px(ddev) && amdgpu_has_atpx_dgpu_power_cntl()) + if (amdgpu_device_is_px(ddev)) runtime = true; vga_switcheroo_register_client(adev->pdev, &amdgpu_switcheroo_ops, runtime); if (runtime) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index e557fc1f17c8..7ecea83ce453 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -541,6 +541,7 @@ int amdgpu_bo_set_metadata (struct amdgpu_bo *bo, void *metadata, if (!metadata_size) { if (bo->metadata_size) { kfree(bo->metadata); + bo->metadata = NULL; bo->metadata_size = 0; } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c b/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c index 1e0bba29e167..1cd6de575305 100644 --- a/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c +++ b/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c @@ -298,6 +298,10 @@ bool amdgpu_atombios_encoder_mode_fixup(struct drm_encoder *encoder, && (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2))) adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2; + /* vertical FP must be at least 1 */ + if (mode->crtc_vsync_start == mode->crtc_vdisplay) + adjusted_mode->crtc_vsync_start++; + /* get the native mode for scaling */ if (amdgpu_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) amdgpu_panel_mode_fixup(encoder, adjusted_mode); diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c index 05b0353d3880..a4a2e6cc61bb 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c @@ -910,7 +910,10 @@ static int gmc_v7_0_late_init(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; - return amdgpu_irq_get(adev, &adev->mc.vm_fault, 0); + if (amdgpu_vm_fault_stop != AMDGPU_VM_FAULT_STOP_ALWAYS) + return amdgpu_irq_get(adev, &adev->mc.vm_fault, 0); + else + return 0; } static int gmc_v7_0_sw_init(void *handle) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c index 02deb3229405..7a9db2c72c89 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c @@ -870,7 +870,10 @@ static int gmc_v8_0_late_init(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; - return amdgpu_irq_get(adev, &adev->mc.vm_fault, 0); + if (amdgpu_vm_fault_stop != AMDGPU_VM_FAULT_STOP_ALWAYS) + return amdgpu_irq_get(adev, &adev->mc.vm_fault, 0); + else + return 0; } #define mmMC_SEQ_MISC0_FIJI 0xA71 diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index e17fbdaf874b..71ea0521ea96 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1796,6 +1796,11 @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr) req_payload.start_slot = cur_slots; if (mgr->proposed_vcpis[i]) { port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi); + port = drm_dp_get_validated_port_ref(mgr, port); + if (!port) { + mutex_unlock(&mgr->payload_lock); + return -EINVAL; + } req_payload.num_slots = mgr->proposed_vcpis[i]->num_slots; req_payload.vcpi = mgr->proposed_vcpis[i]->vcpi; } else { @@ -1823,6 +1828,9 @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr) mgr->payloads[i].payload_state = req_payload.payload_state; } cur_slots += req_payload.num_slots; + + if (port) + drm_dp_put_port(port); } for (i = 0; i < mgr->max_payloads; i++) { @@ -2128,6 +2136,8 @@ int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr) if (mgr->mst_primary) { int sret; + u8 guid[16]; + sret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE); if (sret != DP_RECEIVER_CAP_SIZE) { DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n"); @@ -2142,6 +2152,16 @@ int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr) ret = -1; goto out_unlock; } + + /* Some hubs forget their guids after they resume */ + sret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16); + if (sret != 16) { + DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n"); + ret = -1; + goto out_unlock; + } + drm_dp_check_mstb_guid(mgr->mst_primary, guid); + ret = 0; } else ret = -1; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 09198d0b5814..306dde18a94a 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -572,6 +572,24 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) goto fail; } + /* + * Set the GPU linear window to be at the end of the DMA window, where + * the CMA area is likely to reside. This ensures that we are able to + * map the command buffers while having the linear window overlap as + * much RAM as possible, so we can optimize mappings for other buffers. + * + * For 3D cores only do this if MC2.0 is present, as with MC1.0 it leads + * to different views of the memory on the individual engines. + */ + if (!(gpu->identity.features & chipFeatures_PIPE_3D) || + (gpu->identity.minor_features0 & chipMinorFeatures0_MC20)) { + u32 dma_mask = (u32)dma_get_required_mask(gpu->dev); + if (dma_mask < PHYS_OFFSET + SZ_2G) + gpu->memory_base = PHYS_OFFSET; + else + gpu->memory_base = dma_mask - SZ_2G + 1; + } + ret = etnaviv_hw_reset(gpu); if (ret) goto fail; @@ -1566,7 +1584,6 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct etnaviv_gpu *gpu; - u32 dma_mask; int err = 0; gpu = devm_kzalloc(dev, sizeof(*gpu), GFP_KERNEL); @@ -1576,18 +1593,6 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) gpu->dev = &pdev->dev; mutex_init(&gpu->lock); - /* - * Set the GPU linear window to be at the end of the DMA window, where - * the CMA area is likely to reside. This ensures that we are able to - * map the command buffers while having the linear window overlap as - * much RAM as possible, so we can optimize mappings for other buffers. - */ - dma_mask = (u32)dma_get_required_mask(dev); - if (dma_mask < PHYS_OFFSET + SZ_2G) - gpu->memory_base = PHYS_OFFSET; - else - gpu->memory_base = dma_mask - SZ_2G + 1; - /* Map registers: */ gpu->mmio = etnaviv_ioremap(pdev, NULL, dev_name(gpu->dev)); if (IS_ERR(gpu->mmio)) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 30798cbc6fc0..6d2fb3f4ac62 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -792,7 +792,7 @@ static int i915_drm_resume(struct drm_device *dev) static int i915_drm_resume_early(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - int ret = 0; + int ret; /* * We have a resume ordering issue with the snd-hda driver also @@ -803,6 +803,36 @@ static int i915_drm_resume_early(struct drm_device *dev) * FIXME: This should be solved with a special hdmi sink device or * similar so that power domains can be employed. */ + + /* + * Note that we need to set the power state explicitly, since we + * powered off the device during freeze and the PCI core won't power + * it back up for us during thaw. Powering off the device during + * freeze is not a hard requirement though, and during the + * suspend/resume phases the PCI core makes sure we get here with the + * device powered on. So in case we change our freeze logic and keep + * the device powered we can also remove the following set power state + * call. + */ + ret = pci_set_power_state(dev->pdev, PCI_D0); + if (ret) { + DRM_ERROR("failed to set PCI D0 power state (%d)\n", ret); + goto out; + } + + /* + * Note that pci_enable_device() first enables any parent bridge + * device and only then sets the power state for this device. The + * bridge enabling is a nop though, since bridge devices are resumed + * first. The order of enabling power and enabling the device is + * imposed by the PCI core as described above, so here we preserve the + * same order for the freeze/thaw phases. + * + * TODO: eventually we should remove pci_disable_device() / + * pci_enable_enable_device() from suspend/resume. Due to how they + * depend on the device enable refcount we can't anyway depend on them + * disabling/enabling the device. + */ if (pci_enable_device(dev->pdev)) { ret = -EIO; goto out; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index f76cbf3e5d1e..fffdac801d3b 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2907,7 +2907,14 @@ enum skl_disp_power_wells { #define GEN6_RP_STATE_CAP _MMIO(MCHBAR_MIRROR_BASE_SNB + 0x5998) #define BXT_RP_STATE_CAP _MMIO(0x138170) -#define INTERVAL_1_28_US(us) (((us) * 100) >> 7) +/* + * Make these a multiple of magic 25 to avoid SNB (eg. Dell XPS + * 8300) freezing up around GPU hangs. Looks as if even + * scheduling/timer interrupts start misbehaving if the RPS + * EI/thresholds are "bad", leading to a very sluggish or even + * frozen machine. + */ +#define INTERVAL_1_28_US(us) roundup(((us) * 100) >> 7, 25) #define INTERVAL_1_33_US(us) (((us) * 3) >> 2) #define INTERVAL_0_833_US(us) (((us) * 6) / 5) #define GT_INTERVAL_FROM_US(dev_priv, us) (IS_GEN9(dev_priv) ? \ diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 62de9f4bce09..3b57bf06abe8 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -443,9 +443,17 @@ void intel_prepare_ddi_buffer(struct intel_encoder *encoder) } else if (IS_BROADWELL(dev_priv)) { ddi_translations_fdi = bdw_ddi_translations_fdi; ddi_translations_dp = bdw_ddi_translations_dp; - ddi_translations_edp = bdw_ddi_translations_edp; + + if (dev_priv->edp_low_vswing) { + ddi_translations_edp = bdw_ddi_translations_edp; + n_edp_entries = ARRAY_SIZE(bdw_ddi_translations_edp); + } else { + ddi_translations_edp = bdw_ddi_translations_dp; + n_edp_entries = ARRAY_SIZE(bdw_ddi_translations_dp); + } + ddi_translations_hdmi = bdw_ddi_translations_hdmi; - n_edp_entries = ARRAY_SIZE(bdw_ddi_translations_edp); + n_dp_entries = ARRAY_SIZE(bdw_ddi_translations_dp); n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi); hdmi_default_entry = 7; @@ -3201,12 +3209,6 @@ void intel_ddi_get_config(struct intel_encoder *encoder, intel_ddi_clock_get(encoder, pipe_config); } -static void intel_ddi_destroy(struct drm_encoder *encoder) -{ - /* HDMI has nothing special to destroy, so we can go with this. */ - intel_dp_encoder_destroy(encoder); -} - static bool intel_ddi_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config) { @@ -3225,7 +3227,8 @@ static bool intel_ddi_compute_config(struct intel_encoder *encoder, } static const struct drm_encoder_funcs intel_ddi_funcs = { - .destroy = intel_ddi_destroy, + .reset = intel_dp_encoder_reset, + .destroy = intel_dp_encoder_destroy, }; static struct intel_connector * @@ -3324,6 +3327,7 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_encoder->post_disable = intel_ddi_post_disable; intel_encoder->get_hw_state = intel_ddi_get_hw_state; intel_encoder->get_config = intel_ddi_get_config; + intel_encoder->suspend = intel_dp_encoder_suspend; intel_dig_port->port = port; intel_dig_port->saved_port_bits = I915_READ(DDI_BUF_CTL(port)) & diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6e0d8283daa6..182f84937345 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13351,6 +13351,9 @@ static int intel_atomic_prepare_commit(struct drm_device *dev, } for_each_crtc_in_state(state, crtc, crtc_state, i) { + if (state->legacy_cursor_update) + continue; + ret = intel_crtc_wait_for_pending_flips(crtc); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f069a82deb57..412a34c39522 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4898,7 +4898,7 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder) kfree(intel_dig_port); } -static void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder) +void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base); @@ -4940,7 +4940,7 @@ static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp) edp_panel_vdd_schedule_off(intel_dp); } -static void intel_dp_encoder_reset(struct drm_encoder *encoder) +void intel_dp_encoder_reset(struct drm_encoder *encoder) { struct intel_dp *intel_dp; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 4c027d69fac9..7d3af3a72abe 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1238,6 +1238,8 @@ void intel_dp_set_link_params(struct intel_dp *intel_dp, void intel_dp_start_link_train(struct intel_dp *intel_dp); void intel_dp_stop_link_train(struct intel_dp *intel_dp); void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode); +void intel_dp_encoder_reset(struct drm_encoder *encoder); +void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder); void intel_dp_encoder_destroy(struct drm_encoder *encoder); int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc); bool intel_dp_compute_config(struct intel_encoder *encoder, diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index a0d8daed2470..1ab6f687f640 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1415,8 +1415,16 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) hdmi_to_dig_port(intel_hdmi)); } - if (!live_status) - DRM_DEBUG_KMS("Live status not up!"); + if (!live_status) { + DRM_DEBUG_KMS("HDMI live status down\n"); + /* + * Live status register is not reliable on all intel platforms. + * So consider live_status only for certain platforms, for + * others, read EDID to determine presence of sink. + */ + if (INTEL_INFO(dev_priv)->gen < 7 || IS_IVYBRIDGE(dev_priv)) + live_status = true; + } intel_hdmi_unset_edid(connector); diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index edd05cdb0cd8..587cae4e73c9 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -310,6 +310,10 @@ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder, && (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2))) adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2; + /* vertical FP must be at least 1 */ + if (mode->crtc_vsync_start == mode->crtc_vdisplay) + adjusted_mode->crtc_vsync_start++; + /* get the native mode for scaling */ if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) { radeon_panel_mode_fixup(encoder, adjusted_mode); diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 76c4bdf21b20..34f7a29d9366 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -2608,10 +2608,152 @@ static void evergreen_agp_enable(struct radeon_device *rdev) WREG32(VM_CONTEXT1_CNTL, 0); } +static const unsigned ni_dig_offsets[] = +{ + NI_DIG0_REGISTER_OFFSET, + NI_DIG1_REGISTER_OFFSET, + NI_DIG2_REGISTER_OFFSET, + NI_DIG3_REGISTER_OFFSET, + NI_DIG4_REGISTER_OFFSET, + NI_DIG5_REGISTER_OFFSET +}; + +static const unsigned ni_tx_offsets[] = +{ + NI_DCIO_UNIPHY0_UNIPHY_TX_CONTROL1, + NI_DCIO_UNIPHY1_UNIPHY_TX_CONTROL1, + NI_DCIO_UNIPHY2_UNIPHY_TX_CONTROL1, + NI_DCIO_UNIPHY3_UNIPHY_TX_CONTROL1, + NI_DCIO_UNIPHY4_UNIPHY_TX_CONTROL1, + NI_DCIO_UNIPHY5_UNIPHY_TX_CONTROL1 +}; + +static const unsigned evergreen_dp_offsets[] = +{ + EVERGREEN_DP0_REGISTER_OFFSET, + EVERGREEN_DP1_REGISTER_OFFSET, + EVERGREEN_DP2_REGISTER_OFFSET, + EVERGREEN_DP3_REGISTER_OFFSET, + EVERGREEN_DP4_REGISTER_OFFSET, + EVERGREEN_DP5_REGISTER_OFFSET +}; + + +/* + * Assumption is that EVERGREEN_CRTC_MASTER_EN enable for requested crtc + * We go from crtc to connector and it is not relible since it + * should be an opposite direction .If crtc is enable then + * find the dig_fe which selects this crtc and insure that it enable. + * if such dig_fe is found then find dig_be which selects found dig_be and + * insure that it enable and in DP_SST mode. + * if UNIPHY_PLL_CONTROL1.enable then we should disconnect timing + * from dp symbols clocks . + */ +static bool evergreen_is_dp_sst_stream_enabled(struct radeon_device *rdev, + unsigned crtc_id, unsigned *ret_dig_fe) +{ + unsigned i; + unsigned dig_fe; + unsigned dig_be; + unsigned dig_en_be; + unsigned uniphy_pll; + unsigned digs_fe_selected; + unsigned dig_be_mode; + unsigned dig_fe_mask; + bool is_enabled = false; + bool found_crtc = false; + + /* loop through all running dig_fe to find selected crtc */ + for (i = 0; i < ARRAY_SIZE(ni_dig_offsets); i++) { + dig_fe = RREG32(NI_DIG_FE_CNTL + ni_dig_offsets[i]); + if (dig_fe & NI_DIG_FE_CNTL_SYMCLK_FE_ON && + crtc_id == NI_DIG_FE_CNTL_SOURCE_SELECT(dig_fe)) { + /* found running pipe */ + found_crtc = true; + dig_fe_mask = 1 << i; + dig_fe = i; + break; + } + } + + if (found_crtc) { + /* loop through all running dig_be to find selected dig_fe */ + for (i = 0; i < ARRAY_SIZE(ni_dig_offsets); i++) { + dig_be = RREG32(NI_DIG_BE_CNTL + ni_dig_offsets[i]); + /* if dig_fe_selected by dig_be? */ + digs_fe_selected = NI_DIG_BE_CNTL_FE_SOURCE_SELECT(dig_be); + dig_be_mode = NI_DIG_FE_CNTL_MODE(dig_be); + if (dig_fe_mask & digs_fe_selected && + /* if dig_be in sst mode? */ + dig_be_mode == NI_DIG_BE_DPSST) { + dig_en_be = RREG32(NI_DIG_BE_EN_CNTL + + ni_dig_offsets[i]); + uniphy_pll = RREG32(NI_DCIO_UNIPHY0_PLL_CONTROL1 + + ni_tx_offsets[i]); + /* dig_be enable and tx is running */ + if (dig_en_be & NI_DIG_BE_EN_CNTL_ENABLE && + dig_en_be & NI_DIG_BE_EN_CNTL_SYMBCLK_ON && + uniphy_pll & NI_DCIO_UNIPHY0_PLL_CONTROL1_ENABLE) { + is_enabled = true; + *ret_dig_fe = dig_fe; + break; + } + } + } + } + + return is_enabled; +} + +/* + * Blank dig when in dp sst mode + * Dig ignores crtc timing + */ +static void evergreen_blank_dp_output(struct radeon_device *rdev, + unsigned dig_fe) +{ + unsigned stream_ctrl; + unsigned fifo_ctrl; + unsigned counter = 0; + + if (dig_fe >= ARRAY_SIZE(evergreen_dp_offsets)) { + DRM_ERROR("invalid dig_fe %d\n", dig_fe); + return; + } + + stream_ctrl = RREG32(EVERGREEN_DP_VID_STREAM_CNTL + + evergreen_dp_offsets[dig_fe]); + if (!(stream_ctrl & EVERGREEN_DP_VID_STREAM_CNTL_ENABLE)) { + DRM_ERROR("dig %d , should be enable\n", dig_fe); + return; + } + + stream_ctrl &=~EVERGREEN_DP_VID_STREAM_CNTL_ENABLE; + WREG32(EVERGREEN_DP_VID_STREAM_CNTL + + evergreen_dp_offsets[dig_fe], stream_ctrl); + + stream_ctrl = RREG32(EVERGREEN_DP_VID_STREAM_CNTL + + evergreen_dp_offsets[dig_fe]); + while (counter < 32 && stream_ctrl & EVERGREEN_DP_VID_STREAM_STATUS) { + msleep(1); + counter++; + stream_ctrl = RREG32(EVERGREEN_DP_VID_STREAM_CNTL + + evergreen_dp_offsets[dig_fe]); + } + if (counter >= 32 ) + DRM_ERROR("counter exceeds %d\n", counter); + + fifo_ctrl = RREG32(EVERGREEN_DP_STEER_FIFO + evergreen_dp_offsets[dig_fe]); + fifo_ctrl |= EVERGREEN_DP_STEER_FIFO_RESET; + WREG32(EVERGREEN_DP_STEER_FIFO + evergreen_dp_offsets[dig_fe], fifo_ctrl); + +} + void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save) { u32 crtc_enabled, tmp, frame_count, blackout; int i, j; + unsigned dig_fe; if (!ASIC_IS_NODCE(rdev)) { save->vga_render_control = RREG32(VGA_RENDER_CONTROL); @@ -2651,7 +2793,17 @@ void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *sav break; udelay(1); } - + /*we should disable dig if it drives dp sst*/ + /*but we are in radeon_device_init and the topology is unknown*/ + /*and it is available after radeon_modeset_init*/ + /*the following method radeon_atom_encoder_dpms_dig*/ + /*does the job if we initialize it properly*/ + /*for now we do it this manually*/ + /**/ + if (ASIC_IS_DCE5(rdev) && + evergreen_is_dp_sst_stream_enabled(rdev, i ,&dig_fe)) + evergreen_blank_dp_output(rdev, dig_fe); + /*we could remove 6 lines below*/ /* XXX this is a hack to avoid strange behavior with EFI on certain systems */ WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1); tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]); diff --git a/drivers/gpu/drm/radeon/evergreen_reg.h b/drivers/gpu/drm/radeon/evergreen_reg.h index aa939dfed3a3..b436badf9efa 100644 --- a/drivers/gpu/drm/radeon/evergreen_reg.h +++ b/drivers/gpu/drm/radeon/evergreen_reg.h @@ -250,8 +250,43 @@ /* HDMI blocks at 0x7030, 0x7c30, 0x10830, 0x11430, 0x12030, 0x12c30 */ #define EVERGREEN_HDMI_BASE 0x7030 +/*DIG block*/ +#define NI_DIG0_REGISTER_OFFSET (0x7000 - 0x7000) +#define NI_DIG1_REGISTER_OFFSET (0x7C00 - 0x7000) +#define NI_DIG2_REGISTER_OFFSET (0x10800 - 0x7000) +#define NI_DIG3_REGISTER_OFFSET (0x11400 - 0x7000) +#define NI_DIG4_REGISTER_OFFSET (0x12000 - 0x7000) +#define NI_DIG5_REGISTER_OFFSET (0x12C00 - 0x7000) + + +#define NI_DIG_FE_CNTL 0x7000 +# define NI_DIG_FE_CNTL_SOURCE_SELECT(x) ((x) & 0x3) +# define NI_DIG_FE_CNTL_SYMCLK_FE_ON (1<<24) + + +#define NI_DIG_BE_CNTL 0x7140 +# define NI_DIG_BE_CNTL_FE_SOURCE_SELECT(x) (((x) >> 8 ) & 0x3F) +# define NI_DIG_FE_CNTL_MODE(x) (((x) >> 16) & 0x7 ) + +#define NI_DIG_BE_EN_CNTL 0x7144 +# define NI_DIG_BE_EN_CNTL_ENABLE (1 << 0) +# define NI_DIG_BE_EN_CNTL_SYMBCLK_ON (1 << 8) +# define NI_DIG_BE_DPSST 0 /* Display Port block */ +#define EVERGREEN_DP0_REGISTER_OFFSET (0x730C - 0x730C) +#define EVERGREEN_DP1_REGISTER_OFFSET (0x7F0C - 0x730C) +#define EVERGREEN_DP2_REGISTER_OFFSET (0x10B0C - 0x730C) +#define EVERGREEN_DP3_REGISTER_OFFSET (0x1170C - 0x730C) +#define EVERGREEN_DP4_REGISTER_OFFSET (0x1230C - 0x730C) +#define EVERGREEN_DP5_REGISTER_OFFSET (0x12F0C - 0x730C) + + +#define EVERGREEN_DP_VID_STREAM_CNTL 0x730C +# define EVERGREEN_DP_VID_STREAM_CNTL_ENABLE (1 << 0) +# define EVERGREEN_DP_VID_STREAM_STATUS (1 <<16) +#define EVERGREEN_DP_STEER_FIFO 0x7310 +# define EVERGREEN_DP_STEER_FIFO_RESET (1 << 0) #define EVERGREEN_DP_SEC_CNTL 0x7280 # define EVERGREEN_DP_SEC_STREAM_ENABLE (1 << 0) # define EVERGREEN_DP_SEC_ASP_ENABLE (1 << 4) @@ -266,4 +301,15 @@ # define EVERGREEN_DP_SEC_N_BASE_MULTIPLE(x) (((x) & 0xf) << 24) # define EVERGREEN_DP_SEC_SS_EN (1 << 28) +/*DCIO_UNIPHY block*/ +#define NI_DCIO_UNIPHY0_UNIPHY_TX_CONTROL1 (0x6600 -0x6600) +#define NI_DCIO_UNIPHY1_UNIPHY_TX_CONTROL1 (0x6640 -0x6600) +#define NI_DCIO_UNIPHY2_UNIPHY_TX_CONTROL1 (0x6680 - 0x6600) +#define NI_DCIO_UNIPHY3_UNIPHY_TX_CONTROL1 (0x66C0 - 0x6600) +#define NI_DCIO_UNIPHY4_UNIPHY_TX_CONTROL1 (0x6700 - 0x6600) +#define NI_DCIO_UNIPHY5_UNIPHY_TX_CONTROL1 (0x6740 - 0x6600) + +#define NI_DCIO_UNIPHY0_PLL_CONTROL1 0x6618 +# define NI_DCIO_UNIPHY0_PLL_CONTROL1_ENABLE (1 << 0) + #endif diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 4cbf26555093..e3daafa1be13 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -230,22 +230,13 @@ EXPORT_SYMBOL(ttm_bo_del_sub_from_lru); void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo) { - struct ttm_bo_device *bdev = bo->bdev; - struct ttm_mem_type_manager *man; + int put_count = 0; lockdep_assert_held(&bo->resv->lock.base); - if (bo->mem.placement & TTM_PL_FLAG_NO_EVICT) { - list_del_init(&bo->swap); - list_del_init(&bo->lru); - - } else { - if (bo->ttm && !(bo->ttm->page_flags & TTM_PAGE_FLAG_SG)) - list_move_tail(&bo->swap, &bo->glob->swap_lru); - - man = &bdev->man[bo->mem.mem_type]; - list_move_tail(&bo->lru, &man->lru); - } + put_count = ttm_bo_del_from_lru(bo); + ttm_bo_list_ref_sub(bo, put_count, true); + ttm_bo_add_to_lru(bo); } EXPORT_SYMBOL(ttm_bo_move_to_lru_tail); diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index 4854dac87e24..5fd1fd06effc 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -267,11 +267,23 @@ static int virtio_gpu_crtc_atomic_check(struct drm_crtc *crtc, return 0; } +static void virtio_gpu_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + unsigned long flags; + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + if (crtc->state->event) + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); +} + static const struct drm_crtc_helper_funcs virtio_gpu_crtc_helper_funcs = { .enable = virtio_gpu_crtc_enable, .disable = virtio_gpu_crtc_disable, .mode_set_nofb = virtio_gpu_crtc_mode_set_nofb, .atomic_check = virtio_gpu_crtc_atomic_check, + .atomic_flush = virtio_gpu_crtc_atomic_flush, }; static void virtio_gpu_enc_mode_set(struct drm_encoder *encoder, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 723ba16c6084..1a1a87cbf109 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -3293,19 +3293,19 @@ static const struct vmw_cmd_entry vmw_cmd_entries[SVGA_3D_CMD_MAX] = { &vmw_cmd_dx_cid_check, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_QUERY, &vmw_cmd_dx_define_query, true, false, true), - VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_QUERY, &vmw_cmd_ok, + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_QUERY, &vmw_cmd_dx_cid_check, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_BIND_QUERY, &vmw_cmd_dx_bind_query, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_QUERY_OFFSET, - &vmw_cmd_ok, true, false, true), - VMW_CMD_DEF(SVGA_3D_CMD_DX_BEGIN_QUERY, &vmw_cmd_ok, + &vmw_cmd_dx_cid_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_BEGIN_QUERY, &vmw_cmd_dx_cid_check, true, false, true), - VMW_CMD_DEF(SVGA_3D_CMD_DX_END_QUERY, &vmw_cmd_ok, + VMW_CMD_DEF(SVGA_3D_CMD_DX_END_QUERY, &vmw_cmd_dx_cid_check, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_READBACK_QUERY, &vmw_cmd_invalid, true, false, true), - VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_PREDICATION, &vmw_cmd_invalid, + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_PREDICATION, &vmw_cmd_dx_cid_check, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_VIEWPORTS, &vmw_cmd_dx_cid_check, true, false, true), diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index 3b1faf7862a5..679a4cb98ee3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -573,9 +573,9 @@ static int vmw_fb_set_par(struct fb_info *info) mode = old_mode; old_mode = NULL; } else if (!vmw_kms_validate_mode_vram(vmw_priv, - mode->hdisplay * - (var->bits_per_pixel + 7) / 8, - mode->vdisplay)) { + mode->hdisplay * + DIV_ROUND_UP(var->bits_per_pixel, 8), + mode->vdisplay)) { drm_mode_destroy(vmw_priv->dev, mode); return -EINVAL; } diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index e00db3f510dd..abb98c77bad2 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -1068,7 +1068,6 @@ static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) goto err_register; } - pdev->dev.of_node = of_node; pdev->dev.parent = dev; ret = platform_device_add_data(pdev, ®->pdata, @@ -1079,6 +1078,12 @@ static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) platform_device_put(pdev); goto err_register; } + + /* + * Set of_node only after calling platform_device_add. Otherwise + * the platform:imx-ipuv3-crtc modalias won't be used. + */ + pdev->dev.of_node = of_node; } return 0; diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index c6eaff5f8845..0238f0169e48 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -259,6 +259,7 @@ #define USB_DEVICE_ID_CORSAIR_K90 0x1b02 #define USB_VENDOR_ID_CREATIVELABS 0x041e +#define USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51 0x322c #define USB_DEVICE_ID_PRODIKEYS_PCMIDI 0x2801 #define USB_VENDOR_ID_CVTOUCH 0x1ff7 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index ed2f68edc8f1..53fc856d6867 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -71,6 +71,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL }, + { USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_ELAN, HID_ANY_ID, HID_QUIRK_ALWAYS_POLL }, diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 02c4efea241c..cf2ba43453fd 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -684,6 +684,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) wacom->tool[idx] = wacom_intuos_get_tool_type(wacom->id[idx]); + wacom->shared->stylus_in_proximity = true; return 1; } @@ -3395,6 +3396,10 @@ static const struct wacom_features wacom_features_0x33E = { "Wacom Intuos PT M 2", 21600, 13500, 2047, 63, INTUOSHT2, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16, .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x343 = + { "Wacom DTK1651", 34616, 19559, 1023, 0, + DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4, + WACOM_DTU_OFFSET, WACOM_DTU_OFFSET }; static const struct wacom_features wacom_features_HID_ANY_ID = { "Wacom HID", .type = HID_GENERIC }; @@ -3560,6 +3565,7 @@ const struct hid_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x33C) }, { USB_DEVICE_WACOM(0x33D) }, { USB_DEVICE_WACOM(0x33E) }, + { USB_DEVICE_WACOM(0x343) }, { USB_DEVICE_WACOM(0x4001) }, { USB_DEVICE_WACOM(0x4004) }, { USB_DEVICE_WACOM(0x5000) }, diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 5613e2b5cff7..a40a73a7b71d 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -103,15 +103,29 @@ static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi) * there is room for the producer to send the pending packet. */ -static bool hv_need_to_signal_on_read(u32 prev_write_sz, - struct hv_ring_buffer_info *rbi) +static bool hv_need_to_signal_on_read(struct hv_ring_buffer_info *rbi) { u32 cur_write_sz; u32 r_size; - u32 write_loc = rbi->ring_buffer->write_index; + u32 write_loc; u32 read_loc = rbi->ring_buffer->read_index; - u32 pending_sz = rbi->ring_buffer->pending_send_sz; + u32 pending_sz; + /* + * Issue a full memory barrier before making the signaling decision. + * Here is the reason for having this barrier: + * If the reading of the pend_sz (in this function) + * were to be reordered and read before we commit the new read + * index (in the calling function) we could + * have a problem. If the host were to set the pending_sz after we + * have sampled pending_sz and go to sleep before we commit the + * read index, we could miss sending the interrupt. Issue a full + * memory barrier to address this. + */ + mb(); + + pending_sz = rbi->ring_buffer->pending_send_sz; + write_loc = rbi->ring_buffer->write_index; /* If the other end is not blocked on write don't bother. */ if (pending_sz == 0) return false; @@ -120,7 +134,7 @@ static bool hv_need_to_signal_on_read(u32 prev_write_sz, cur_write_sz = write_loc >= read_loc ? r_size - (write_loc - read_loc) : read_loc - write_loc; - if ((prev_write_sz < pending_sz) && (cur_write_sz >= pending_sz)) + if (cur_write_sz >= pending_sz) return true; return false; @@ -455,7 +469,7 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, /* Update the read index */ hv_set_next_read_location(inring_info, next_read_location); - *signal = hv_need_to_signal_on_read(bytes_avail_towrite, inring_info); + *signal = hv_need_to_signal_on_read(inring_info); return ret; } diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index faa8e6821fea..0967e1a5b3a2 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -975,10 +975,10 @@ config I2C_XLR config I2C_XLP9XX tristate "XLP9XX I2C support" - depends on CPU_XLP || COMPILE_TEST + depends on CPU_XLP || ARCH_VULCAN || COMPILE_TEST help This driver enables support for the on-chip I2C interface of - the Broadcom XLP9xx/XLP5xx MIPS processors. + the Broadcom XLP9xx/XLP5xx MIPS and Vulcan ARM64 processors. This driver can also be built as a module. If so, the module will be called i2c-xlp9xx. diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index 714bdc837769..b167ab25310a 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -116,8 +116,8 @@ struct cpm_i2c { cbd_t __iomem *rbase; u_char *txbuf[CPM_MAXBD]; u_char *rxbuf[CPM_MAXBD]; - u32 txdma[CPM_MAXBD]; - u32 rxdma[CPM_MAXBD]; + dma_addr_t txdma[CPM_MAXBD]; + dma_addr_t rxdma[CPM_MAXBD]; }; static irqreturn_t cpm_i2c_interrupt(int irq, void *dev_id) diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index b29c7500461a..f54ece8fce78 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -671,7 +671,9 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap, return -EIO; } - clk_prepare_enable(i2c->clk); + ret = clk_enable(i2c->clk); + if (ret) + return ret; for (i = 0; i < num; i++, msgs++) { stop = (i == num - 1); @@ -695,7 +697,7 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap, } out: - clk_disable_unprepare(i2c->clk); + clk_disable(i2c->clk); return ret; } @@ -747,7 +749,9 @@ static int exynos5_i2c_probe(struct platform_device *pdev) return -ENOENT; } - clk_prepare_enable(i2c->clk); + ret = clk_prepare_enable(i2c->clk); + if (ret) + return ret; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); i2c->regs = devm_ioremap_resource(&pdev->dev, mem); @@ -799,6 +803,10 @@ static int exynos5_i2c_probe(struct platform_device *pdev) platform_set_drvdata(pdev, i2c); + clk_disable(i2c->clk); + + return 0; + err_clk: clk_disable_unprepare(i2c->clk); return ret; @@ -810,6 +818,8 @@ static int exynos5_i2c_remove(struct platform_device *pdev) i2c_del_adapter(&i2c->adap); + clk_unprepare(i2c->clk); + return 0; } @@ -821,6 +831,8 @@ static int exynos5_i2c_suspend_noirq(struct device *dev) i2c->suspended = 1; + clk_unprepare(i2c->clk); + return 0; } @@ -830,7 +842,9 @@ static int exynos5_i2c_resume_noirq(struct device *dev) struct exynos5_i2c *i2c = platform_get_drvdata(pdev); int ret = 0; - clk_prepare_enable(i2c->clk); + ret = clk_prepare_enable(i2c->clk); + if (ret) + return ret; ret = exynos5_hsi2c_clock_setup(i2c); if (ret) { @@ -839,7 +853,7 @@ static int exynos5_i2c_resume_noirq(struct device *dev) } exynos5_i2c_init(i2c); - clk_disable_unprepare(i2c->clk); + clk_disable(i2c->clk); i2c->suspended = 0; return 0; diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c index 7ba795b24e75..1c8707710098 100644 --- a/drivers/i2c/busses/i2c-ismt.c +++ b/drivers/i2c/busses/i2c-ismt.c @@ -75,6 +75,7 @@ /* PCI DIDs for the Intel SMBus Message Transport (SMT) Devices */ #define PCI_DEVICE_ID_INTEL_S1200_SMT0 0x0c59 #define PCI_DEVICE_ID_INTEL_S1200_SMT1 0x0c5a +#define PCI_DEVICE_ID_INTEL_DNV_SMT 0x19ac #define PCI_DEVICE_ID_INTEL_AVOTON_SMT 0x1f15 #define ISMT_DESC_ENTRIES 2 /* number of descriptor entries */ @@ -180,6 +181,7 @@ struct ismt_priv { static const struct pci_device_id ismt_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT0) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DNV_SMT) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMT) }, { 0, } }; diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 9096d17beb5b..3dcc5f3f26cb 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -855,6 +855,7 @@ static struct rk3x_i2c_soc_data soc_data[3] = { static const struct of_device_id rk3x_i2c_match[] = { { .compatible = "rockchip,rk3066-i2c", .data = (void *)&soc_data[0] }, { .compatible = "rockchip,rk3188-i2c", .data = (void *)&soc_data[1] }, + { .compatible = "rockchip,rk3228-i2c", .data = (void *)&soc_data[2] }, { .compatible = "rockchip,rk3288-i2c", .data = (void *)&soc_data[2] }, {}, }; diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index c6935de425fa..c96649292b55 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -766,6 +766,67 @@ static struct cpuidle_state knl_cstates[] = { .enter = NULL } }; +static struct cpuidle_state bxt_cstates[] = { + { + .name = "C1-BXT", + .desc = "MWAIT 0x00", + .flags = MWAIT2flg(0x00), + .exit_latency = 2, + .target_residency = 2, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, + { + .name = "C1E-BXT", + .desc = "MWAIT 0x01", + .flags = MWAIT2flg(0x01), + .exit_latency = 10, + .target_residency = 20, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, + { + .name = "C6-BXT", + .desc = "MWAIT 0x20", + .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 133, + .target_residency = 133, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, + { + .name = "C7s-BXT", + .desc = "MWAIT 0x31", + .flags = MWAIT2flg(0x31) | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 155, + .target_residency = 155, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, + { + .name = "C8-BXT", + .desc = "MWAIT 0x40", + .flags = MWAIT2flg(0x40) | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 1000, + .target_residency = 1000, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, + { + .name = "C9-BXT", + .desc = "MWAIT 0x50", + .flags = MWAIT2flg(0x50) | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 2000, + .target_residency = 2000, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, + { + .name = "C10-BXT", + .desc = "MWAIT 0x60", + .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 10000, + .target_residency = 10000, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, + { + .enter = NULL } +}; + /** * intel_idle * @dev: cpuidle_device @@ -950,6 +1011,11 @@ static const struct idle_cpu idle_cpu_knl = { .state_table = knl_cstates, }; +static const struct idle_cpu idle_cpu_bxt = { + .state_table = bxt_cstates, + .disable_promotion_to_c1e = true, +}; + #define ICPU(model, cpu) \ { X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&cpu } @@ -985,6 +1051,7 @@ static const struct x86_cpu_id intel_idle_ids[] __initconst = { ICPU(0x9e, idle_cpu_skl), ICPU(0x55, idle_cpu_skx), ICPU(0x57, idle_cpu_knl), + ICPU(0x5c, idle_cpu_bxt), {} }; MODULE_DEVICE_TABLE(x86cpu, intel_idle_ids); @@ -1075,6 +1142,73 @@ static void ivt_idle_state_table_update(void) /* else, 1 and 2 socket systems use default ivt_cstates */ } + +/* + * Translate IRTL (Interrupt Response Time Limit) MSR to usec + */ + +static unsigned int irtl_ns_units[] = { + 1, 32, 1024, 32768, 1048576, 33554432, 0, 0 }; + +static unsigned long long irtl_2_usec(unsigned long long irtl) +{ + unsigned long long ns; + + ns = irtl_ns_units[(irtl >> 10) & 0x3]; + + return div64_u64((irtl & 0x3FF) * ns, 1000); +} +/* + * bxt_idle_state_table_update(void) + * + * On BXT, we trust the IRTL to show the definitive maximum latency + * We use the same value for target_residency. + */ +static void bxt_idle_state_table_update(void) +{ + unsigned long long msr; + + rdmsrl(MSR_PKGC6_IRTL, msr); + if (msr) { + unsigned int usec = irtl_2_usec(msr); + + bxt_cstates[2].exit_latency = usec; + bxt_cstates[2].target_residency = usec; + } + + rdmsrl(MSR_PKGC7_IRTL, msr); + if (msr) { + unsigned int usec = irtl_2_usec(msr); + + bxt_cstates[3].exit_latency = usec; + bxt_cstates[3].target_residency = usec; + } + + rdmsrl(MSR_PKGC8_IRTL, msr); + if (msr) { + unsigned int usec = irtl_2_usec(msr); + + bxt_cstates[4].exit_latency = usec; + bxt_cstates[4].target_residency = usec; + } + + rdmsrl(MSR_PKGC9_IRTL, msr); + if (msr) { + unsigned int usec = irtl_2_usec(msr); + + bxt_cstates[5].exit_latency = usec; + bxt_cstates[5].target_residency = usec; + } + + rdmsrl(MSR_PKGC10_IRTL, msr); + if (msr) { + unsigned int usec = irtl_2_usec(msr); + + bxt_cstates[6].exit_latency = usec; + bxt_cstates[6].target_residency = usec; + } + +} /* * sklh_idle_state_table_update(void) * @@ -1130,6 +1264,9 @@ static void intel_idle_state_table_update(void) case 0x3e: /* IVT */ ivt_idle_state_table_update(); break; + case 0x5c: /* BXT */ + bxt_idle_state_table_update(); + break; case 0x5e: /* SKL-H */ sklh_idle_state_table_update(); break; diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index dbee13ad33a3..2e154cb51685 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -451,6 +451,8 @@ static int at91_adc_probe(struct platform_device *pdev) if (ret) goto vref_disable; + platform_set_drvdata(pdev, indio_dev); + ret = iio_device_register(indio_dev); if (ret < 0) goto per_clk_disable_unprepare; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index f581256d9d4c..5ee4e0dc093e 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -104,6 +104,19 @@ static int inv_mpu6050_deselect_bypass(struct i2c_adapter *adap, return 0; } +static const char *inv_mpu_match_acpi_device(struct device *dev, int *chip_id) +{ + const struct acpi_device_id *id; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return NULL; + + *chip_id = (int)id->driver_data; + + return dev_name(dev); +} + /** * inv_mpu_probe() - probe function. * @client: i2c client. @@ -115,14 +128,25 @@ static int inv_mpu_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct inv_mpu6050_state *st; - int result; - const char *name = id ? id->name : NULL; + int result, chip_type; struct regmap *regmap; + const char *name; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) return -EOPNOTSUPP; + if (id) { + chip_type = (int)id->driver_data; + name = id->name; + } else if (ACPI_HANDLE(&client->dev)) { + name = inv_mpu_match_acpi_device(&client->dev, &chip_type); + if (!name) + return -ENODEV; + } else { + return -ENOSYS; + } + regmap = devm_regmap_init_i2c(client, &inv_mpu_regmap_config); if (IS_ERR(regmap)) { dev_err(&client->dev, "Failed to register i2c regmap %d\n", @@ -131,7 +155,7 @@ static int inv_mpu_probe(struct i2c_client *client, } result = inv_mpu_core_probe(regmap, client->irq, name, - NULL, id->driver_data); + NULL, chip_type); if (result < 0) return result; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index dea6c4361de0..7bcb8d839f05 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -46,6 +46,7 @@ static int inv_mpu_probe(struct spi_device *spi) struct regmap *regmap; const struct spi_device_id *id = spi_get_device_id(spi); const char *name = id ? id->name : NULL; + const int chip_type = id ? id->driver_data : 0; regmap = devm_regmap_init_spi(spi, &inv_mpu_regmap_config); if (IS_ERR(regmap)) { @@ -55,7 +56,7 @@ static int inv_mpu_probe(struct spi_device *spi) } return inv_mpu_core_probe(regmap, spi->irq, name, - inv_mpu_i2c_disable, id->driver_data); + inv_mpu_i2c_disable, chip_type); } static int inv_mpu_remove(struct spi_device *spi) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 9c5c9ef3f1da..0e931a9a1669 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -462,6 +462,8 @@ static int ak8975_setup_irq(struct ak8975_data *data) int rc; int irq; + init_waitqueue_head(&data->data_ready_queue); + clear_bit(0, &data->flags); if (client->irq) irq = client->irq; else @@ -477,8 +479,6 @@ static int ak8975_setup_irq(struct ak8975_data *data) return rc; } - init_waitqueue_head(&data->data_ready_queue); - clear_bit(0, &data->flags); data->eoc_irq = irq; return rc; @@ -732,7 +732,7 @@ static int ak8975_probe(struct i2c_client *client, int eoc_gpio; int err; const char *name = NULL; - enum asahi_compass_chipset chipset; + enum asahi_compass_chipset chipset = AK_MAX_TYPE; /* Grab and set up the supplied GPIO. */ if (client->dev.platform_data) diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c index cb00d59da456..c2e257d97eff 100644 --- a/drivers/infiniband/core/cache.c +++ b/drivers/infiniband/core/cache.c @@ -691,7 +691,8 @@ void ib_cache_gid_set_default_gid(struct ib_device *ib_dev, u8 port, NULL); /* Coudn't find default GID location */ - WARN_ON(ix < 0); + if (WARN_ON(ix < 0)) + goto release; zattr_type.gid_type = gid_type; diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c index 4a9aa0433b07..7713ef089c3c 100644 --- a/drivers/infiniband/core/ucm.c +++ b/drivers/infiniband/core/ucm.c @@ -48,6 +48,7 @@ #include <asm/uaccess.h> +#include <rdma/ib.h> #include <rdma/ib_cm.h> #include <rdma/ib_user_cm.h> #include <rdma/ib_marshall.h> @@ -1103,6 +1104,9 @@ static ssize_t ib_ucm_write(struct file *filp, const char __user *buf, struct ib_ucm_cmd_hdr hdr; ssize_t result; + if (WARN_ON_ONCE(!ib_safe_file_access(filp))) + return -EACCES; + if (len < sizeof(hdr)) return -EINVAL; diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index dd3bcceadfde..c0f3826abb30 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -1574,6 +1574,9 @@ static ssize_t ucma_write(struct file *filp, const char __user *buf, struct rdma_ucm_cmd_hdr hdr; ssize_t ret; + if (WARN_ON_ONCE(!ib_safe_file_access(filp))) + return -EACCES; + if (len < sizeof(hdr)) return -EINVAL; diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 28ba2cc81535..31f422a70623 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -48,6 +48,8 @@ #include <asm/uaccess.h> +#include <rdma/ib.h> + #include "uverbs.h" MODULE_AUTHOR("Roland Dreier"); @@ -709,6 +711,9 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, int srcu_key; ssize_t ret; + if (WARN_ON_ONCE(!ib_safe_file_access(filp))) + return -EACCES; + if (count < sizeof hdr) return -EINVAL; diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 15b8adbf39c0..b65b3541e732 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -1860,6 +1860,7 @@ EXPORT_SYMBOL(ib_drain_rq); void ib_drain_qp(struct ib_qp *qp) { ib_drain_sq(qp); - ib_drain_rq(qp); + if (!qp->srq) + ib_drain_rq(qp); } EXPORT_SYMBOL(ib_drain_qp); diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index 42a7b8952d13..3234a8be16f6 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -1390,6 +1390,8 @@ int iwch_register_device(struct iwch_dev *dev) dev->ibdev.iwcm->add_ref = iwch_qp_add_ref; dev->ibdev.iwcm->rem_ref = iwch_qp_rem_ref; dev->ibdev.iwcm->get_qp = iwch_get_qp; + memcpy(dev->ibdev.iwcm->ifname, dev->rdev.t3cdev_p->lldev->name, + sizeof(dev->ibdev.iwcm->ifname)); ret = ib_register_device(&dev->ibdev, NULL); if (ret) diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c index b4eeb783573c..b0b955724458 100644 --- a/drivers/infiniband/hw/cxgb4/cq.c +++ b/drivers/infiniband/hw/cxgb4/cq.c @@ -162,7 +162,7 @@ static int create_cq(struct c4iw_rdev *rdev, struct t4_cq *cq, cq->bar2_va = c4iw_bar2_addrs(rdev, cq->cqid, T4_BAR2_QTYPE_INGRESS, &cq->bar2_qid, user ? &cq->bar2_pa : NULL); - if (user && !cq->bar2_va) { + if (user && !cq->bar2_pa) { pr_warn(MOD "%s: cqid %u not in BAR2 range.\n", pci_name(rdev->lldi.pdev), cq->cqid); ret = -EINVAL; diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c index 124682dc5709..7574f394fdac 100644 --- a/drivers/infiniband/hw/cxgb4/provider.c +++ b/drivers/infiniband/hw/cxgb4/provider.c @@ -580,6 +580,8 @@ int c4iw_register_device(struct c4iw_dev *dev) dev->ibdev.iwcm->add_ref = c4iw_qp_add_ref; dev->ibdev.iwcm->rem_ref = c4iw_qp_rem_ref; dev->ibdev.iwcm->get_qp = c4iw_get_qp; + memcpy(dev->ibdev.iwcm->ifname, dev->rdev.lldi.ports[0]->name, + sizeof(dev->ibdev.iwcm->ifname)); ret = ib_register_device(&dev->ibdev, NULL); if (ret) diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index e17fb5d5e033..e8993e49b8b3 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -185,6 +185,10 @@ void __iomem *c4iw_bar2_addrs(struct c4iw_rdev *rdev, unsigned int qid, if (pbar2_pa) *pbar2_pa = (rdev->bar2_pa + bar2_qoffset) & PAGE_MASK; + + if (is_t4(rdev->lldi.adapter_type)) + return NULL; + return rdev->bar2_kva + bar2_qoffset; } @@ -270,7 +274,7 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq, /* * User mode must have bar2 access. */ - if (user && (!wq->sq.bar2_va || !wq->rq.bar2_va)) { + if (user && (!wq->sq.bar2_pa || !wq->rq.bar2_pa)) { pr_warn(MOD "%s: sqid %u or rqid %u not in BAR2 range.\n", pci_name(rdev->lldi.pdev), wq->sq.qid, wq->rq.qid); goto free_dma; @@ -1895,13 +1899,27 @@ int c4iw_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, void c4iw_drain_sq(struct ib_qp *ibqp) { struct c4iw_qp *qp = to_c4iw_qp(ibqp); + unsigned long flag; + bool need_to_wait; - wait_for_completion(&qp->sq_drained); + spin_lock_irqsave(&qp->lock, flag); + need_to_wait = !t4_sq_empty(&qp->wq); + spin_unlock_irqrestore(&qp->lock, flag); + + if (need_to_wait) + wait_for_completion(&qp->sq_drained); } void c4iw_drain_rq(struct ib_qp *ibqp) { struct c4iw_qp *qp = to_c4iw_qp(ibqp); + unsigned long flag; + bool need_to_wait; + + spin_lock_irqsave(&qp->lock, flag); + need_to_wait = !t4_rq_empty(&qp->wq); + spin_unlock_irqrestore(&qp->lock, flag); - wait_for_completion(&qp->rq_drained); + if (need_to_wait) + wait_for_completion(&qp->rq_drained); } diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 5acf346e048e..6ad0489cb3c5 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -530,7 +530,7 @@ static int mlx5_ib_query_device(struct ib_device *ibdev, sizeof(struct mlx5_wqe_ctrl_seg)) / sizeof(struct mlx5_wqe_data_seg); props->max_sge = min(max_rq_sg, max_sq_sg); - props->max_sge_rd = props->max_sge; + props->max_sge_rd = MLX5_MAX_SGE_RD; props->max_cq = 1 << MLX5_CAP_GEN(mdev, log_max_cq); props->max_cqe = (1 << MLX5_CAP_GEN(mdev, log_max_cq_sz)) - 1; props->max_mr = 1 << MLX5_CAP_GEN(mdev, log_max_mkey); @@ -671,8 +671,8 @@ static int mlx5_query_hca_port(struct ib_device *ibdev, u8 port, struct mlx5_ib_dev *dev = to_mdev(ibdev); struct mlx5_core_dev *mdev = dev->mdev; struct mlx5_hca_vport_context *rep; - int max_mtu; - int oper_mtu; + u16 max_mtu; + u16 oper_mtu; int err; u8 ib_link_width_oper; u8 vl_hw_cap; diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c index 3ea9e055fdd3..92914539edc7 100644 --- a/drivers/infiniband/hw/nes/nes_nic.c +++ b/drivers/infiniband/hw/nes/nes_nic.c @@ -500,9 +500,6 @@ static int nes_netdev_start_xmit(struct sk_buff *skb, struct net_device *netdev) * skb_shinfo(skb)->nr_frags, skb_is_gso(skb)); */ - if (!netif_carrier_ok(netdev)) - return NETDEV_TX_OK; - if (netif_queue_stopped(netdev)) return NETDEV_TX_BUSY; diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c index e449e394963f..24f4a782e0f4 100644 --- a/drivers/infiniband/hw/qib/qib_file_ops.c +++ b/drivers/infiniband/hw/qib/qib_file_ops.c @@ -45,6 +45,8 @@ #include <linux/export.h> #include <linux/uio.h> +#include <rdma/ib.h> + #include "qib.h" #include "qib_common.h" #include "qib_user_sdma.h" @@ -2067,6 +2069,9 @@ static ssize_t qib_write(struct file *fp, const char __user *data, ssize_t ret = 0; void *dest; + if (WARN_ON_ONCE(!ib_safe_file_access(fp))) + return -EACCES; + if (count < sizeof(cmd.type)) { ret = -EINVAL; goto bail; diff --git a/drivers/infiniband/sw/rdmavt/qp.c b/drivers/infiniband/sw/rdmavt/qp.c index bd82a6948dc8..a9e3bcc522c4 100644 --- a/drivers/infiniband/sw/rdmavt/qp.c +++ b/drivers/infiniband/sw/rdmavt/qp.c @@ -1637,9 +1637,9 @@ bail: spin_unlock_irqrestore(&qp->s_hlock, flags); if (nreq) { if (call_send) - rdi->driver_f.schedule_send_no_lock(qp); - else rdi->driver_f.do_send(qp); + else + rdi->driver_f.schedule_send_no_lock(qp); } return err; } diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c index 80b6bedc172f..64b3d11dcf1e 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.c +++ b/drivers/infiniband/ulp/iser/iscsi_iser.c @@ -612,6 +612,7 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep, struct Scsi_Host *shost; struct iser_conn *iser_conn = NULL; struct ib_conn *ib_conn; + u32 max_fr_sectors; u16 max_cmds; shost = iscsi_host_alloc(&iscsi_iser_sht, 0, 0); @@ -632,7 +633,6 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep, iser_conn = ep->dd_data; max_cmds = iser_conn->max_cmds; shost->sg_tablesize = iser_conn->scsi_sg_tablesize; - shost->max_sectors = iser_conn->scsi_max_sectors; mutex_lock(&iser_conn->state_mutex); if (iser_conn->state != ISER_CONN_UP) { @@ -657,8 +657,6 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep, */ shost->sg_tablesize = min_t(unsigned short, shost->sg_tablesize, ib_conn->device->ib_device->attrs.max_fast_reg_page_list_len); - shost->max_sectors = min_t(unsigned int, - 1024, (shost->sg_tablesize * PAGE_SIZE) >> 9); if (iscsi_host_add(shost, ib_conn->device->ib_device->dma_device)) { @@ -672,6 +670,15 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep, goto free_host; } + /* + * FRs or FMRs can only map up to a (device) page per entry, but if the + * first entry is misaligned we'll end up using using two entries + * (head and tail) for a single page worth data, so we have to drop + * one segment from the calculation. + */ + max_fr_sectors = ((shost->sg_tablesize - 1) * PAGE_SIZE) >> 9; + shost->max_sectors = min(iser_max_sectors, max_fr_sectors); + if (cmds_max > max_cmds) { iser_info("cmds_max changed from %u to %u\n", cmds_max, max_cmds); @@ -989,7 +996,6 @@ static struct scsi_host_template iscsi_iser_sht = { .queuecommand = iscsi_queuecommand, .change_queue_depth = scsi_change_queue_depth, .sg_tablesize = ISCSI_ISER_DEF_SG_TABLESIZE, - .max_sectors = ISER_DEF_MAX_SECTORS, .cmd_per_lun = ISER_DEF_CMD_PER_LUN, .eh_abort_handler = iscsi_eh_abort, .eh_device_reset_handler= iscsi_eh_device_reset, diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index 53e33fab3f7a..df3581f60628 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -181,6 +181,14 @@ static void vibra_play_work(struct work_struct *work) { struct vibra_info *info = container_of(work, struct vibra_info, play_work); + int ret; + + /* Do not allow effect, while the routing is set to use audio */ + ret = twl6040_get_vibralr_status(info->twl6040); + if (ret & TWL6040_VIBSEL) { + dev_info(info->dev, "Vibra is configured for audio\n"); + return; + } mutex_lock(&info->mutex); @@ -199,14 +207,6 @@ static int vibra_play(struct input_dev *input, void *data, struct ff_effect *effect) { struct vibra_info *info = input_get_drvdata(input); - int ret; - - /* Do not allow effect, while the routing is set to use audio */ - ret = twl6040_get_vibralr_status(info->twl6040); - if (ret & TWL6040_VIBSEL) { - dev_info(&input->dev, "Vibra is configured for audio\n"); - return -EBUSY; - } info->weak_speed = effect->u.rumble.weak_magnitude; info->strong_speed = effect->u.rumble.strong_magnitude; diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 2160512e861a..5af7907d0af4 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1093,6 +1093,19 @@ static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset, return 0; } +static int mxt_acquire_irq(struct mxt_data *data) +{ + int error; + + enable_irq(data->irq); + + error = mxt_process_messages_until_invalid(data); + if (error) + return error; + + return 0; +} + static int mxt_soft_reset(struct mxt_data *data) { struct device *dev = &data->client->dev; @@ -1111,7 +1124,7 @@ static int mxt_soft_reset(struct mxt_data *data) /* Ignore CHG line for 100ms after reset */ msleep(100); - enable_irq(data->irq); + mxt_acquire_irq(data); ret = mxt_wait_for_completion(data, &data->reset_completion, MXT_RESET_TIMEOUT); @@ -1466,19 +1479,6 @@ release_mem: return ret; } -static int mxt_acquire_irq(struct mxt_data *data) -{ - int error; - - enable_irq(data->irq); - - error = mxt_process_messages_until_invalid(data); - if (error) - return error; - - return 0; -} - static int mxt_get_info(struct mxt_data *data) { struct i2c_client *client = data->client; diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index 9bbadaaf6bc3..7b3845aa5983 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -370,8 +370,8 @@ static int zforce_touch_event(struct zforce_ts *ts, u8 *payload) point.coord_x = point.coord_y = 0; } - point.state = payload[9 * i + 5] & 0x03; - point.id = (payload[9 * i + 5] & 0xfc) >> 2; + point.state = payload[9 * i + 5] & 0x0f; + point.id = (payload[9 * i + 5] & 0xf0) >> 4; /* determine touch major, minor and orientation */ point.area_major = max(payload[9 * i + 6], diff --git a/drivers/md/md.c b/drivers/md/md.c index 194580fba7fd..14d3b37944df 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -284,6 +284,8 @@ static blk_qc_t md_make_request(struct request_queue *q, struct bio *bio) * go away inside make_request */ sectors = bio_sectors(bio); + /* bio could be mergeable after passing to underlayer */ + bio->bi_rw &= ~REQ_NOMERGE; mddev->pers->make_request(mddev, bio); cpu = part_stat_lock(); diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 2ea12c6bf659..34783a3c8b3c 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -70,7 +70,6 @@ static void dump_zones(struct mddev *mddev) (unsigned long long)zone_size>>1); zone_start = conf->strip_zone[j].zone_end; } - printk(KERN_INFO "\n"); } static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf) @@ -85,6 +84,7 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf) struct r0conf *conf = kzalloc(sizeof(*conf), GFP_KERNEL); unsigned short blksize = 512; + *private_conf = ERR_PTR(-ENOMEM); if (!conf) return -ENOMEM; rdev_for_each(rdev1, mddev) { diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 8ab8b65e1741..e48c262ce032 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3502,8 +3502,6 @@ returnbi: dev = &sh->dev[i]; } else if (test_bit(R5_Discard, &dev->flags)) discard_pending = 1; - WARN_ON(test_bit(R5_SkipCopy, &dev->flags)); - WARN_ON(dev->page != dev->orig_page); } r5l_stripe_write_finished(sh); diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 6e43c95629ea..3cfd7af8c5ca 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -846,11 +846,11 @@ struct media_device *media_device_find_devres(struct device *dev) } EXPORT_SYMBOL_GPL(media_device_find_devres); +#if IS_ENABLED(CONFIG_PCI) void media_device_pci_init(struct media_device *mdev, struct pci_dev *pci_dev, const char *name) { -#ifdef CONFIG_PCI mdev->dev = &pci_dev->dev; if (name) @@ -866,16 +866,16 @@ void media_device_pci_init(struct media_device *mdev, mdev->driver_version = LINUX_VERSION_CODE; media_device_init(mdev); -#endif } EXPORT_SYMBOL_GPL(media_device_pci_init); +#endif +#if IS_ENABLED(CONFIG_USB) void __media_device_usb_init(struct media_device *mdev, struct usb_device *udev, const char *board_name, const char *driver_name) { -#ifdef CONFIG_USB mdev->dev = &udev->dev; if (driver_name) @@ -895,9 +895,9 @@ void __media_device_usb_init(struct media_device *mdev, mdev->driver_version = LINUX_VERSION_CODE; media_device_init(mdev); -#endif } EXPORT_SYMBOL_GPL(__media_device_usb_init); +#endif #endif /* CONFIG_MEDIA_CONTROLLER */ diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index feb521f28e14..4f494acd8150 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1446,22 +1446,13 @@ static int fimc_md_probe(struct platform_device *pdev) platform_set_drvdata(pdev, fmd); - /* Protect the media graph while we're registering entities */ - mutex_lock(&fmd->media_dev.graph_mutex); - ret = fimc_md_register_platform_entities(fmd, dev->of_node); - if (ret) { - mutex_unlock(&fmd->media_dev.graph_mutex); + if (ret) goto err_clk; - } ret = fimc_md_register_sensor_entities(fmd); - if (ret) { - mutex_unlock(&fmd->media_dev.graph_mutex); + if (ret) goto err_m_ent; - } - - mutex_unlock(&fmd->media_dev.graph_mutex); ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode); if (ret) diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c index 0b44b9accf50..af237af204e2 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -493,21 +493,17 @@ static int s3c_camif_probe(struct platform_device *pdev) if (ret < 0) goto err_sens; - mutex_lock(&camif->media_dev.graph_mutex); - ret = v4l2_device_register_subdev_nodes(&camif->v4l2_dev); if (ret < 0) - goto err_unlock; + goto err_sens; ret = camif_register_video_nodes(camif); if (ret < 0) - goto err_unlock; + goto err_sens; ret = camif_create_media_links(camif); if (ret < 0) - goto err_unlock; - - mutex_unlock(&camif->media_dev.graph_mutex); + goto err_sens; ret = media_device_register(&camif->media_dev); if (ret < 0) @@ -516,8 +512,6 @@ static int s3c_camif_probe(struct platform_device *pdev) pm_runtime_put(dev); return 0; -err_unlock: - mutex_unlock(&camif->media_dev.graph_mutex); err_sens: v4l2_device_unregister(&camif->v4l2_dev); media_device_unregister(&camif->media_dev); diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 12f5ebbd0436..ad2f3d27b266 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -1452,13 +1452,6 @@ static int usbvision_probe(struct usb_interface *intf, printk(KERN_INFO "%s: %s found\n", __func__, usbvision_device_data[model].model_string); - /* - * this is a security check. - * an exploit using an incorrect bInterfaceNumber is known - */ - if (ifnum >= USB_MAXINTERFACES || !dev->actconfig->interface[ifnum]) - return -ENODEV; - if (usbvision_device_data[model].interface >= 0) interface = &dev->actconfig->interface[usbvision_device_data[model].interface]->altsetting[0]; else if (ifnum < dev->actconfig->desc.bNumInterfaces) diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 5d016f496e0e..9fbcb67a9ee6 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1645,7 +1645,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) * Will sleep if required for nonblocking == false. */ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, - int nonblocking) + void *pb, int nonblocking) { unsigned long flags; int ret; @@ -1666,10 +1666,10 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, /* * Only remove the buffer from done_list if v4l2_buffer can handle all * the planes. - * Verifying planes is NOT necessary since it already has been checked - * before the buffer is queued/prepared. So it can never fail. */ - list_del(&(*vb)->done_entry); + ret = call_bufop(q, verify_planes_array, *vb, pb); + if (!ret) + list_del(&(*vb)->done_entry); spin_unlock_irqrestore(&q->done_lock, flags); return ret; @@ -1748,7 +1748,7 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, struct vb2_buffer *vb = NULL; int ret; - ret = __vb2_get_done_vb(q, &vb, nonblocking); + ret = __vb2_get_done_vb(q, &vb, pb, nonblocking); if (ret < 0) return ret; @@ -2298,6 +2298,16 @@ unsigned int vb2_core_poll(struct vb2_queue *q, struct file *file, return POLLERR; /* + * If this quirk is set and QBUF hasn't been called yet then + * return POLLERR as well. This only affects capture queues, output + * queues will always initialize waiting_for_buffers to false. + * This quirk is set by V4L2 for backwards compatibility reasons. + */ + if (q->quirk_poll_must_check_waiting_for_buffers && + q->waiting_for_buffers && (req_events & (POLLIN | POLLRDNORM))) + return POLLERR; + + /* * For output streams you can call write() as long as there are fewer * buffers queued than there are buffers available. */ diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c index dbec5923fcf0..3c3b517f1d1c 100644 --- a/drivers/media/v4l2-core/videobuf2-memops.c +++ b/drivers/media/v4l2-core/videobuf2-memops.c @@ -49,7 +49,7 @@ struct frame_vector *vb2_create_framevec(unsigned long start, vec = frame_vector_create(nr); if (!vec) return ERR_PTR(-ENOMEM); - ret = get_vaddr_frames(start, nr, write, 1, vec); + ret = get_vaddr_frames(start & PAGE_MASK, nr, write, true, vec); if (ret < 0) goto out_destroy; /* We accept only complete set of PFNs */ diff --git a/drivers/media/v4l2-core/videobuf2-v4l2.c b/drivers/media/v4l2-core/videobuf2-v4l2.c index 91f552124050..7f366f1b0377 100644 --- a/drivers/media/v4l2-core/videobuf2-v4l2.c +++ b/drivers/media/v4l2-core/videobuf2-v4l2.c @@ -74,6 +74,11 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer return 0; } +static int __verify_planes_array_core(struct vb2_buffer *vb, const void *pb) +{ + return __verify_planes_array(vb, pb); +} + /** * __verify_length() - Verify that the bytesused value for each plane fits in * the plane length and that the data offset doesn't exceed the bytesused value. @@ -437,6 +442,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, } static const struct vb2_buf_ops v4l2_buf_ops = { + .verify_planes_array = __verify_planes_array_core, .fill_user_buffer = __fill_v4l2_buffer, .fill_vb2_buffer = __fill_vb2_buffer, .copy_timestamp = __copy_timestamp, @@ -765,6 +771,12 @@ int vb2_queue_init(struct vb2_queue *q) q->is_output = V4L2_TYPE_IS_OUTPUT(q->type); q->copy_timestamp = (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == V4L2_BUF_FLAG_TIMESTAMP_COPY; + /* + * For compatibility with vb1: if QBUF hasn't been called yet, then + * return POLLERR as well. This only affects capture queues, output + * queues will always initialize waiting_for_buffers to false. + */ + q->quirk_poll_must_check_waiting_for_buffers = true; return vb2_core_queue_init(q); } @@ -818,14 +830,6 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) poll_wait(file, &fh->wait, wait); } - /* - * For compatibility with vb1: if QBUF hasn't been called yet, then - * return POLLERR as well. This only affects capture queues, output - * queues will always initialize waiting_for_buffers to false. - */ - if (q->waiting_for_buffers && (req_events & (POLLIN | POLLRDNORM))) - return POLLERR; - return res | vb2_core_poll(q, file, wait); } EXPORT_SYMBOL_GPL(vb2_poll); diff --git a/drivers/misc/cxl/context.c b/drivers/misc/cxl/context.c index 10370f280500..7edea9c19199 100644 --- a/drivers/misc/cxl/context.c +++ b/drivers/misc/cxl/context.c @@ -223,6 +223,13 @@ int __detach_context(struct cxl_context *ctx) cxl_ops->link_ok(ctx->afu->adapter, ctx->afu)); flush_work(&ctx->fault_work); /* Only needed for dedicated process */ + /* + * Wait until no further interrupts are presented by the PSL + * for this context. + */ + if (cxl_ops->irq_wait) + cxl_ops->irq_wait(ctx); + /* release the reference to the group leader and mm handling pid */ put_pid(ctx->pid); put_pid(ctx->glpid); diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index 38e21cf7806e..73dc2a33da74 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -274,6 +274,7 @@ static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0}; #define CXL_PSL_DSISR_An_PE (1ull << (63-4)) /* PSL Error (implementation specific) */ #define CXL_PSL_DSISR_An_AE (1ull << (63-5)) /* AFU Error */ #define CXL_PSL_DSISR_An_OC (1ull << (63-6)) /* OS Context Warning */ +#define CXL_PSL_DSISR_PENDING (CXL_PSL_DSISR_TRANS | CXL_PSL_DSISR_An_PE | CXL_PSL_DSISR_An_AE | CXL_PSL_DSISR_An_OC) /* NOTE: Bits 32:63 are undefined if DSISR[DS] = 1 */ #define CXL_PSL_DSISR_An_M DSISR_NOHPTE /* PTE not found */ #define CXL_PSL_DSISR_An_P DSISR_PROTFAULT /* Storage protection violation */ @@ -855,6 +856,7 @@ struct cxl_backend_ops { u64 dsisr, u64 errstat); irqreturn_t (*psl_interrupt)(int irq, void *data); int (*ack_irq)(struct cxl_context *ctx, u64 tfc, u64 psl_reset_mask); + void (*irq_wait)(struct cxl_context *ctx); int (*attach_process)(struct cxl_context *ctx, bool kernel, u64 wed, u64 amr); int (*detach_process)(struct cxl_context *ctx); diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c index be646dc41a2c..8def4553acba 100644 --- a/drivers/misc/cxl/irq.c +++ b/drivers/misc/cxl/irq.c @@ -203,7 +203,6 @@ unsigned int cxl_map_irq(struct cxl *adapter, irq_hw_number_t hwirq, void cxl_unmap_irq(unsigned int virq, void *cookie) { free_irq(virq, cookie); - irq_dispose_mapping(virq); } int cxl_register_one_irq(struct cxl *adapter, diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c index 387fcbdf9793..ecf7557cd657 100644 --- a/drivers/misc/cxl/native.c +++ b/drivers/misc/cxl/native.c @@ -14,6 +14,7 @@ #include <linux/mutex.h> #include <linux/mm.h> #include <linux/uaccess.h> +#include <linux/delay.h> #include <asm/synch.h> #include <misc/cxl-base.h> @@ -797,6 +798,35 @@ static irqreturn_t native_irq_multiplexed(int irq, void *data) return fail_psl_irq(afu, &irq_info); } +void native_irq_wait(struct cxl_context *ctx) +{ + u64 dsisr; + int timeout = 1000; + int ph; + + /* + * Wait until no further interrupts are presented by the PSL + * for this context. + */ + while (timeout--) { + ph = cxl_p2n_read(ctx->afu, CXL_PSL_PEHandle_An) & 0xffff; + if (ph != ctx->pe) + return; + dsisr = cxl_p2n_read(ctx->afu, CXL_PSL_DSISR_An); + if ((dsisr & CXL_PSL_DSISR_PENDING) == 0) + return; + /* + * We are waiting for the workqueue to process our + * irq, so need to let that run here. + */ + msleep(1); + } + + dev_warn(&ctx->afu->dev, "WARNING: waiting on DSI for PE %i" + " DSISR %016llx!\n", ph, dsisr); + return; +} + static irqreturn_t native_slice_irq_err(int irq, void *data) { struct cxl_afu *afu = data; @@ -1076,6 +1106,7 @@ const struct cxl_backend_ops cxl_native_ops = { .handle_psl_slice_error = native_handle_psl_slice_error, .psl_interrupt = NULL, .ack_irq = native_ack_irq, + .irq_wait = native_irq_wait, .attach_process = native_attach_process, .detach_process = native_detach_process, .support_attributes = native_support_attributes, diff --git a/drivers/misc/mic/vop/vop_vringh.c b/drivers/misc/mic/vop/vop_vringh.c index e94c7fb6712a..88e45234d527 100644 --- a/drivers/misc/mic/vop/vop_vringh.c +++ b/drivers/misc/mic/vop/vop_vringh.c @@ -945,6 +945,11 @@ static long vop_ioctl(struct file *f, unsigned int cmd, unsigned long arg) ret = -EFAULT; goto free_ret; } + /* Ensure desc has not changed between the two reads */ + if (memcmp(&dd, dd_config, sizeof(dd))) { + ret = -EINVAL; + goto free_ret; + } mutex_lock(&vdev->vdev_mutex); mutex_lock(&vi->vop_mutex); ret = vop_virtio_add_device(vdev, dd_config); diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 04feea8354cb..e657af0e95fa 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -97,6 +97,7 @@ config MMC_RICOH_MMC config MMC_SDHCI_ACPI tristate "SDHCI support for ACPI enumerated SDHCI controllers" depends on MMC_SDHCI && ACPI + select IOSF_MBI if X86 help This selects support for ACPI enumerated SDHCI controllers, identified by ACPI Compatibility ID PNP0D40 or specific diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 6839e41c6d58..bed6a494f52c 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -41,6 +41,11 @@ #include <linux/mmc/pm.h> #include <linux/mmc/slot-gpio.h> +#ifdef CONFIG_X86 +#include <asm/cpu_device_id.h> +#include <asm/iosf_mbi.h> +#endif + #include "sdhci.h" enum { @@ -116,6 +121,75 @@ static const struct sdhci_acpi_chip sdhci_acpi_chip_int = { .ops = &sdhci_acpi_ops_int, }; +#ifdef CONFIG_X86 + +static bool sdhci_acpi_byt(void) +{ + static const struct x86_cpu_id byt[] = { + { X86_VENDOR_INTEL, 6, 0x37 }, + {} + }; + + return x86_match_cpu(byt); +} + +#define BYT_IOSF_SCCEP 0x63 +#define BYT_IOSF_OCP_NETCTRL0 0x1078 +#define BYT_IOSF_OCP_TIMEOUT_BASE GENMASK(10, 8) + +static void sdhci_acpi_byt_setting(struct device *dev) +{ + u32 val = 0; + + if (!sdhci_acpi_byt()) + return; + + if (iosf_mbi_read(BYT_IOSF_SCCEP, MBI_CR_READ, BYT_IOSF_OCP_NETCTRL0, + &val)) { + dev_err(dev, "%s read error\n", __func__); + return; + } + + if (!(val & BYT_IOSF_OCP_TIMEOUT_BASE)) + return; + + val &= ~BYT_IOSF_OCP_TIMEOUT_BASE; + + if (iosf_mbi_write(BYT_IOSF_SCCEP, MBI_CR_WRITE, BYT_IOSF_OCP_NETCTRL0, + val)) { + dev_err(dev, "%s write error\n", __func__); + return; + } + + dev_dbg(dev, "%s completed\n", __func__); +} + +static bool sdhci_acpi_byt_defer(struct device *dev) +{ + if (!sdhci_acpi_byt()) + return false; + + if (!iosf_mbi_available()) + return true; + + sdhci_acpi_byt_setting(dev); + + return false; +} + +#else + +static inline void sdhci_acpi_byt_setting(struct device *dev) +{ +} + +static inline bool sdhci_acpi_byt_defer(struct device *dev) +{ + return false; +} + +#endif + static int bxt_get_cd(struct mmc_host *mmc) { int gpio_cd = mmc_gpio_get_cd(mmc); @@ -322,6 +396,9 @@ static int sdhci_acpi_probe(struct platform_device *pdev) if (acpi_bus_get_status(device) || !device->status.present) return -ENODEV; + if (sdhci_acpi_byt_defer(dev)) + return -EPROBE_DEFER; + hid = acpi_device_hid(device); uid = device->pnp.unique_id; @@ -447,6 +524,8 @@ static int sdhci_acpi_resume(struct device *dev) { struct sdhci_acpi_host *c = dev_get_drvdata(dev); + sdhci_acpi_byt_setting(&c->pdev->dev); + return sdhci_resume_host(c->host); } @@ -470,6 +549,8 @@ static int sdhci_acpi_runtime_resume(struct device *dev) { struct sdhci_acpi_host *c = dev_get_drvdata(dev); + sdhci_acpi_byt_setting(&c->pdev->dev); + return sdhci_runtime_resume_host(c->host); } diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 8372a413848c..7fc8b7aa83f0 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -1129,6 +1129,11 @@ static int sunxi_mmc_probe(struct platform_device *pdev) MMC_CAP_1_8V_DDR | MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ; + /* TODO MMC DDR is not working on A80 */ + if (of_device_is_compatible(pdev->dev.of_node, + "allwinner,sun9i-a80-mmc")) + mmc->caps &= ~MMC_CAP_1_8V_DDR; + ret = mmc_of_parse(mmc); if (ret) goto error_free_dma; diff --git a/drivers/mmc/host/toshsd.c b/drivers/mmc/host/toshsd.c index e2cdd5fb1423..553ef41bb806 100644 --- a/drivers/mmc/host/toshsd.c +++ b/drivers/mmc/host/toshsd.c @@ -21,6 +21,7 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/pm.h> +#include <linux/pm_runtime.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index a24c18eee598..befd67df08e1 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -62,9 +62,8 @@ config DUMMY this device is consigned into oblivion) with a configurable IP address. It is most commonly used in order to make your currently inactive SLIP address seem like a real address for local programs. - If you use SLIP or PPP, you might want to say Y here. Since this - thing often comes in handy, the default is Y. It won't enlarge your - kernel either. What a deal. Read about it in the Network + If you use SLIP or PPP, you might want to say Y here. It won't + enlarge your kernel. What a deal. Read about it in the Network Administrator's Guide, available from <http://www.tldp.org/docs.html#guide>. diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index a2904029cccc..5e572b3510b9 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -2181,7 +2181,7 @@ int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *bridge) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int i, err; + int i, err = 0; mutex_lock(&ps->smi_mutex); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 12a009d720cd..72eb29ed0359 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -581,12 +581,30 @@ static inline int bnxt_alloc_rx_page(struct bnxt *bp, struct page *page; dma_addr_t mapping; u16 sw_prod = rxr->rx_sw_agg_prod; + unsigned int offset = 0; - page = alloc_page(gfp); - if (!page) - return -ENOMEM; + if (PAGE_SIZE > BNXT_RX_PAGE_SIZE) { + page = rxr->rx_page; + if (!page) { + page = alloc_page(gfp); + if (!page) + return -ENOMEM; + rxr->rx_page = page; + rxr->rx_page_offset = 0; + } + offset = rxr->rx_page_offset; + rxr->rx_page_offset += BNXT_RX_PAGE_SIZE; + if (rxr->rx_page_offset == PAGE_SIZE) + rxr->rx_page = NULL; + else + get_page(page); + } else { + page = alloc_page(gfp); + if (!page) + return -ENOMEM; + } - mapping = dma_map_page(&pdev->dev, page, 0, PAGE_SIZE, + mapping = dma_map_page(&pdev->dev, page, offset, BNXT_RX_PAGE_SIZE, PCI_DMA_FROMDEVICE); if (dma_mapping_error(&pdev->dev, mapping)) { __free_page(page); @@ -601,6 +619,7 @@ static inline int bnxt_alloc_rx_page(struct bnxt *bp, rxr->rx_sw_agg_prod = NEXT_RX_AGG(sw_prod); rx_agg_buf->page = page; + rx_agg_buf->offset = offset; rx_agg_buf->mapping = mapping; rxbd->rx_bd_haddr = cpu_to_le64(mapping); rxbd->rx_bd_opaque = sw_prod; @@ -642,6 +661,7 @@ static void bnxt_reuse_rx_agg_bufs(struct bnxt_napi *bnapi, u16 cp_cons, page = cons_rx_buf->page; cons_rx_buf->page = NULL; prod_rx_buf->page = page; + prod_rx_buf->offset = cons_rx_buf->offset; prod_rx_buf->mapping = cons_rx_buf->mapping; @@ -709,7 +729,8 @@ static struct sk_buff *bnxt_rx_pages(struct bnxt *bp, struct bnxt_napi *bnapi, RX_AGG_CMP_LEN) >> RX_AGG_CMP_LEN_SHIFT; cons_rx_buf = &rxr->rx_agg_ring[cons]; - skb_fill_page_desc(skb, i, cons_rx_buf->page, 0, frag_len); + skb_fill_page_desc(skb, i, cons_rx_buf->page, + cons_rx_buf->offset, frag_len); __clear_bit(cons, rxr->rx_agg_bmap); /* It is possible for bnxt_alloc_rx_page() to allocate @@ -740,7 +761,7 @@ static struct sk_buff *bnxt_rx_pages(struct bnxt *bp, struct bnxt_napi *bnapi, return NULL; } - dma_unmap_page(&pdev->dev, mapping, PAGE_SIZE, + dma_unmap_page(&pdev->dev, mapping, BNXT_RX_PAGE_SIZE, PCI_DMA_FROMDEVICE); skb->data_len += frag_len; @@ -1584,13 +1605,17 @@ static void bnxt_free_rx_skbs(struct bnxt *bp) dma_unmap_page(&pdev->dev, dma_unmap_addr(rx_agg_buf, mapping), - PAGE_SIZE, PCI_DMA_FROMDEVICE); + BNXT_RX_PAGE_SIZE, PCI_DMA_FROMDEVICE); rx_agg_buf->page = NULL; __clear_bit(j, rxr->rx_agg_bmap); __free_page(page); } + if (rxr->rx_page) { + __free_page(rxr->rx_page); + rxr->rx_page = NULL; + } } } @@ -1973,7 +1998,7 @@ static int bnxt_init_one_rx_ring(struct bnxt *bp, int ring_nr) if (!(bp->flags & BNXT_FLAG_AGG_RINGS)) return 0; - type = ((u32)PAGE_SIZE << RX_BD_LEN_SHIFT) | + type = ((u32)BNXT_RX_PAGE_SIZE << RX_BD_LEN_SHIFT) | RX_BD_TYPE_RX_AGG_BD | RX_BD_FLAGS_SOP; bnxt_init_rxbd_pages(ring, type); @@ -2164,7 +2189,7 @@ void bnxt_set_ring_params(struct bnxt *bp) bp->rx_agg_nr_pages = 0; if (bp->flags & BNXT_FLAG_TPA) - agg_factor = 4; + agg_factor = min_t(u32, 4, 65536 / BNXT_RX_PAGE_SIZE); bp->flags &= ~BNXT_FLAG_JUMBO; if (rx_space > PAGE_SIZE) { @@ -3020,12 +3045,12 @@ static int bnxt_hwrm_vnic_set_tpa(struct bnxt *bp, u16 vnic_id, u32 tpa_flags) /* Number of segs are log2 units, and first packet is not * included as part of this units. */ - if (mss <= PAGE_SIZE) { - n = PAGE_SIZE / mss; + if (mss <= BNXT_RX_PAGE_SIZE) { + n = BNXT_RX_PAGE_SIZE / mss; nsegs = (MAX_SKB_FRAGS - 1) * n; } else { - n = mss / PAGE_SIZE; - if (mss & (PAGE_SIZE - 1)) + n = mss / BNXT_RX_PAGE_SIZE; + if (mss & (BNXT_RX_PAGE_SIZE - 1)) n++; nsegs = (MAX_SKB_FRAGS - n) / n; } @@ -4309,7 +4334,7 @@ static int bnxt_setup_int_mode(struct bnxt *bp) if (bp->flags & BNXT_FLAG_MSIX_CAP) rc = bnxt_setup_msix(bp); - if (!(bp->flags & BNXT_FLAG_USING_MSIX)) { + if (!(bp->flags & BNXT_FLAG_USING_MSIX) && BNXT_PF(bp)) { /* fallback to INTA */ rc = bnxt_setup_inta(bp); } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 709b95b8fcba..8b823ff558ff 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -407,6 +407,15 @@ struct rx_tpa_end_cmp_ext { #define BNXT_PAGE_SIZE (1 << BNXT_PAGE_SHIFT) +/* The RXBD length is 16-bit so we can only support page sizes < 64K */ +#if (PAGE_SHIFT > 15) +#define BNXT_RX_PAGE_SHIFT 15 +#else +#define BNXT_RX_PAGE_SHIFT PAGE_SHIFT +#endif + +#define BNXT_RX_PAGE_SIZE (1 << BNXT_RX_PAGE_SHIFT) + #define BNXT_MIN_PKT_SIZE 45 #define BNXT_NUM_TESTS(bp) 0 @@ -506,6 +515,7 @@ struct bnxt_sw_rx_bd { struct bnxt_sw_rx_agg_bd { struct page *page; + unsigned int offset; dma_addr_t mapping; }; @@ -586,6 +596,9 @@ struct bnxt_rx_ring_info { unsigned long *rx_agg_bmap; u16 rx_agg_bmap_size; + struct page *rx_page; + unsigned int rx_page_offset; + dma_addr_t rx_desc_mapping[MAX_RX_PAGES]; dma_addr_t rx_agg_desc_mapping[MAX_RX_AGG_PAGES]; diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 48a7d7dee846..a63551d0a18a 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -441,7 +441,7 @@ static int macb_mii_init(struct macb *bp) snprintf(bp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", bp->pdev->name, bp->pdev->id); bp->mii_bus->priv = bp; - bp->mii_bus->parent = &bp->dev->dev; + bp->mii_bus->parent = &bp->pdev->dev; pdata = dev_get_platdata(&bp->pdev->dev); dev_set_drvdata(&bp->dev->dev, bp->mii_bus); @@ -458,7 +458,8 @@ static int macb_mii_init(struct macb *bp) struct phy_device *phydev; phydev = mdiobus_scan(bp->mii_bus, i); - if (IS_ERR(phydev)) { + if (IS_ERR(phydev) && + PTR_ERR(phydev) != -ENODEV) { err = PTR_ERR(phydev); break; } @@ -3019,29 +3020,36 @@ static int macb_probe(struct platform_device *pdev) if (err) goto err_out_free_netdev; + err = macb_mii_init(bp); + if (err) + goto err_out_free_netdev; + + phydev = bp->phy_dev; + + netif_carrier_off(dev); + err = register_netdev(dev); if (err) { dev_err(&pdev->dev, "Cannot register net device, aborting.\n"); - goto err_out_unregister_netdev; + goto err_out_unregister_mdio; } - err = macb_mii_init(bp); - if (err) - goto err_out_unregister_netdev; - - netif_carrier_off(dev); + phy_attached_info(phydev); netdev_info(dev, "Cadence %s rev 0x%08x at 0x%08lx irq %d (%pM)\n", macb_is_gem(bp) ? "GEM" : "MACB", macb_readl(bp, MID), dev->base_addr, dev->irq, dev->dev_addr); - phydev = bp->phy_dev; - phy_attached_info(phydev); - return 0; -err_out_unregister_netdev: - unregister_netdev(dev); +err_out_unregister_mdio: + phy_disconnect(bp->phy_dev); + mdiobus_unregister(bp->mii_bus); + mdiobus_free(bp->mii_bus); + + /* Shutdown the PHY if there is a GPIO reset */ + if (bp->reset_gpio) + gpiod_set_value(bp->reset_gpio, 0); err_out_free_netdev: free_netdev(dev); diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 60908eab3b3a..43da891fab97 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -576,7 +576,7 @@ static void setup_rss(struct adapter *adap) unsigned int nq0 = adap2pinfo(adap, 0)->nqsets; unsigned int nq1 = adap->port[1] ? adap2pinfo(adap, 1)->nqsets : 1; u8 cpus[SGE_QSETS + 1]; - u16 rspq_map[RSS_TABLE_SIZE]; + u16 rspq_map[RSS_TABLE_SIZE + 1]; for (i = 0; i < SGE_QSETS; ++i) cpus[i] = i; @@ -586,6 +586,7 @@ static void setup_rss(struct adapter *adap) rspq_map[i] = i % nq0; rspq_map[i + RSS_TABLE_SIZE / 2] = (i % nq1) + nq0; } + rspq_map[RSS_TABLE_SIZE] = 0xffff; /* terminator */ t3_config_rss(adap, F_RQFEEDBACKENABLE | F_TNLLKPEN | F_TNLMAPEN | F_TNLPRTEN | F_TNL2TUPEN | F_TNL4TUPEN | diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 7fc490225da5..a6d26d351dfc 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3354,8 +3354,7 @@ static int mvneta_percpu_notifier(struct notifier_block *nfb, /* Enable per-CPU interrupts on the CPU that is * brought up. */ - smp_call_function_single(cpu, mvneta_percpu_enable, - pp, true); + mvneta_percpu_enable(pp); /* Enable per-CPU interrupt on the one CPU we care * about. @@ -3387,8 +3386,7 @@ static int mvneta_percpu_notifier(struct notifier_block *nfb, /* Disable per-CPU interrupts on the CPU that is * brought down. */ - smp_call_function_single(cpu, mvneta_percpu_disable, - pp, true); + mvneta_percpu_disable(pp); break; case CPU_DEAD: diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index 7ace07dad6a3..c442f6ad15ff 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -979,6 +979,8 @@ static int pxa168_init_phy(struct net_device *dev) return 0; pep->phy = mdiobus_scan(pep->smi_bus, pep->phy_addr); + if (IS_ERR(pep->phy)) + return PTR_ERR(pep->phy); if (!pep->phy) return -ENODEV; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index c0d7b7296236..a386f047c1af 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -405,7 +405,6 @@ static bool mlx4_en_process_tx_cq(struct net_device *dev, u32 packets = 0; u32 bytes = 0; int factor = priv->cqe_factor; - u64 timestamp = 0; int done = 0; int budget = priv->tx_work_limit; u32 last_nr_txbb; @@ -445,9 +444,12 @@ static bool mlx4_en_process_tx_cq(struct net_device *dev, new_index = be16_to_cpu(cqe->wqe_index) & size_mask; do { + u64 timestamp = 0; + txbbs_skipped += last_nr_txbb; ring_index = (ring_index + last_nr_txbb) & size_mask; - if (ring->tx_info[ring_index].ts_requested) + + if (unlikely(ring->tx_info[ring_index].ts_requested)) timestamp = mlx4_en_get_cqe_ts(cqe); /* free next descriptor */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig index 1cf722eba607..559d11a443bc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig +++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig @@ -14,6 +14,7 @@ config MLX5_CORE_EN bool "Mellanox Technologies ConnectX-4 Ethernet support" depends on NETDEVICES && ETHERNET && PCI && MLX5_CORE select PTP_1588_CLOCK + select VXLAN if MLX5_CORE=y default n ---help--- Ethernet support in Mellanox Technologies ConnectX-4 NIC. diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 879e6276c473..3881dce0cc30 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -567,6 +567,7 @@ struct mlx5e_priv { struct mlx5e_vxlan_db vxlan; struct mlx5e_params params; + struct workqueue_struct *wq; struct work_struct update_carrier_work; struct work_struct set_rx_mode_work; struct delayed_work update_stats_work; @@ -609,7 +610,7 @@ enum mlx5e_link_mode { MLX5E_100GBASE_KR4 = 22, MLX5E_100GBASE_LR4 = 23, MLX5E_100BASE_TX = 24, - MLX5E_100BASE_T = 25, + MLX5E_1000BASE_T = 25, MLX5E_10GBASE_T = 26, MLX5E_25GBASE_CR = 27, MLX5E_25GBASE_KR = 28, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 68834b715f6c..3476ab844634 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -138,10 +138,10 @@ static const struct { [MLX5E_100BASE_TX] = { .speed = 100, }, - [MLX5E_100BASE_T] = { - .supported = SUPPORTED_100baseT_Full, - .advertised = ADVERTISED_100baseT_Full, - .speed = 100, + [MLX5E_1000BASE_T] = { + .supported = SUPPORTED_1000baseT_Full, + .advertised = ADVERTISED_1000baseT_Full, + .speed = 1000, }, [MLX5E_10GBASE_T] = { .supported = SUPPORTED_10000baseT_Full, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index e0adb604f461..d4dfc5ce516a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -262,9 +262,8 @@ static void mlx5e_update_stats_work(struct work_struct *work) mutex_lock(&priv->state_lock); if (test_bit(MLX5E_STATE_OPENED, &priv->state)) { mlx5e_update_stats(priv); - schedule_delayed_work(dwork, - msecs_to_jiffies( - MLX5E_UPDATE_STATS_INTERVAL)); + queue_delayed_work(priv->wq, dwork, + msecs_to_jiffies(MLX5E_UPDATE_STATS_INTERVAL)); } mutex_unlock(&priv->state_lock); } @@ -280,7 +279,7 @@ static void mlx5e_async_event(struct mlx5_core_dev *mdev, void *vpriv, switch (event) { case MLX5_DEV_EVENT_PORT_UP: case MLX5_DEV_EVENT_PORT_DOWN: - schedule_work(&priv->update_carrier_work); + queue_work(priv->wq, &priv->update_carrier_work); break; default: @@ -1404,24 +1403,50 @@ static int mlx5e_refresh_tirs_self_loopback_enable(struct mlx5e_priv *priv) return 0; } -static int mlx5e_set_dev_port_mtu(struct net_device *netdev) +static int mlx5e_set_mtu(struct mlx5e_priv *priv, u16 mtu) { - struct mlx5e_priv *priv = netdev_priv(netdev); struct mlx5_core_dev *mdev = priv->mdev; - int hw_mtu; + u16 hw_mtu = MLX5E_SW2HW_MTU(mtu); int err; - err = mlx5_set_port_mtu(mdev, MLX5E_SW2HW_MTU(netdev->mtu), 1); + err = mlx5_set_port_mtu(mdev, hw_mtu, 1); if (err) return err; - mlx5_query_port_oper_mtu(mdev, &hw_mtu, 1); + /* Update vport context MTU */ + mlx5_modify_nic_vport_mtu(mdev, hw_mtu); + return 0; +} + +static void mlx5e_query_mtu(struct mlx5e_priv *priv, u16 *mtu) +{ + struct mlx5_core_dev *mdev = priv->mdev; + u16 hw_mtu = 0; + int err; - if (MLX5E_HW2SW_MTU(hw_mtu) != netdev->mtu) - netdev_warn(netdev, "%s: Port MTU %d is different than netdev mtu %d\n", - __func__, MLX5E_HW2SW_MTU(hw_mtu), netdev->mtu); + err = mlx5_query_nic_vport_mtu(mdev, &hw_mtu); + if (err || !hw_mtu) /* fallback to port oper mtu */ + mlx5_query_port_oper_mtu(mdev, &hw_mtu, 1); + + *mtu = MLX5E_HW2SW_MTU(hw_mtu); +} + +static int mlx5e_set_dev_port_mtu(struct net_device *netdev) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + u16 mtu; + int err; - netdev->mtu = MLX5E_HW2SW_MTU(hw_mtu); + err = mlx5e_set_mtu(priv, netdev->mtu); + if (err) + return err; + + mlx5e_query_mtu(priv, &mtu); + if (mtu != netdev->mtu) + netdev_warn(netdev, "%s: VPort MTU %d is different than netdev mtu %d\n", + __func__, mtu, netdev->mtu); + + netdev->mtu = mtu; return 0; } @@ -1479,7 +1504,7 @@ int mlx5e_open_locked(struct net_device *netdev) mlx5e_update_carrier(priv); mlx5e_timestamp_init(priv); - schedule_delayed_work(&priv->update_stats_work, 0); + queue_delayed_work(priv->wq, &priv->update_stats_work, 0); return 0; @@ -1935,7 +1960,7 @@ static void mlx5e_set_rx_mode(struct net_device *dev) { struct mlx5e_priv *priv = netdev_priv(dev); - schedule_work(&priv->set_rx_mode_work); + queue_work(priv->wq, &priv->set_rx_mode_work); } static int mlx5e_set_mac(struct net_device *netdev, void *addr) @@ -1950,7 +1975,7 @@ static int mlx5e_set_mac(struct net_device *netdev, void *addr) ether_addr_copy(netdev->dev_addr, saddr->sa_data); netif_addr_unlock_bh(netdev); - schedule_work(&priv->set_rx_mode_work); + queue_work(priv->wq, &priv->set_rx_mode_work); return 0; } @@ -1999,22 +2024,27 @@ static int mlx5e_set_features(struct net_device *netdev, return err; } +#define MXL5_HW_MIN_MTU 64 +#define MXL5E_MIN_MTU (MXL5_HW_MIN_MTU + ETH_FCS_LEN) + static int mlx5e_change_mtu(struct net_device *netdev, int new_mtu) { struct mlx5e_priv *priv = netdev_priv(netdev); struct mlx5_core_dev *mdev = priv->mdev; bool was_opened; - int max_mtu; + u16 max_mtu; + u16 min_mtu; int err = 0; mlx5_query_port_max_mtu(mdev, &max_mtu, 1); max_mtu = MLX5E_HW2SW_MTU(max_mtu); + min_mtu = MLX5E_HW2SW_MTU(MXL5E_MIN_MTU); - if (new_mtu > max_mtu) { + if (new_mtu > max_mtu || new_mtu < min_mtu) { netdev_err(netdev, - "%s: Bad MTU (%d) > (%d) Max\n", - __func__, new_mtu, max_mtu); + "%s: Bad MTU (%d), valid range is: [%d..%d]\n", + __func__, new_mtu, min_mtu, max_mtu); return -EINVAL; } @@ -2127,7 +2157,7 @@ static void mlx5e_add_vxlan_port(struct net_device *netdev, if (!mlx5e_vxlan_allowed(priv->mdev)) return; - mlx5e_vxlan_add_port(priv, be16_to_cpu(port)); + mlx5e_vxlan_queue_work(priv, sa_family, be16_to_cpu(port), 1); } static void mlx5e_del_vxlan_port(struct net_device *netdev, @@ -2138,7 +2168,7 @@ static void mlx5e_del_vxlan_port(struct net_device *netdev, if (!mlx5e_vxlan_allowed(priv->mdev)) return; - mlx5e_vxlan_del_port(priv, be16_to_cpu(port)); + mlx5e_vxlan_queue_work(priv, sa_family, be16_to_cpu(port), 0); } static netdev_features_t mlx5e_vxlan_features_check(struct mlx5e_priv *priv, @@ -2467,10 +2497,14 @@ static void *mlx5e_create_netdev(struct mlx5_core_dev *mdev) priv = netdev_priv(netdev); + priv->wq = create_singlethread_workqueue("mlx5e"); + if (!priv->wq) + goto err_free_netdev; + err = mlx5_alloc_map_uar(mdev, &priv->cq_uar, false); if (err) { mlx5_core_err(mdev, "alloc_map uar failed, %d\n", err); - goto err_free_netdev; + goto err_destroy_wq; } err = mlx5_core_alloc_pd(mdev, &priv->pdn); @@ -2549,7 +2583,7 @@ static void *mlx5e_create_netdev(struct mlx5_core_dev *mdev) vxlan_get_rx_port(netdev); mlx5e_enable_async_events(priv); - schedule_work(&priv->set_rx_mode_work); + queue_work(priv->wq, &priv->set_rx_mode_work); return priv; @@ -2586,6 +2620,9 @@ err_dealloc_pd: err_unmap_free_uar: mlx5_unmap_free_uar(mdev, &priv->cq_uar); +err_destroy_wq: + destroy_workqueue(priv->wq); + err_free_netdev: free_netdev(netdev); @@ -2599,10 +2636,19 @@ static void mlx5e_destroy_netdev(struct mlx5_core_dev *mdev, void *vpriv) set_bit(MLX5E_STATE_DESTROYING, &priv->state); - schedule_work(&priv->set_rx_mode_work); + queue_work(priv->wq, &priv->set_rx_mode_work); mlx5e_disable_async_events(priv); - flush_scheduled_work(); - unregister_netdev(netdev); + flush_workqueue(priv->wq); + if (test_bit(MLX5_INTERFACE_STATE_SHUTDOWN, &mdev->intf_state)) { + netif_device_detach(netdev); + mutex_lock(&priv->state_lock); + if (test_bit(MLX5E_STATE_OPENED, &priv->state)) + mlx5e_close_locked(netdev); + mutex_unlock(&priv->state_lock); + } else { + unregister_netdev(netdev); + } + mlx5e_tc_cleanup(priv); mlx5e_vxlan_cleanup(priv); mlx5e_destroy_flow_tables(priv); @@ -2615,7 +2661,11 @@ static void mlx5e_destroy_netdev(struct mlx5_core_dev *mdev, void *vpriv) mlx5_core_dealloc_transport_domain(priv->mdev, priv->tdn); mlx5_core_dealloc_pd(priv->mdev, priv->pdn); mlx5_unmap_free_uar(priv->mdev, &priv->cq_uar); - free_netdev(netdev); + cancel_delayed_work_sync(&priv->update_stats_work); + destroy_workqueue(priv->wq); + + if (!test_bit(MLX5_INTERFACE_STATE_SHUTDOWN, &mdev->intf_state)) + free_netdev(netdev); } static void *mlx5e_get_netdev(void *vpriv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 5121be4675d1..89cce97d46c6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -1065,33 +1065,6 @@ unlock_fg: return rule; } -static struct mlx5_flow_rule *add_rule_to_auto_fg(struct mlx5_flow_table *ft, - u8 match_criteria_enable, - u32 *match_criteria, - u32 *match_value, - u8 action, - u32 flow_tag, - struct mlx5_flow_destination *dest) -{ - struct mlx5_flow_rule *rule; - struct mlx5_flow_group *g; - - g = create_autogroup(ft, match_criteria_enable, match_criteria); - if (IS_ERR(g)) - return (void *)g; - - rule = add_rule_fg(g, match_value, - action, flow_tag, dest); - if (IS_ERR(rule)) { - /* Remove assumes refcount > 0 and autogroup creates a group - * with a refcount = 0. - */ - tree_get_node(&g->node); - tree_remove_node(&g->node); - } - return rule; -} - static struct mlx5_flow_rule * _mlx5_add_flow_rule(struct mlx5_flow_table *ft, u8 match_criteria_enable, @@ -1119,8 +1092,23 @@ _mlx5_add_flow_rule(struct mlx5_flow_table *ft, goto unlock; } - rule = add_rule_to_auto_fg(ft, match_criteria_enable, match_criteria, - match_value, action, flow_tag, dest); + g = create_autogroup(ft, match_criteria_enable, match_criteria); + if (IS_ERR(g)) { + rule = (void *)g; + goto unlock; + } + + rule = add_rule_fg(g, match_value, + action, flow_tag, dest); + if (IS_ERR(rule)) { + /* Remove assumes refcount > 0 and autogroup creates a group + * with a refcount = 0. + */ + unlock_ref_node(&ft->node); + tree_get_node(&g->node); + tree_remove_node(&g->node); + return rule; + } unlock: unlock_ref_node(&ft->node); return rule; @@ -1288,7 +1276,7 @@ struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev, { struct mlx5_flow_root_namespace *root_ns = dev->priv.root_ns; int prio; - static struct fs_prio *fs_prio; + struct fs_prio *fs_prio; struct mlx5_flow_namespace *ns; if (!root_ns) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 3f3b2fae4991..6892746fd10d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -966,7 +966,7 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) int err; mutex_lock(&dev->intf_state_mutex); - if (dev->interface_state == MLX5_INTERFACE_STATE_UP) { + if (test_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state)) { dev_warn(&dev->pdev->dev, "%s: interface is up, NOP\n", __func__); goto out; @@ -1133,7 +1133,8 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) if (err) pr_info("failed request module on %s\n", MLX5_IB_MOD); - dev->interface_state = MLX5_INTERFACE_STATE_UP; + clear_bit(MLX5_INTERFACE_STATE_DOWN, &dev->intf_state); + set_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state); out: mutex_unlock(&dev->intf_state_mutex); @@ -1207,7 +1208,7 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) } mutex_lock(&dev->intf_state_mutex); - if (dev->interface_state == MLX5_INTERFACE_STATE_DOWN) { + if (test_bit(MLX5_INTERFACE_STATE_DOWN, &dev->intf_state)) { dev_warn(&dev->pdev->dev, "%s: interface is down, NOP\n", __func__); goto out; @@ -1241,7 +1242,8 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) mlx5_cmd_cleanup(dev); out: - dev->interface_state = MLX5_INTERFACE_STATE_DOWN; + clear_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state); + set_bit(MLX5_INTERFACE_STATE_DOWN, &dev->intf_state); mutex_unlock(&dev->intf_state_mutex); return err; } @@ -1452,6 +1454,18 @@ static const struct pci_error_handlers mlx5_err_handler = { .resume = mlx5_pci_resume }; +static void shutdown(struct pci_dev *pdev) +{ + struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + struct mlx5_priv *priv = &dev->priv; + + dev_info(&pdev->dev, "Shutdown was called\n"); + /* Notify mlx5 clients that the kernel is being shut down */ + set_bit(MLX5_INTERFACE_STATE_SHUTDOWN, &dev->intf_state); + mlx5_unload_one(dev, priv); + mlx5_pci_disable_device(dev); +} + static const struct pci_device_id mlx5_core_pci_table[] = { { PCI_VDEVICE(MELLANOX, 0x1011) }, /* Connect-IB */ { PCI_VDEVICE(MELLANOX, 0x1012), MLX5_PCI_DEV_IS_VF}, /* Connect-IB VF */ @@ -1459,6 +1473,8 @@ static const struct pci_device_id mlx5_core_pci_table[] = { { PCI_VDEVICE(MELLANOX, 0x1014), MLX5_PCI_DEV_IS_VF}, /* ConnectX-4 VF */ { PCI_VDEVICE(MELLANOX, 0x1015) }, /* ConnectX-4LX */ { PCI_VDEVICE(MELLANOX, 0x1016), MLX5_PCI_DEV_IS_VF}, /* ConnectX-4LX VF */ + { PCI_VDEVICE(MELLANOX, 0x1017) }, /* ConnectX-5 */ + { PCI_VDEVICE(MELLANOX, 0x1018), MLX5_PCI_DEV_IS_VF}, /* ConnectX-5 VF */ { 0, } }; @@ -1469,6 +1485,7 @@ static struct pci_driver mlx5_core_driver = { .id_table = mlx5_core_pci_table, .probe = init_one, .remove = remove_one, + .shutdown = shutdown, .err_handler = &mlx5_err_handler, .sriov_configure = mlx5_core_sriov_configure, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index ae378c575deb..53cc1e2c693b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -247,8 +247,8 @@ int mlx5_query_port_admin_status(struct mlx5_core_dev *dev, } EXPORT_SYMBOL_GPL(mlx5_query_port_admin_status); -static void mlx5_query_port_mtu(struct mlx5_core_dev *dev, int *admin_mtu, - int *max_mtu, int *oper_mtu, u8 port) +static void mlx5_query_port_mtu(struct mlx5_core_dev *dev, u16 *admin_mtu, + u16 *max_mtu, u16 *oper_mtu, u8 port) { u32 in[MLX5_ST_SZ_DW(pmtu_reg)]; u32 out[MLX5_ST_SZ_DW(pmtu_reg)]; @@ -268,7 +268,7 @@ static void mlx5_query_port_mtu(struct mlx5_core_dev *dev, int *admin_mtu, *admin_mtu = MLX5_GET(pmtu_reg, out, admin_mtu); } -int mlx5_set_port_mtu(struct mlx5_core_dev *dev, int mtu, u8 port) +int mlx5_set_port_mtu(struct mlx5_core_dev *dev, u16 mtu, u8 port) { u32 in[MLX5_ST_SZ_DW(pmtu_reg)]; u32 out[MLX5_ST_SZ_DW(pmtu_reg)]; @@ -283,14 +283,14 @@ int mlx5_set_port_mtu(struct mlx5_core_dev *dev, int mtu, u8 port) } EXPORT_SYMBOL_GPL(mlx5_set_port_mtu); -void mlx5_query_port_max_mtu(struct mlx5_core_dev *dev, int *max_mtu, +void mlx5_query_port_max_mtu(struct mlx5_core_dev *dev, u16 *max_mtu, u8 port) { mlx5_query_port_mtu(dev, NULL, max_mtu, NULL, port); } EXPORT_SYMBOL_GPL(mlx5_query_port_max_mtu); -void mlx5_query_port_oper_mtu(struct mlx5_core_dev *dev, int *oper_mtu, +void mlx5_query_port_oper_mtu(struct mlx5_core_dev *dev, u16 *oper_mtu, u8 port) { mlx5_query_port_mtu(dev, NULL, NULL, oper_mtu, port); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/uar.c b/drivers/net/ethernet/mellanox/mlx5/core/uar.c index 8ba080e441a1..5ff8af472bf5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/uar.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/uar.c @@ -269,8 +269,10 @@ EXPORT_SYMBOL(mlx5_alloc_map_uar); void mlx5_unmap_free_uar(struct mlx5_core_dev *mdev, struct mlx5_uar *uar) { - iounmap(uar->map); - iounmap(uar->bf_map); + if (uar->map) + iounmap(uar->map); + else + iounmap(uar->bf_map); mlx5_cmd_free_uar(mdev, uar->index); } EXPORT_SYMBOL(mlx5_unmap_free_uar); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index bd518405859e..b69dadcfb897 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -196,6 +196,46 @@ int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *mdev, } EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_mac_address); +int mlx5_query_nic_vport_mtu(struct mlx5_core_dev *mdev, u16 *mtu) +{ + int outlen = MLX5_ST_SZ_BYTES(query_nic_vport_context_out); + u32 *out; + int err; + + out = mlx5_vzalloc(outlen); + if (!out) + return -ENOMEM; + + err = mlx5_query_nic_vport_context(mdev, 0, out, outlen); + if (!err) + *mtu = MLX5_GET(query_nic_vport_context_out, out, + nic_vport_context.mtu); + + kvfree(out); + return err; +} +EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_mtu); + +int mlx5_modify_nic_vport_mtu(struct mlx5_core_dev *mdev, u16 mtu) +{ + int inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in); + void *in; + int err; + + in = mlx5_vzalloc(inlen); + if (!in) + return -ENOMEM; + + MLX5_SET(modify_nic_vport_context_in, in, field_select.mtu, 1); + MLX5_SET(modify_nic_vport_context_in, in, nic_vport_context.mtu, mtu); + + err = mlx5_modify_nic_vport_context(mdev, in, inlen); + + kvfree(in); + return err; +} +EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_mtu); + int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev, u32 vport, enum mlx5_list_type list_type, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c index 9f10df25f3cd..f2fd1ef16da7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c @@ -95,21 +95,22 @@ struct mlx5e_vxlan *mlx5e_vxlan_lookup_port(struct mlx5e_priv *priv, u16 port) return vxlan; } -int mlx5e_vxlan_add_port(struct mlx5e_priv *priv, u16 port) +static void mlx5e_vxlan_add_port(struct work_struct *work) { + struct mlx5e_vxlan_work *vxlan_work = + container_of(work, struct mlx5e_vxlan_work, work); + struct mlx5e_priv *priv = vxlan_work->priv; struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan; + u16 port = vxlan_work->port; struct mlx5e_vxlan *vxlan; int err; - err = mlx5e_vxlan_core_add_port_cmd(priv->mdev, port); - if (err) - return err; + if (mlx5e_vxlan_core_add_port_cmd(priv->mdev, port)) + goto free_work; vxlan = kzalloc(sizeof(*vxlan), GFP_KERNEL); - if (!vxlan) { - err = -ENOMEM; + if (!vxlan) goto err_delete_port; - } vxlan->udp_port = port; @@ -119,13 +120,14 @@ int mlx5e_vxlan_add_port(struct mlx5e_priv *priv, u16 port) if (err) goto err_free; - return 0; + goto free_work; err_free: kfree(vxlan); err_delete_port: mlx5e_vxlan_core_del_port_cmd(priv->mdev, port); - return err; +free_work: + kfree(vxlan_work); } static void __mlx5e_vxlan_core_del_port(struct mlx5e_priv *priv, u16 port) @@ -145,12 +147,36 @@ static void __mlx5e_vxlan_core_del_port(struct mlx5e_priv *priv, u16 port) kfree(vxlan); } -void mlx5e_vxlan_del_port(struct mlx5e_priv *priv, u16 port) +static void mlx5e_vxlan_del_port(struct work_struct *work) { - if (!mlx5e_vxlan_lookup_port(priv, port)) - return; + struct mlx5e_vxlan_work *vxlan_work = + container_of(work, struct mlx5e_vxlan_work, work); + struct mlx5e_priv *priv = vxlan_work->priv; + u16 port = vxlan_work->port; __mlx5e_vxlan_core_del_port(priv, port); + + kfree(vxlan_work); +} + +void mlx5e_vxlan_queue_work(struct mlx5e_priv *priv, sa_family_t sa_family, + u16 port, int add) +{ + struct mlx5e_vxlan_work *vxlan_work; + + vxlan_work = kmalloc(sizeof(*vxlan_work), GFP_ATOMIC); + if (!vxlan_work) + return; + + if (add) + INIT_WORK(&vxlan_work->work, mlx5e_vxlan_add_port); + else + INIT_WORK(&vxlan_work->work, mlx5e_vxlan_del_port); + + vxlan_work->priv = priv; + vxlan_work->port = port; + vxlan_work->sa_family = sa_family; + queue_work(priv->wq, &vxlan_work->work); } void mlx5e_vxlan_cleanup(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h index a01685056ab1..129f3527aa14 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h @@ -39,6 +39,13 @@ struct mlx5e_vxlan { u16 udp_port; }; +struct mlx5e_vxlan_work { + struct work_struct work; + struct mlx5e_priv *priv; + sa_family_t sa_family; + u16 port; +}; + static inline bool mlx5e_vxlan_allowed(struct mlx5_core_dev *mdev) { return (MLX5_CAP_ETH(mdev, tunnel_stateless_vxlan) && @@ -46,8 +53,8 @@ static inline bool mlx5e_vxlan_allowed(struct mlx5_core_dev *mdev) } void mlx5e_vxlan_init(struct mlx5e_priv *priv); -int mlx5e_vxlan_add_port(struct mlx5e_priv *priv, u16 port); -void mlx5e_vxlan_del_port(struct mlx5e_priv *priv, u16 port); +void mlx5e_vxlan_queue_work(struct mlx5e_priv *priv, sa_family_t sa_family, + u16 port, int add); struct mlx5e_vxlan *mlx5e_vxlan_lookup_port(struct mlx5e_priv *priv, u16 port); void mlx5e_vxlan_cleanup(struct mlx5e_priv *priv); diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index 270c9eeb7ab6..6d1a956e3f77 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -2668,9 +2668,9 @@ static int myri10ge_close(struct net_device *dev) del_timer_sync(&mgp->watchdog_timer); mgp->running = MYRI10GE_ETH_STOPPING; - local_bh_disable(); /* myri10ge_ss_lock_napi needs bh disabled */ for (i = 0; i < mgp->num_slices; i++) { napi_disable(&mgp->ss[i].napi); + local_bh_disable(); /* myri10ge_ss_lock_napi needs this */ /* Lock the slice to prevent the busy_poll handler from * accessing it. Later when we bring the NIC up, myri10ge_open * resets the slice including this lock. @@ -2679,8 +2679,8 @@ static int myri10ge_close(struct net_device *dev) pr_info("Slice %d locked\n", i); mdelay(1); } + local_bh_enable(); } - local_bh_enable(); netif_carrier_off(dev); netif_tx_stop_all_queues(dev); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 55007f1e6bbc..caf6ddb7ea76 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -37,8 +37,8 @@ #define _QLCNIC_LINUX_MAJOR 5 #define _QLCNIC_LINUX_MINOR 3 -#define _QLCNIC_LINUX_SUBVERSION 63 -#define QLCNIC_LINUX_VERSIONID "5.3.63" +#define _QLCNIC_LINUX_SUBVERSION 64 +#define QLCNIC_LINUX_VERSIONID "5.3.64" #define QLCNIC_DRV_IDC_VER 0x01 #define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\ (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION)) diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 98d33d462c6c..1681084cc96f 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -1920,6 +1920,10 @@ static int efx_ef10_alloc_rss_context(struct efx_nic *efx, u32 *context, return 0; } + if (nic_data->datapath_caps & + 1 << MC_CMD_GET_CAPABILITIES_OUT_RX_RSS_LIMITED_LBN) + return -EOPNOTSUPP; + MCDI_SET_DWORD(inbuf, RSS_CONTEXT_ALLOC_IN_UPSTREAM_PORT_ID, nic_data->vport_id); MCDI_SET_DWORD(inbuf, RSS_CONTEXT_ALLOC_IN_TYPE, alloc_type); @@ -2923,9 +2927,16 @@ static void efx_ef10_filter_push_prep(struct efx_nic *efx, bool replacing) { struct efx_ef10_nic_data *nic_data = efx->nic_data; + u32 flags = spec->flags; memset(inbuf, 0, MC_CMD_FILTER_OP_IN_LEN); + /* Remove RSS flag if we don't have an RSS context. */ + if (flags & EFX_FILTER_FLAG_RX_RSS && + spec->rss_context == EFX_FILTER_RSS_CONTEXT_DEFAULT && + nic_data->rx_rss_context == EFX_EF10_RSS_CONTEXT_INVALID) + flags &= ~EFX_FILTER_FLAG_RX_RSS; + if (replacing) { MCDI_SET_DWORD(inbuf, FILTER_OP_IN_OP, MC_CMD_FILTER_OP_IN_OP_REPLACE); @@ -2985,10 +2996,10 @@ static void efx_ef10_filter_push_prep(struct efx_nic *efx, spec->dmaq_id == EFX_FILTER_RX_DMAQ_ID_DROP ? 0 : spec->dmaq_id); MCDI_SET_DWORD(inbuf, FILTER_OP_IN_RX_MODE, - (spec->flags & EFX_FILTER_FLAG_RX_RSS) ? + (flags & EFX_FILTER_FLAG_RX_RSS) ? MC_CMD_FILTER_OP_IN_RX_MODE_RSS : MC_CMD_FILTER_OP_IN_RX_MODE_SIMPLE); - if (spec->flags & EFX_FILTER_FLAG_RX_RSS) + if (flags & EFX_FILTER_FLAG_RX_RSS) MCDI_SET_DWORD(inbuf, FILTER_OP_IN_RX_CONTEXT, spec->rss_context != EFX_FILTER_RSS_CONTEXT_DEFAULT ? diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c index 44022b1845ce..afb90d129cb6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c @@ -49,7 +49,6 @@ struct socfpga_dwmac { u32 reg_shift; struct device *dev; struct regmap *sys_mgr_base_addr; - struct reset_control *stmmac_rst; void __iomem *splitter_base; bool f2h_ptp_ref_clk; }; @@ -92,15 +91,6 @@ static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device * struct device_node *np_splitter; struct resource res_splitter; - dwmac->stmmac_rst = devm_reset_control_get(dev, - STMMAC_RESOURCE_NAME); - if (IS_ERR(dwmac->stmmac_rst)) { - dev_info(dev, "Could not get reset control!\n"); - if (PTR_ERR(dwmac->stmmac_rst) == -EPROBE_DEFER) - return -EPROBE_DEFER; - dwmac->stmmac_rst = NULL; - } - dwmac->interface = of_get_phy_mode(np); sys_mgr_base_addr = syscon_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon"); @@ -194,30 +184,23 @@ static int socfpga_dwmac_setup(struct socfpga_dwmac *dwmac) return 0; } -static void socfpga_dwmac_exit(struct platform_device *pdev, void *priv) -{ - struct socfpga_dwmac *dwmac = priv; - - /* On socfpga platform exit, assert and hold reset to the - * enet controller - the default state after a hard reset. - */ - if (dwmac->stmmac_rst) - reset_control_assert(dwmac->stmmac_rst); -} - static int socfpga_dwmac_init(struct platform_device *pdev, void *priv) { - struct socfpga_dwmac *dwmac = priv; + struct socfpga_dwmac *dwmac = priv; struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *stpriv = NULL; int ret = 0; - if (ndev) - stpriv = netdev_priv(ndev); + if (!ndev) + return -EINVAL; + + stpriv = netdev_priv(ndev); + if (!stpriv) + return -EINVAL; /* Assert reset to the enet controller before changing the phy mode */ - if (dwmac->stmmac_rst) - reset_control_assert(dwmac->stmmac_rst); + if (stpriv->stmmac_rst) + reset_control_assert(stpriv->stmmac_rst); /* Setup the phy mode in the system manager registers according to * devicetree configuration @@ -227,8 +210,8 @@ static int socfpga_dwmac_init(struct platform_device *pdev, void *priv) /* Deassert reset for the phy configuration to be sampled by * the enet controller, and operation to start in requested mode */ - if (dwmac->stmmac_rst) - reset_control_deassert(dwmac->stmmac_rst); + if (stpriv->stmmac_rst) + reset_control_deassert(stpriv->stmmac_rst); /* Before the enet controller is suspended, the phy is suspended. * This causes the phy clock to be gated. The enet controller is @@ -245,7 +228,7 @@ static int socfpga_dwmac_init(struct platform_device *pdev, void *priv) * control register 0, and can be modified by the phy driver * framework. */ - if (stpriv && stpriv->phydev) + if (stpriv->phydev) phy_resume(stpriv->phydev); return ret; @@ -285,14 +268,13 @@ static int socfpga_dwmac_probe(struct platform_device *pdev) plat_dat->bsp_priv = dwmac; plat_dat->init = socfpga_dwmac_init; - plat_dat->exit = socfpga_dwmac_exit; plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed; - ret = socfpga_dwmac_init(pdev, plat_dat->bsp_priv); - if (ret) - return ret; + ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + if (!ret) + ret = socfpga_dwmac_init(pdev, dwmac); - return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + return ret; } static const struct of_device_id socfpga_dwmac_match[] = { diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index bbb77cd8ad67..e2fcdf1eec44 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -367,7 +367,6 @@ struct cpsw_priv { spinlock_t lock; struct platform_device *pdev; struct net_device *ndev; - struct device_node *phy_node; struct napi_struct napi_rx; struct napi_struct napi_tx; struct device *dev; @@ -1148,25 +1147,34 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, 1 << slave_port, 0, 0, ALE_MCAST_FWD_2); - if (priv->phy_node) - slave->phy = of_phy_connect(priv->ndev, priv->phy_node, + if (slave->data->phy_node) { + slave->phy = of_phy_connect(priv->ndev, slave->data->phy_node, &cpsw_adjust_link, 0, slave->data->phy_if); - else + if (!slave->phy) { + dev_err(priv->dev, "phy \"%s\" not found on slave %d\n", + slave->data->phy_node->full_name, + slave->slave_num); + return; + } + } else { slave->phy = phy_connect(priv->ndev, slave->data->phy_id, &cpsw_adjust_link, slave->data->phy_if); - if (IS_ERR(slave->phy)) { - dev_err(priv->dev, "phy %s not found on slave %d\n", - slave->data->phy_id, slave->slave_num); - slave->phy = NULL; - } else { - phy_attached_info(slave->phy); + if (IS_ERR(slave->phy)) { + dev_err(priv->dev, + "phy \"%s\" not found on slave %d, err %ld\n", + slave->data->phy_id, slave->slave_num, + PTR_ERR(slave->phy)); + slave->phy = NULL; + return; + } + } - phy_start(slave->phy); + phy_attached_info(slave->phy); - /* Configure GMII_SEL register */ - cpsw_phy_sel(&priv->pdev->dev, slave->phy->interface, - slave->slave_num); - } + phy_start(slave->phy); + + /* Configure GMII_SEL register */ + cpsw_phy_sel(&priv->pdev->dev, slave->phy->interface, slave->slave_num); } static inline void cpsw_add_default_vlan(struct cpsw_priv *priv) @@ -1940,12 +1948,11 @@ static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv, slave->port_vlan = data->dual_emac_res_vlan; } -static int cpsw_probe_dt(struct cpsw_priv *priv, +static int cpsw_probe_dt(struct cpsw_platform_data *data, struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; struct device_node *slave_node; - struct cpsw_platform_data *data = &priv->data; int i = 0, ret; u32 prop; @@ -2033,25 +2040,21 @@ static int cpsw_probe_dt(struct cpsw_priv *priv, if (strcmp(slave_node->name, "slave")) continue; - priv->phy_node = of_parse_phandle(slave_node, "phy-handle", 0); + slave_data->phy_node = of_parse_phandle(slave_node, + "phy-handle", 0); parp = of_get_property(slave_node, "phy_id", &lenp); - if (of_phy_is_fixed_link(slave_node)) { - struct device_node *phy_node; - struct phy_device *phy_dev; - + if (slave_data->phy_node) { + dev_dbg(&pdev->dev, + "slave[%d] using phy-handle=\"%s\"\n", + i, slave_data->phy_node->full_name); + } else if (of_phy_is_fixed_link(slave_node)) { /* In the case of a fixed PHY, the DT node associated * to the PHY is the Ethernet MAC DT node. */ ret = of_phy_register_fixed_link(slave_node); if (ret) return ret; - phy_node = of_node_get(slave_node); - phy_dev = of_phy_find_device(phy_node); - if (!phy_dev) - return -ENODEV; - snprintf(slave_data->phy_id, sizeof(slave_data->phy_id), - PHY_ID_FMT, phy_dev->mdio.bus->id, - phy_dev->mdio.addr); + slave_data->phy_node = of_node_get(slave_node); } else if (parp) { u32 phyid; struct device_node *mdio_node; @@ -2072,7 +2075,9 @@ static int cpsw_probe_dt(struct cpsw_priv *priv, snprintf(slave_data->phy_id, sizeof(slave_data->phy_id), PHY_ID_FMT, mdio->name, phyid); } else { - dev_err(&pdev->dev, "No slave[%d] phy_id or fixed-link property\n", i); + dev_err(&pdev->dev, + "No slave[%d] phy_id, phy-handle, or fixed-link property\n", + i); goto no_phy_slave; } slave_data->phy_if = of_get_phy_mode(slave_node); @@ -2275,7 +2280,7 @@ static int cpsw_probe(struct platform_device *pdev) /* Select default pin state */ pinctrl_pm_select_default_state(&pdev->dev); - if (cpsw_probe_dt(priv, pdev)) { + if (cpsw_probe_dt(&priv->data, pdev)) { dev_err(&pdev->dev, "cpsw: platform data missing\n"); ret = -ENODEV; goto clean_runtime_disable_ret; diff --git a/drivers/net/ethernet/ti/cpsw.h b/drivers/net/ethernet/ti/cpsw.h index 442a7038e660..e50afd1b2eda 100644 --- a/drivers/net/ethernet/ti/cpsw.h +++ b/drivers/net/ethernet/ti/cpsw.h @@ -18,6 +18,7 @@ #include <linux/phy.h> struct cpsw_slave_data { + struct device_node *phy_node; char phy_id[MII_BUS_ID_SIZE]; int phy_if; u8 mac_addr[ETH_ALEN]; diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 58d58f002559..f56d66e6ec15 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1512,7 +1512,10 @@ static int emac_devioctl(struct net_device *ndev, struct ifreq *ifrq, int cmd) /* TODO: Add phy read and write and private statistics get feature */ - return phy_mii_ioctl(priv->phydev, ifrq, cmd); + if (priv->phydev) + return phy_mii_ioctl(priv->phydev, ifrq, cmd); + else + return -EOPNOTSUPP; } static int match_first_device(struct device *dev, void *data) diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c index 13214a6492ac..743b18266a7c 100644 --- a/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c +++ b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c @@ -1622,7 +1622,7 @@ static void gelic_wl_scan_complete_event(struct gelic_wl_info *wl) continue; /* copy hw scan info */ - memcpy(target->hwinfo, scan_info, scan_info->size); + memcpy(target->hwinfo, scan_info, be16_to_cpu(scan_info->size)); target->essid_len = strnlen(scan_info->essid, sizeof(scan_info->essid)); target->rate_len = 0; diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 84d3e5ca8817..c6385617bfb2 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -880,12 +880,12 @@ static struct sk_buff *macsec_decrypt(struct sk_buff *skb, macsec_skb_cb(skb)->valid = false; skb = skb_share_check(skb, GFP_ATOMIC); if (!skb) - return NULL; + return ERR_PTR(-ENOMEM); req = aead_request_alloc(rx_sa->key.tfm, GFP_ATOMIC); if (!req) { kfree_skb(skb); - return NULL; + return ERR_PTR(-ENOMEM); } hdr = (struct macsec_eth_header *)skb->data; @@ -905,7 +905,7 @@ static struct sk_buff *macsec_decrypt(struct sk_buff *skb, skb = skb_unshare(skb, GFP_ATOMIC); if (!skb) { aead_request_free(req); - return NULL; + return ERR_PTR(-ENOMEM); } } else { /* integrity only: all headers + data authenticated */ @@ -921,14 +921,14 @@ static struct sk_buff *macsec_decrypt(struct sk_buff *skb, dev_hold(dev); ret = crypto_aead_decrypt(req); if (ret == -EINPROGRESS) { - return NULL; + return ERR_PTR(ret); } else if (ret != 0) { /* decryption/authentication failed * 10.6 if validateFrames is disabled, deliver anyway */ if (ret != -EBADMSG) { kfree_skb(skb); - skb = NULL; + skb = ERR_PTR(ret); } } else { macsec_skb_cb(skb)->valid = true; @@ -1146,8 +1146,10 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) secy->validate_frames != MACSEC_VALIDATE_DISABLED) skb = macsec_decrypt(skb, dev, rx_sa, sci, secy); - if (!skb) { - macsec_rxsa_put(rx_sa); + if (IS_ERR(skb)) { + /* the decrypt callback needs the reference */ + if (PTR_ERR(skb) != -EINPROGRESS) + macsec_rxsa_put(rx_sa); rcu_read_unlock(); *pskb = NULL; return RX_HANDLER_CONSUMED; @@ -1161,7 +1163,8 @@ deliver: macsec_extra_len(macsec_skb_cb(skb)->has_sci)); macsec_reset_skb(skb, secy->netdev); - macsec_rxsa_put(rx_sa); + if (rx_sa) + macsec_rxsa_put(rx_sa); count_rx(dev, skb->len); rcu_read_unlock(); @@ -1622,8 +1625,9 @@ static int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info) } rx_sa = kmalloc(sizeof(*rx_sa), GFP_KERNEL); - if (init_rx_sa(rx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]), secy->key_len, - secy->icv_len)) { + if (!rx_sa || init_rx_sa(rx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]), + secy->key_len, secy->icv_len)) { + kfree(rx_sa); rtnl_unlock(); return -ENOMEM; } @@ -1768,6 +1772,7 @@ static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info) tx_sa = kmalloc(sizeof(*tx_sa), GFP_KERNEL); if (!tx_sa || init_tx_sa(tx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]), secy->key_len, secy->icv_len)) { + kfree(tx_sa); rtnl_unlock(); return -ENOMEM; } @@ -2227,7 +2232,8 @@ static int nla_put_secy(struct macsec_secy *secy, struct sk_buff *skb) return 1; if (nla_put_sci(skb, MACSEC_SECY_ATTR_SCI, secy->sci) || - nla_put_u64(skb, MACSEC_SECY_ATTR_CIPHER_SUITE, DEFAULT_CIPHER_ID) || + nla_put_u64(skb, MACSEC_SECY_ATTR_CIPHER_SUITE, + MACSEC_DEFAULT_CIPHER_ID) || nla_put_u8(skb, MACSEC_SECY_ATTR_ICV_LEN, secy->icv_len) || nla_put_u8(skb, MACSEC_SECY_ATTR_OPER, secy->operational) || nla_put_u8(skb, MACSEC_SECY_ATTR_PROTECT, secy->protect_frames) || @@ -2268,7 +2274,7 @@ static int dump_secy(struct macsec_secy *secy, struct net_device *dev, if (!hdr) return -EMSGSIZE; - rtnl_lock(); + genl_dump_check_consistent(cb, hdr, &macsec_fam); if (nla_put_u32(skb, MACSEC_ATTR_IFINDEX, dev->ifindex)) goto nla_put_failure; @@ -2429,18 +2435,17 @@ static int dump_secy(struct macsec_secy *secy, struct net_device *dev, nla_nest_end(skb, rxsc_list); - rtnl_unlock(); - genlmsg_end(skb, hdr); return 0; nla_put_failure: - rtnl_unlock(); genlmsg_cancel(skb, hdr); return -EMSGSIZE; } +static int macsec_generation = 1; /* protected by RTNL */ + static int macsec_dump_txsc(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); @@ -2450,6 +2455,10 @@ static int macsec_dump_txsc(struct sk_buff *skb, struct netlink_callback *cb) dev_idx = cb->args[0]; d = 0; + rtnl_lock(); + + cb->seq = macsec_generation; + for_each_netdev(net, dev) { struct macsec_secy *secy; @@ -2467,6 +2476,7 @@ next: } done: + rtnl_unlock(); cb->args[0] = d; return skb->len; } @@ -2920,10 +2930,14 @@ static void macsec_dellink(struct net_device *dev, struct list_head *head) struct net_device *real_dev = macsec->real_dev; struct macsec_rxh_data *rxd = macsec_data_rtnl(real_dev); + macsec_generation++; + unregister_netdevice_queue(dev, head); list_del_rcu(&macsec->secys); - if (list_empty(&rxd->secys)) + if (list_empty(&rxd->secys)) { netdev_rx_handler_unregister(real_dev); + kfree(rxd); + } macsec_del_dev(macsec); } @@ -2945,8 +2959,10 @@ static int register_macsec_dev(struct net_device *real_dev, err = netdev_rx_handler_register(real_dev, macsec_handle_frame, rxd); - if (err < 0) + if (err < 0) { + kfree(rxd); return err; + } } list_add_tail_rcu(&macsec->secys, &rxd->secys); @@ -3066,6 +3082,8 @@ static int macsec_newlink(struct net *net, struct net_device *dev, if (err < 0) goto del_dev; + macsec_generation++; + dev_hold(real_dev); return 0; @@ -3079,7 +3097,7 @@ unregister: static int macsec_validate_attr(struct nlattr *tb[], struct nlattr *data[]) { - u64 csid = DEFAULT_CIPHER_ID; + u64 csid = MACSEC_DEFAULT_CIPHER_ID; u8 icv_len = DEFAULT_ICV_LEN; int flag; bool es, scb, sci; @@ -3094,8 +3112,8 @@ static int macsec_validate_attr(struct nlattr *tb[], struct nlattr *data[]) icv_len = nla_get_u8(data[IFLA_MACSEC_ICV_LEN]); switch (csid) { - case DEFAULT_CIPHER_ID: - case DEFAULT_CIPHER_ALT: + case MACSEC_DEFAULT_CIPHER_ID: + case MACSEC_DEFAULT_CIPHER_ALT: if (icv_len < MACSEC_MIN_ICV_LEN || icv_len > MACSEC_MAX_ICV_LEN) return -EINVAL; @@ -3129,8 +3147,8 @@ static int macsec_validate_attr(struct nlattr *tb[], struct nlattr *data[]) nla_get_u8(data[IFLA_MACSEC_VALIDATION]) > MACSEC_VALIDATE_MAX) return -EINVAL; - if ((data[IFLA_MACSEC_PROTECT] && - nla_get_u8(data[IFLA_MACSEC_PROTECT])) && + if ((data[IFLA_MACSEC_REPLAY_PROTECT] && + nla_get_u8(data[IFLA_MACSEC_REPLAY_PROTECT])) && !data[IFLA_MACSEC_WINDOW]) return -EINVAL; @@ -3168,7 +3186,8 @@ static int macsec_fill_info(struct sk_buff *skb, if (nla_put_sci(skb, IFLA_MACSEC_SCI, secy->sci) || nla_put_u8(skb, IFLA_MACSEC_ICV_LEN, secy->icv_len) || - nla_put_u64(skb, IFLA_MACSEC_CIPHER_SUITE, DEFAULT_CIPHER_ID) || + nla_put_u64(skb, IFLA_MACSEC_CIPHER_SUITE, + MACSEC_DEFAULT_CIPHER_ID) || nla_put_u8(skb, IFLA_MACSEC_ENCODING_SA, tx_sc->encoding_sa) || nla_put_u8(skb, IFLA_MACSEC_ENCRYPT, tx_sc->encrypt) || nla_put_u8(skb, IFLA_MACSEC_PROTECT, secy->protect_frames) || diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index b3ffaee30858..f279a897a5c7 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -359,27 +359,25 @@ static void at803x_link_change_notify(struct phy_device *phydev) * in the FIFO. In such cases, the FIFO enters an error mode it * cannot recover from by software. */ - if (phydev->drv->phy_id == ATH8030_PHY_ID) { - if (phydev->state == PHY_NOLINK) { - if (priv->gpiod_reset && !priv->phy_reset) { - struct at803x_context context; - - at803x_context_save(phydev, &context); - - gpiod_set_value(priv->gpiod_reset, 1); - msleep(1); - gpiod_set_value(priv->gpiod_reset, 0); - msleep(1); - - at803x_context_restore(phydev, &context); - - phydev_dbg(phydev, "%s(): phy was reset\n", - __func__); - priv->phy_reset = true; - } - } else { - priv->phy_reset = false; + if (phydev->state == PHY_NOLINK) { + if (priv->gpiod_reset && !priv->phy_reset) { + struct at803x_context context; + + at803x_context_save(phydev, &context); + + gpiod_set_value(priv->gpiod_reset, 1); + msleep(1); + gpiod_set_value(priv->gpiod_reset, 0); + msleep(1); + + at803x_context_restore(phydev, &context); + + phydev_dbg(phydev, "%s(): phy was reset\n", + __func__); + priv->phy_reset = true; } + } else { + priv->phy_reset = false; } } @@ -391,7 +389,6 @@ static struct phy_driver at803x_driver[] = { .phy_id_mask = 0xffffffef, .probe = at803x_probe, .config_init = at803x_config_init, - .link_change_notify = at803x_link_change_notify, .set_wol = at803x_set_wol, .get_wol = at803x_get_wol, .suspend = at803x_suspend, @@ -427,7 +424,6 @@ static struct phy_driver at803x_driver[] = { .phy_id_mask = 0xffffffef, .probe = at803x_probe, .config_init = at803x_config_init, - .link_change_notify = at803x_link_change_notify, .set_wol = at803x_set_wol, .get_wol = at803x_get_wol, .suspend = at803x_suspend, diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index f20890ee03f3..f64778ad9753 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -269,6 +269,7 @@ struct skb_data { /* skb->cb is one of these */ struct lan78xx_net *dev; enum skb_state state; size_t length; + int num_of_packet; }; struct usb_context { @@ -1803,7 +1804,34 @@ static void lan78xx_remove_mdio(struct lan78xx_net *dev) static void lan78xx_link_status_change(struct net_device *net) { - /* nothing to do */ + struct phy_device *phydev = net->phydev; + int ret, temp; + + /* At forced 100 F/H mode, chip may fail to set mode correctly + * when cable is switched between long(~50+m) and short one. + * As workaround, set to 10 before setting to 100 + * at forced 100 F/H mode. + */ + if (!phydev->autoneg && (phydev->speed == 100)) { + /* disable phy interrupt */ + temp = phy_read(phydev, LAN88XX_INT_MASK); + temp &= ~LAN88XX_INT_MASK_MDINTPIN_EN_; + ret = phy_write(phydev, LAN88XX_INT_MASK, temp); + + temp = phy_read(phydev, MII_BMCR); + temp &= ~(BMCR_SPEED100 | BMCR_SPEED1000); + phy_write(phydev, MII_BMCR, temp); /* set to 10 first */ + temp |= BMCR_SPEED100; + phy_write(phydev, MII_BMCR, temp); /* set to 100 later */ + + /* clear pending interrupt generated while workaround */ + temp = phy_read(phydev, LAN88XX_INT_STS); + + /* enable phy interrupt back */ + temp = phy_read(phydev, LAN88XX_INT_MASK); + temp |= LAN88XX_INT_MASK_MDINTPIN_EN_; + ret = phy_write(phydev, LAN88XX_INT_MASK, temp); + } } static int lan78xx_phy_init(struct lan78xx_net *dev) @@ -2464,7 +2492,7 @@ static void tx_complete(struct urb *urb) struct lan78xx_net *dev = entry->dev; if (urb->status == 0) { - dev->net->stats.tx_packets++; + dev->net->stats.tx_packets += entry->num_of_packet; dev->net->stats.tx_bytes += entry->length; } else { dev->net->stats.tx_errors++; @@ -2681,10 +2709,11 @@ void lan78xx_skb_return(struct lan78xx_net *dev, struct sk_buff *skb) return; } - skb->protocol = eth_type_trans(skb, dev->net); dev->net->stats.rx_packets++; dev->net->stats.rx_bytes += skb->len; + skb->protocol = eth_type_trans(skb, dev->net); + netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n", skb->len + sizeof(struct ethhdr), skb->protocol); memset(skb->cb, 0, sizeof(struct skb_data)); @@ -2934,13 +2963,16 @@ static void lan78xx_tx_bh(struct lan78xx_net *dev) skb_totallen = 0; pkt_cnt = 0; + count = 0; + length = 0; for (skb = tqp->next; pkt_cnt < tqp->qlen; skb = skb->next) { if (skb_is_gso(skb)) { if (pkt_cnt) { /* handle previous packets first */ break; } - length = skb->len; + count = 1; + length = skb->len - TX_OVERHEAD; skb2 = skb_dequeue(tqp); goto gso_skb; } @@ -2961,14 +2993,13 @@ static void lan78xx_tx_bh(struct lan78xx_net *dev) for (count = pos = 0; count < pkt_cnt; count++) { skb2 = skb_dequeue(tqp); if (skb2) { + length += (skb2->len - TX_OVERHEAD); memcpy(skb->data + pos, skb2->data, skb2->len); pos += roundup(skb2->len, sizeof(u32)); dev_kfree_skb(skb2); } } - length = skb_totallen; - gso_skb: urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { @@ -2980,6 +3011,7 @@ gso_skb: entry->urb = urb; entry->dev = dev; entry->length = length; + entry->num_of_packet = count; spin_lock_irqsave(&dev->txq.lock, flags); ret = usb_autopm_get_interface_async(dev->intf); diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index f84080215915..82129eef7774 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -411,7 +411,7 @@ static int enable_net_traffic(struct net_device *dev, struct usb_device *usb) int ret; read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart); - data[0] = 0xc9; + data[0] = 0xc8; /* TX & RX enable, append status, no CRC */ data[1] = 0; if (linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL)) data[1] |= 0x20; /* set full duplex */ @@ -497,7 +497,7 @@ static void read_bulk_callback(struct urb *urb) pkt_len = buf[count - 3] << 8; pkt_len += buf[count - 4]; pkt_len &= 0xfff; - pkt_len -= 8; + pkt_len -= 4; } /* @@ -528,7 +528,7 @@ static void read_bulk_callback(struct urb *urb) goon: usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), - pegasus->rx_skb->data, PEGASUS_MTU + 8, + pegasus->rx_skb->data, PEGASUS_MTU, read_bulk_callback, pegasus); rx_status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC); if (rx_status == -ENODEV) @@ -569,7 +569,7 @@ static void rx_fixup(unsigned long data) } usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), - pegasus->rx_skb->data, PEGASUS_MTU + 8, + pegasus->rx_skb->data, PEGASUS_MTU, read_bulk_callback, pegasus); try_again: status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC); @@ -823,7 +823,7 @@ static int pegasus_open(struct net_device *net) usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), - pegasus->rx_skb->data, PEGASUS_MTU + 8, + pegasus->rx_skb->data, PEGASUS_MTU, read_bulk_callback, pegasus); if ((res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL))) { if (res == -ENODEV) diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 30033dbe6662..c369db99c005 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -29,6 +29,7 @@ #include <linux/crc32.h> #include <linux/usb/usbnet.h> #include <linux/slab.h> +#include <linux/of_net.h> #include "smsc75xx.h" #define SMSC_CHIPNAME "smsc75xx" @@ -761,6 +762,15 @@ static int smsc75xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) static void smsc75xx_init_mac_address(struct usbnet *dev) { + const u8 *mac_addr; + + /* maybe the boot loader passed the MAC address in devicetree */ + mac_addr = of_get_mac_address(dev->udev->dev.of_node); + if (mac_addr) { + memcpy(dev->net->dev_addr, mac_addr, ETH_ALEN); + return; + } + /* try reading mac address from EEPROM */ if (smsc75xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, dev->net->dev_addr) == 0) { @@ -772,7 +782,7 @@ static void smsc75xx_init_mac_address(struct usbnet *dev) } } - /* no eeprom, or eeprom values are invalid. generate random MAC */ + /* no useful static MAC address found. generate a random one */ eth_hw_addr_random(dev->net); netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n"); } diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 66b3ab9f614e..2edc2bc6d1b9 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -29,6 +29,7 @@ #include <linux/crc32.h> #include <linux/usb/usbnet.h> #include <linux/slab.h> +#include <linux/of_net.h> #include "smsc95xx.h" #define SMSC_CHIPNAME "smsc95xx" @@ -765,6 +766,15 @@ static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) static void smsc95xx_init_mac_address(struct usbnet *dev) { + const u8 *mac_addr; + + /* maybe the boot loader passed the MAC address in devicetree */ + mac_addr = of_get_mac_address(dev->udev->dev.of_node); + if (mac_addr) { + memcpy(dev->net->dev_addr, mac_addr, ETH_ALEN); + return; + } + /* try reading mac address from EEPROM */ if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, dev->net->dev_addr) == 0) { @@ -775,7 +785,7 @@ static void smsc95xx_init_mac_address(struct usbnet *dev) } } - /* no eeprom, or eeprom values are invalid. generate random MAC */ + /* no useful static MAC address found. generate a random one */ eth_hw_addr_random(dev->net); netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n"); } diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 8f8793004b9f..1b271b99c49e 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -274,6 +274,9 @@ void ar5008_hw_cmn_spur_mitigate(struct ath_hw *ah, }; static const int inc[4] = { 0, 100, 0, 0 }; + memset(&mask_m, 0, sizeof(int8_t) * 123); + memset(&mask_p, 0, sizeof(int8_t) * 123); + cur_bin = -6000; upper = bin + 100; lower = bin - 100; @@ -424,14 +427,9 @@ static void ar5008_hw_spur_mitigate(struct ath_hw *ah, int tmp, new; int i; - int8_t mask_m[123]; - int8_t mask_p[123]; int cur_bb_spur; bool is2GHz = IS_CHAN_2GHZ(chan); - memset(&mask_m, 0, sizeof(int8_t) * 123); - memset(&mask_p, 0, sizeof(int8_t) * 123); - for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz); if (AR_NO_SPUR == cur_bb_spur) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index db6624527d99..53d7445a5d12 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -178,14 +178,9 @@ static void ar9002_hw_spur_mitigate(struct ath_hw *ah, int i; struct chan_centers centers; - int8_t mask_m[123]; - int8_t mask_p[123]; int cur_bb_spur; bool is2GHz = IS_CHAN_2GHZ(chan); - memset(&mask_m, 0, sizeof(int8_t) * 123); - memset(&mask_p, 0, sizeof(int8_t) * 123); - ath9k_hw_get_channel_centers(ah, chan, ¢ers); freq = centers.synth_center; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c index 97be104d1203..b5c57eebf995 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c @@ -93,7 +93,7 @@ #define IWL8260_SMEM_OFFSET 0x400000 #define IWL8260_SMEM_LEN 0x68000 -#define IWL8000_FW_PRE "iwlwifi-8000" +#define IWL8000_FW_PRE "iwlwifi-8000C-" #define IWL8000_MODULE_FIRMWARE(api) \ IWL8000_FW_PRE "-" __stringify(api) ".ucode" diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index f899666acb41..9e45bf9c6071 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -238,19 +238,6 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first) snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s%s.ucode", name_pre, tag); - /* - * Starting 8000B - FW name format has changed. This overwrites the - * previous name and uses the new format. - */ - if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { - char rev_step = 'A' + CSR_HW_REV_STEP(drv->trans->hw_rev); - - if (rev_step != 'A') - snprintf(drv->firmware_name, - sizeof(drv->firmware_name), "%s%c-%s.ucode", - name_pre, rev_step, tag); - } - IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n", (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) ? "EXPERIMENTAL " : "", @@ -1060,11 +1047,18 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, return -EINVAL; } - if (WARN(fw_has_capa(capa, IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT) && - !gscan_capa, - "GSCAN is supported but capabilities TLV is unavailable\n")) + /* + * If ucode advertises that it supports GSCAN but GSCAN + * capabilities TLV is not present, or if it has an old format, + * warn and continue without GSCAN. + */ + if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT) && + !gscan_capa) { + IWL_DEBUG_INFO(drv, + "GSCAN is supported but capabilities TLV is unavailable\n"); __clear_bit((__force long)IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT, capa->_capa); + } return 0; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index 4856eac120f6..6938cd37be57 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -526,7 +526,8 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len; /* Make room for fw's virtual image pages, if it exists */ - if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) + if (mvm->fw->img[mvm->cur_ucode].paging_mem_size && + mvm->fw_paging_db[0].fw_paging_block) file_len += mvm->num_of_paging_blk * (sizeof(*dump_data) + sizeof(struct iwl_fw_error_dump_paging) + @@ -643,7 +644,8 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) } /* Dump fw's virtual image */ - if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) { + if (mvm->fw->img[mvm->cur_ucode].paging_mem_size && + mvm->fw_paging_db[0].fw_paging_block) { for (i = 1; i < mvm->num_of_paging_blk + 1; i++) { struct iwl_fw_error_dump_paging *paging; struct page *pages = diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 594cd0dc7df9..09d895fafaf2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -144,9 +144,11 @@ void iwl_free_fw_paging(struct iwl_mvm *mvm) __free_pages(mvm->fw_paging_db[i].fw_paging_block, get_order(mvm->fw_paging_db[i].fw_paging_size)); + mvm->fw_paging_db[i].fw_paging_block = NULL; } kfree(mvm->trans->paging_download_buf); mvm->trans->paging_download_buf = NULL; + mvm->trans->paging_db = NULL; memset(mvm->fw_paging_db, 0, sizeof(mvm->fw_paging_db)); } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 05b968506836..79d7cd7d461e 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -479,8 +479,18 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x24F3, 0x0930, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x0000, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x0010, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0110, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x1110, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x1010, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0050, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0150, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x9010, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x8110, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x8050, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x8010, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x0810, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x9110, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x8130, iwl8265_2ac_cfg)}, /* 9000 Series */ {IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl5165_2ac_cfg)}, diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index f798899338ed..5101f3ab4f29 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c @@ -397,10 +397,17 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) */ start += start_pad; npfns = (pmem->size - start_pad - end_trunc - SZ_8K) / SZ_4K; - if (nd_pfn->mode == PFN_MODE_PMEM) - offset = ALIGN(start + SZ_8K + 64 * npfns, nd_pfn->align) + if (nd_pfn->mode == PFN_MODE_PMEM) { + unsigned long memmap_size; + + /* + * vmemmap_populate_hugepages() allocates the memmap array in + * HPAGE_SIZE chunks. + */ + memmap_size = ALIGN(64 * npfns, HPAGE_SIZE); + offset = ALIGN(start + SZ_8K + memmap_size, nd_pfn->align) - start; - else if (nd_pfn->mode == PFN_MODE_RAM) + } else if (nd_pfn->mode == PFN_MODE_RAM) offset = ALIGN(start + SZ_8K, nd_pfn->align) - start; else goto err; diff --git a/drivers/nvmem/mxs-ocotp.c b/drivers/nvmem/mxs-ocotp.c index 8ba19bba3156..2bb3c5799ac4 100644 --- a/drivers/nvmem/mxs-ocotp.c +++ b/drivers/nvmem/mxs-ocotp.c @@ -94,7 +94,7 @@ static int mxs_ocotp_read(void *context, const void *reg, size_t reg_size, if (ret) goto close_banks; - while (val_size) { + while (val_size >= reg_size) { if ((offset < OCOTP_DATA_OFFSET) || (offset % 16)) { /* fill up non-data register */ *buf = 0; @@ -103,7 +103,7 @@ static int mxs_ocotp_read(void *context, const void *reg, size_t reg_size, } buf++; - val_size--; + val_size -= reg_size; offset += reg_size; } diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index df1f1a76a862..01e12d221a8b 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -135,7 +135,7 @@ MODULE_LICENSE("GPL"); /* Field definitions */ #define HCI_ACCEL_MASK 0x7fff #define HCI_HOTKEY_DISABLE 0x0b -#define HCI_HOTKEY_ENABLE 0x01 +#define HCI_HOTKEY_ENABLE 0x09 #define HCI_HOTKEY_SPECIAL_FUNCTIONS 0x10 #define HCI_LCD_BRIGHTNESS_BITS 3 #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) diff --git a/drivers/power/avs/rockchip-io-domain.c b/drivers/power/avs/rockchip-io-domain.c index 8986382718dd..01b6d3f9b8fb 100644 --- a/drivers/power/avs/rockchip-io-domain.c +++ b/drivers/power/avs/rockchip-io-domain.c @@ -336,6 +336,7 @@ static int rockchip_iodomain_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; const struct of_device_id *match; struct rockchip_iodomain *iod; + struct device *parent; int i, ret = 0; if (!np) @@ -351,7 +352,14 @@ static int rockchip_iodomain_probe(struct platform_device *pdev) match = of_match_node(rockchip_iodomain_match, np); iod->soc_data = (struct rockchip_iodomain_soc_data *)match->data; - iod->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + parent = pdev->dev.parent; + if (parent && parent->of_node) { + iod->grf = syscon_node_to_regmap(parent->of_node); + } else { + dev_dbg(&pdev->dev, "falling back to old binding\n"); + iod->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + } + if (IS_ERR(iod->grf)) { dev_err(&pdev->dev, "couldn't find grf regmap\n"); return PTR_ERR(iod->grf); diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c index 8fad0a7044d3..470bb622fd8c 100644 --- a/drivers/powercap/intel_rapl.c +++ b/drivers/powercap/intel_rapl.c @@ -1101,6 +1101,8 @@ static const struct x86_cpu_id rapl_ids[] __initconst = { RAPL_CPU(0X5C, rapl_defaults_core),/* Broxton */ RAPL_CPU(0x5E, rapl_defaults_core),/* Skylake-H/S */ RAPL_CPU(0x57, rapl_defaults_hsw_server),/* Knights Landing */ + RAPL_CPU(0x8E, rapl_defaults_core),/* Kabylake */ + RAPL_CPU(0x9E, rapl_defaults_core),/* Kabylake */ {} }; MODULE_DEVICE_TABLE(x86cpu, rapl_ids); diff --git a/drivers/rapidio/devices/rio_mport_cdev.c b/drivers/rapidio/devices/rio_mport_cdev.c index 5d4d91846357..e165b7ce29d7 100644 --- a/drivers/rapidio/devices/rio_mport_cdev.c +++ b/drivers/rapidio/devices/rio_mport_cdev.c @@ -126,7 +126,7 @@ struct rio_mport_mapping { struct list_head node; struct mport_dev *md; enum rio_mport_map_dir dir; - u32 rioid; + u16 rioid; u64 rio_addr; dma_addr_t phys_addr; /* for mmap */ void *virt_addr; /* kernel address, for dma_free_coherent */ @@ -137,7 +137,7 @@ struct rio_mport_mapping { struct rio_mport_dma_map { int valid; - uint64_t length; + u64 length; void *vaddr; dma_addr_t paddr; }; @@ -208,7 +208,7 @@ struct mport_cdev_priv { struct kfifo event_fifo; wait_queue_head_t event_rx_wait; spinlock_t fifo_lock; - unsigned int event_mask; /* RIO_DOORBELL, RIO_PORTWRITE */ + u32 event_mask; /* RIO_DOORBELL, RIO_PORTWRITE */ #ifdef CONFIG_RAPIDIO_DMA_ENGINE struct dma_chan *dmach; struct list_head async_list; @@ -276,7 +276,8 @@ static int rio_mport_maint_rd(struct mport_cdev_priv *priv, void __user *arg, return -EFAULT; if ((maint_io.offset % 4) || - (maint_io.length == 0) || (maint_io.length % 4)) + (maint_io.length == 0) || (maint_io.length % 4) || + (maint_io.length + maint_io.offset) > RIO_MAINT_SPACE_SZ) return -EINVAL; buffer = vmalloc(maint_io.length); @@ -298,7 +299,8 @@ static int rio_mport_maint_rd(struct mport_cdev_priv *priv, void __user *arg, offset += 4; } - if (unlikely(copy_to_user(maint_io.buffer, buffer, maint_io.length))) + if (unlikely(copy_to_user((void __user *)(uintptr_t)maint_io.buffer, + buffer, maint_io.length))) ret = -EFAULT; out: vfree(buffer); @@ -319,7 +321,8 @@ static int rio_mport_maint_wr(struct mport_cdev_priv *priv, void __user *arg, return -EFAULT; if ((maint_io.offset % 4) || - (maint_io.length == 0) || (maint_io.length % 4)) + (maint_io.length == 0) || (maint_io.length % 4) || + (maint_io.length + maint_io.offset) > RIO_MAINT_SPACE_SZ) return -EINVAL; buffer = vmalloc(maint_io.length); @@ -327,7 +330,8 @@ static int rio_mport_maint_wr(struct mport_cdev_priv *priv, void __user *arg, return -ENOMEM; length = maint_io.length; - if (unlikely(copy_from_user(buffer, maint_io.buffer, length))) { + if (unlikely(copy_from_user(buffer, + (void __user *)(uintptr_t)maint_io.buffer, length))) { ret = -EFAULT; goto out; } @@ -360,7 +364,7 @@ out: */ static int rio_mport_create_outbound_mapping(struct mport_dev *md, struct file *filp, - u32 rioid, u64 raddr, u32 size, + u16 rioid, u64 raddr, u32 size, dma_addr_t *paddr) { struct rio_mport *mport = md->mport; @@ -369,7 +373,7 @@ rio_mport_create_outbound_mapping(struct mport_dev *md, struct file *filp, rmcd_debug(OBW, "did=%d ra=0x%llx sz=0x%x", rioid, raddr, size); - map = kzalloc(sizeof(struct rio_mport_mapping), GFP_KERNEL); + map = kzalloc(sizeof(*map), GFP_KERNEL); if (map == NULL) return -ENOMEM; @@ -394,7 +398,7 @@ err_map_outb: static int rio_mport_get_outbound_mapping(struct mport_dev *md, struct file *filp, - u32 rioid, u64 raddr, u32 size, + u16 rioid, u64 raddr, u32 size, dma_addr_t *paddr) { struct rio_mport_mapping *map; @@ -433,7 +437,7 @@ static int rio_mport_obw_map(struct file *filp, void __user *arg) dma_addr_t paddr; int ret; - if (unlikely(copy_from_user(&map, arg, sizeof(struct rio_mmap)))) + if (unlikely(copy_from_user(&map, arg, sizeof(map)))) return -EFAULT; rmcd_debug(OBW, "did=%d ra=0x%llx sz=0x%llx", @@ -448,7 +452,7 @@ static int rio_mport_obw_map(struct file *filp, void __user *arg) map.handle = paddr; - if (unlikely(copy_to_user(arg, &map, sizeof(struct rio_mmap)))) + if (unlikely(copy_to_user(arg, &map, sizeof(map)))) return -EFAULT; return 0; } @@ -469,7 +473,7 @@ static int rio_mport_obw_free(struct file *filp, void __user *arg) if (!md->mport->ops->unmap_outb) return -EPROTONOSUPPORT; - if (copy_from_user(&handle, arg, sizeof(u64))) + if (copy_from_user(&handle, arg, sizeof(handle))) return -EFAULT; rmcd_debug(OBW, "h=0x%llx", handle); @@ -498,9 +502,9 @@ static int rio_mport_obw_free(struct file *filp, void __user *arg) static int maint_hdid_set(struct mport_cdev_priv *priv, void __user *arg) { struct mport_dev *md = priv->md; - uint16_t hdid; + u16 hdid; - if (copy_from_user(&hdid, arg, sizeof(uint16_t))) + if (copy_from_user(&hdid, arg, sizeof(hdid))) return -EFAULT; md->mport->host_deviceid = hdid; @@ -520,9 +524,9 @@ static int maint_hdid_set(struct mport_cdev_priv *priv, void __user *arg) static int maint_comptag_set(struct mport_cdev_priv *priv, void __user *arg) { struct mport_dev *md = priv->md; - uint32_t comptag; + u32 comptag; - if (copy_from_user(&comptag, arg, sizeof(uint32_t))) + if (copy_from_user(&comptag, arg, sizeof(comptag))) return -EFAULT; rio_local_write_config_32(md->mport, RIO_COMPONENT_TAG_CSR, comptag); @@ -837,7 +841,7 @@ err_out: * @xfer: data transfer descriptor structure */ static int -rio_dma_transfer(struct file *filp, uint32_t transfer_mode, +rio_dma_transfer(struct file *filp, u32 transfer_mode, enum rio_transfer_sync sync, enum dma_data_direction dir, struct rio_transfer_io *xfer) { @@ -875,7 +879,7 @@ rio_dma_transfer(struct file *filp, uint32_t transfer_mode, unsigned long offset; long pinned; - offset = (unsigned long)xfer->loc_addr & ~PAGE_MASK; + offset = (unsigned long)(uintptr_t)xfer->loc_addr & ~PAGE_MASK; nr_pages = PAGE_ALIGN(xfer->length + offset) >> PAGE_SHIFT; page_list = kmalloc_array(nr_pages, @@ -1015,19 +1019,20 @@ static int rio_mport_transfer_ioctl(struct file *filp, void __user *arg) if (unlikely(copy_from_user(&transaction, arg, sizeof(transaction)))) return -EFAULT; - if (transaction.count != 1) + if (transaction.count != 1) /* only single transfer for now */ return -EINVAL; if ((transaction.transfer_mode & priv->md->properties.transfer_mode) == 0) return -ENODEV; - transfer = vmalloc(transaction.count * sizeof(struct rio_transfer_io)); + transfer = vmalloc(transaction.count * sizeof(*transfer)); if (!transfer) return -ENOMEM; - if (unlikely(copy_from_user(transfer, transaction.block, - transaction.count * sizeof(struct rio_transfer_io)))) { + if (unlikely(copy_from_user(transfer, + (void __user *)(uintptr_t)transaction.block, + transaction.count * sizeof(*transfer)))) { ret = -EFAULT; goto out_free; } @@ -1038,8 +1043,9 @@ static int rio_mport_transfer_ioctl(struct file *filp, void __user *arg) ret = rio_dma_transfer(filp, transaction.transfer_mode, transaction.sync, dir, &transfer[i]); - if (unlikely(copy_to_user(transaction.block, transfer, - transaction.count * sizeof(struct rio_transfer_io)))) + if (unlikely(copy_to_user((void __user *)(uintptr_t)transaction.block, + transfer, + transaction.count * sizeof(*transfer)))) ret = -EFAULT; out_free: @@ -1129,11 +1135,11 @@ err_tmo: } static int rio_mport_create_dma_mapping(struct mport_dev *md, struct file *filp, - uint64_t size, struct rio_mport_mapping **mapping) + u64 size, struct rio_mport_mapping **mapping) { struct rio_mport_mapping *map; - map = kzalloc(sizeof(struct rio_mport_mapping), GFP_KERNEL); + map = kzalloc(sizeof(*map), GFP_KERNEL); if (map == NULL) return -ENOMEM; @@ -1165,7 +1171,7 @@ static int rio_mport_alloc_dma(struct file *filp, void __user *arg) struct rio_mport_mapping *mapping = NULL; int ret; - if (unlikely(copy_from_user(&map, arg, sizeof(struct rio_dma_mem)))) + if (unlikely(copy_from_user(&map, arg, sizeof(map)))) return -EFAULT; ret = rio_mport_create_dma_mapping(md, filp, map.length, &mapping); @@ -1174,7 +1180,7 @@ static int rio_mport_alloc_dma(struct file *filp, void __user *arg) map.dma_handle = mapping->phys_addr; - if (unlikely(copy_to_user(arg, &map, sizeof(struct rio_dma_mem)))) { + if (unlikely(copy_to_user(arg, &map, sizeof(map)))) { mutex_lock(&md->buf_mutex); kref_put(&mapping->ref, mport_release_mapping); mutex_unlock(&md->buf_mutex); @@ -1192,7 +1198,7 @@ static int rio_mport_free_dma(struct file *filp, void __user *arg) int ret = -EFAULT; struct rio_mport_mapping *map, *_map; - if (copy_from_user(&handle, arg, sizeof(u64))) + if (copy_from_user(&handle, arg, sizeof(handle))) return -EFAULT; rmcd_debug(EXIT, "filp=%p", filp); @@ -1242,14 +1248,18 @@ static int rio_mport_free_dma(struct file *filp, void __user *arg) static int rio_mport_create_inbound_mapping(struct mport_dev *md, struct file *filp, - u64 raddr, u32 size, + u64 raddr, u64 size, struct rio_mport_mapping **mapping) { struct rio_mport *mport = md->mport; struct rio_mport_mapping *map; int ret; - map = kzalloc(sizeof(struct rio_mport_mapping), GFP_KERNEL); + /* rio_map_inb_region() accepts u32 size */ + if (size > 0xffffffff) + return -EINVAL; + + map = kzalloc(sizeof(*map), GFP_KERNEL); if (map == NULL) return -ENOMEM; @@ -1262,7 +1272,7 @@ rio_mport_create_inbound_mapping(struct mport_dev *md, struct file *filp, if (raddr == RIO_MAP_ANY_ADDR) raddr = map->phys_addr; - ret = rio_map_inb_region(mport, map->phys_addr, raddr, size, 0); + ret = rio_map_inb_region(mport, map->phys_addr, raddr, (u32)size, 0); if (ret < 0) goto err_map_inb; @@ -1288,7 +1298,7 @@ err_dma_alloc: static int rio_mport_get_inbound_mapping(struct mport_dev *md, struct file *filp, - u64 raddr, u32 size, + u64 raddr, u64 size, struct rio_mport_mapping **mapping) { struct rio_mport_mapping *map; @@ -1331,7 +1341,7 @@ static int rio_mport_map_inbound(struct file *filp, void __user *arg) if (!md->mport->ops->map_inb) return -EPROTONOSUPPORT; - if (unlikely(copy_from_user(&map, arg, sizeof(struct rio_mmap)))) + if (unlikely(copy_from_user(&map, arg, sizeof(map)))) return -EFAULT; rmcd_debug(IBW, "%s filp=%p", dev_name(&priv->md->dev), filp); @@ -1344,7 +1354,7 @@ static int rio_mport_map_inbound(struct file *filp, void __user *arg) map.handle = mapping->phys_addr; map.rio_addr = mapping->rio_addr; - if (unlikely(copy_to_user(arg, &map, sizeof(struct rio_mmap)))) { + if (unlikely(copy_to_user(arg, &map, sizeof(map)))) { /* Delete mapping if it was created by this request */ if (ret == 0 && mapping->filp == filp) { mutex_lock(&md->buf_mutex); @@ -1375,7 +1385,7 @@ static int rio_mport_inbound_free(struct file *filp, void __user *arg) if (!md->mport->ops->unmap_inb) return -EPROTONOSUPPORT; - if (copy_from_user(&handle, arg, sizeof(u64))) + if (copy_from_user(&handle, arg, sizeof(handle))) return -EFAULT; mutex_lock(&md->buf_mutex); @@ -1401,7 +1411,7 @@ static int rio_mport_inbound_free(struct file *filp, void __user *arg) static int maint_port_idx_get(struct mport_cdev_priv *priv, void __user *arg) { struct mport_dev *md = priv->md; - uint32_t port_idx = md->mport->index; + u32 port_idx = md->mport->index; rmcd_debug(MPORT, "port_index=%d", port_idx); @@ -1451,7 +1461,7 @@ static void rio_mport_doorbell_handler(struct rio_mport *mport, void *dev_id, handled = 0; spin_lock(&data->db_lock); list_for_each_entry(db_filter, &data->doorbells, data_node) { - if (((db_filter->filter.rioid == 0xffffffff || + if (((db_filter->filter.rioid == RIO_INVALID_DESTID || db_filter->filter.rioid == src)) && info >= db_filter->filter.low && info <= db_filter->filter.high) { @@ -1525,6 +1535,9 @@ static int rio_mport_remove_db_filter(struct mport_cdev_priv *priv, if (copy_from_user(&filter, arg, sizeof(filter))) return -EFAULT; + if (filter.low > filter.high) + return -EINVAL; + spin_lock_irqsave(&priv->md->db_lock, flags); list_for_each_entry(db_filter, &priv->db_filters, priv_node) { if (db_filter->filter.rioid == filter.rioid && @@ -1737,10 +1750,10 @@ static int rio_mport_add_riodev(struct mport_cdev_priv *priv, return -EEXIST; } - size = sizeof(struct rio_dev); + size = sizeof(*rdev); mport = md->mport; - destid = (u16)dev_info.destid; - hopcount = (u8)dev_info.hopcount; + destid = dev_info.destid; + hopcount = dev_info.hopcount; if (rio_mport_read_config_32(mport, destid, hopcount, RIO_PEF_CAR, &rval)) @@ -1872,8 +1885,8 @@ static int rio_mport_del_riodev(struct mport_cdev_priv *priv, void __user *arg) do { rdev = rio_get_comptag(dev_info.comptag, rdev); if (rdev && rdev->dev.parent == &mport->net->dev && - rdev->destid == (u16)dev_info.destid && - rdev->hopcount == (u8)dev_info.hopcount) + rdev->destid == dev_info.destid && + rdev->hopcount == dev_info.hopcount) break; } while (rdev); } @@ -2146,8 +2159,8 @@ static long mport_cdev_ioctl(struct file *filp, return maint_port_idx_get(data, (void __user *)arg); case RIO_MPORT_GET_PROPERTIES: md->properties.hdid = md->mport->host_deviceid; - if (copy_to_user((void __user *)arg, &(data->md->properties), - sizeof(data->md->properties))) + if (copy_to_user((void __user *)arg, &(md->properties), + sizeof(md->properties))) return -EFAULT; return 0; case RIO_ENABLE_DOORBELL_RANGE: @@ -2159,11 +2172,11 @@ static long mport_cdev_ioctl(struct file *filp, case RIO_DISABLE_PORTWRITE_RANGE: return rio_mport_remove_pw_filter(data, (void __user *)arg); case RIO_SET_EVENT_MASK: - data->event_mask = arg; + data->event_mask = (u32)arg; return 0; case RIO_GET_EVENT_MASK: if (copy_to_user((void __user *)arg, &data->event_mask, - sizeof(data->event_mask))) + sizeof(u32))) return -EFAULT; return 0; case RIO_MAP_OUTBOUND: @@ -2374,7 +2387,7 @@ static ssize_t mport_write(struct file *filp, const char __user *buf, return -EINVAL; ret = rio_mport_send_doorbell(mport, - (u16)event.u.doorbell.rioid, + event.u.doorbell.rioid, event.u.doorbell.payload); if (ret < 0) return ret; @@ -2421,7 +2434,7 @@ static struct mport_dev *mport_cdev_add(struct rio_mport *mport) struct mport_dev *md; struct rio_mport_attr attr; - md = kzalloc(sizeof(struct mport_dev), GFP_KERNEL); + md = kzalloc(sizeof(*md), GFP_KERNEL); if (!md) { rmcd_error("Unable allocate a device object"); return NULL; @@ -2470,7 +2483,7 @@ static struct mport_dev *mport_cdev_add(struct rio_mport *mport) /* The transfer_mode property will be returned through mport query * interface */ -#ifdef CONFIG_PPC /* for now: only on Freescale's SoCs */ +#ifdef CONFIG_FSL_RIO /* for now: only on Freescale's SoCs */ md->properties.transfer_mode |= RIO_TRANSFER_MODE_MAPPED; #else md->properties.transfer_mode |= RIO_TRANSFER_MODE_TRANSFER; @@ -2669,9 +2682,9 @@ static int __init mport_init(void) /* Create device class needed by udev */ dev_class = class_create(THIS_MODULE, DRV_NAME); - if (!dev_class) { + if (IS_ERR(dev_class)) { rmcd_error("Unable to create " DRV_NAME " class"); - return -EINVAL; + return PTR_ERR(dev_class); } ret = alloc_chrdev_region(&dev_number, 0, RIO_MAX_MPORTS, DRV_NAME); diff --git a/drivers/s390/char/sclp_ctl.c b/drivers/s390/char/sclp_ctl.c index 648cb86afd42..ea607a4a1bdd 100644 --- a/drivers/s390/char/sclp_ctl.c +++ b/drivers/s390/char/sclp_ctl.c @@ -56,6 +56,7 @@ static int sclp_ctl_ioctl_sccb(void __user *user_area) { struct sclp_ctl_sccb ctl_sccb; struct sccb_header *sccb; + unsigned long copied; int rc; if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb))) @@ -65,14 +66,15 @@ static int sclp_ctl_ioctl_sccb(void __user *user_area) sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!sccb) return -ENOMEM; - if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sizeof(*sccb))) { + copied = PAGE_SIZE - + copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), PAGE_SIZE); + if (offsetof(struct sccb_header, length) + + sizeof(sccb->length) > copied || sccb->length > copied) { rc = -EFAULT; goto out_free; } - if (sccb->length > PAGE_SIZE || sccb->length < 8) - return -EINVAL; - if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sccb->length)) { - rc = -EFAULT; + if (sccb->length < 8) { + rc = -EINVAL; goto out_free; } rc = sclp_sync_request(ctl_sccb.cmdw, sccb); diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index f3bb7af4e984..ead83a24bcd1 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -688,6 +688,7 @@ static struct rt6_info *find_route_ipv6(const struct in6_addr *saddr, { struct flowi6 fl; + memset(&fl, 0, sizeof(fl)); if (saddr) memcpy(&fl.saddr, saddr, sizeof(struct in6_addr)); if (daddr) diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c index 57e781c71e67..837effe19907 100644 --- a/drivers/soc/mediatek/mtk-scpsys.c +++ b/drivers/soc/mediatek/mtk-scpsys.c @@ -491,13 +491,14 @@ static int scpsys_probe(struct platform_device *pdev) genpd->dev_ops.active_wakeup = scpsys_active_wakeup; /* - * With CONFIG_PM disabled turn on all domains to make the - * hardware usable. + * Initially turn on all domains to make the domains usable + * with !CONFIG_PM and to get the hardware in sync with the + * software. The unused domains will be switched off during + * late_init time. */ - if (!IS_ENABLED(CONFIG_PM)) - genpd->power_on(genpd); + genpd->power_on(genpd); - pm_genpd_init(genpd, NULL, true); + pm_genpd_init(genpd, NULL, false); } /* diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c index 5548a31e1a39..1fcbb22a4a1c 100644 --- a/drivers/soc/qcom/spm.c +++ b/drivers/soc/qcom/spm.c @@ -274,7 +274,7 @@ check_spm: return per_cpu(cpu_spm_drv, cpu) ? 0 : -ENXIO; } -static struct cpuidle_ops qcom_cpuidle_ops __initdata = { +static const struct cpuidle_ops qcom_cpuidle_ops __initconst = { .suspend = qcom_idle_enter, .init = qcom_cpuidle_init, }; diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c index b793c04028a3..be72a8e5f221 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -172,9 +172,11 @@ static int vpfe_prepare_pipeline(struct vpfe_video_device *video) static int vpfe_update_pipe_state(struct vpfe_video_device *video) { struct vpfe_pipeline *pipe = &video->pipe; + int ret; - if (vpfe_prepare_pipeline(video)) - return vpfe_prepare_pipeline(video); + ret = vpfe_prepare_pipeline(video); + if (ret) + return ret; /* * Find out if there is any input video @@ -182,9 +184,10 @@ static int vpfe_update_pipe_state(struct vpfe_video_device *video) */ if (pipe->input_num == 0) { pipe->state = VPFE_PIPELINE_STREAM_CONTINUOUS; - if (vpfe_update_current_ext_subdev(video)) { + ret = vpfe_update_current_ext_subdev(video); + if (ret) { pr_err("Invalid external subdev\n"); - return vpfe_update_current_ext_subdev(video); + return ret; } } else { pipe->state = VPFE_PIPELINE_STREAM_SINGLESHOT; @@ -667,6 +670,7 @@ static int vpfe_enum_fmt(struct file *file, void *priv, struct v4l2_subdev *subdev; struct v4l2_format format; struct media_pad *remote; + int ret; v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt\n"); @@ -695,10 +699,11 @@ static int vpfe_enum_fmt(struct file *file, void *priv, sd_fmt.pad = remote->index; sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; /* get output format of remote subdev */ - if (v4l2_subdev_call(subdev, pad, get_fmt, NULL, &sd_fmt)) { + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &sd_fmt); + if (ret) { v4l2_err(&vpfe_dev->v4l2_dev, "invalid remote subdev for video node\n"); - return v4l2_subdev_call(subdev, pad, get_fmt, NULL, &sd_fmt); + return ret; } /* convert to pix format */ mbus.code = sd_fmt.format.code; @@ -725,6 +730,7 @@ static int vpfe_s_fmt(struct file *file, void *priv, struct vpfe_video_device *video = video_drvdata(file); struct vpfe_device *vpfe_dev = video->vpfe_dev; struct v4l2_format format; + int ret; v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt\n"); /* If streaming is started, return error */ @@ -733,8 +739,9 @@ static int vpfe_s_fmt(struct file *file, void *priv, return -EBUSY; } /* get adjacent subdev's output pad format */ - if (__vpfe_video_get_format(video, &format)) - return __vpfe_video_get_format(video, &format); + ret = __vpfe_video_get_format(video, &format); + if (ret) + return ret; *fmt = format; video->fmt = *fmt; return 0; @@ -757,11 +764,13 @@ static int vpfe_try_fmt(struct file *file, void *priv, struct vpfe_video_device *video = video_drvdata(file); struct vpfe_device *vpfe_dev = video->vpfe_dev; struct v4l2_format format; + int ret; v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt\n"); /* get adjacent subdev's output pad format */ - if (__vpfe_video_get_format(video, &format)) - return __vpfe_video_get_format(video, &format); + ret = __vpfe_video_get_format(video, &format); + if (ret) + return ret; *fmt = format; return 0; @@ -838,8 +847,9 @@ static int vpfe_s_input(struct file *file, void *priv, unsigned int index) v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n"); - if (mutex_lock_interruptible(&video->lock)) - return mutex_lock_interruptible(&video->lock); + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; /* * If streaming is started return device busy * error @@ -940,8 +950,9 @@ static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id) v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n"); /* Call decoder driver function to set the standard */ - if (mutex_lock_interruptible(&video->lock)) - return mutex_lock_interruptible(&video->lock); + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; sdinfo = video->current_ext_subdev; /* If streaming is started, return device busy error */ if (video->started) { @@ -1327,8 +1338,9 @@ static int vpfe_reqbufs(struct file *file, void *priv, return -EINVAL; } - if (mutex_lock_interruptible(&video->lock)) - return mutex_lock_interruptible(&video->lock); + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; if (video->io_usrs != 0) { v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n"); @@ -1354,10 +1366,11 @@ static int vpfe_reqbufs(struct file *file, void *priv, q->buf_struct_size = sizeof(struct vpfe_cap_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - if (vb2_queue_init(q)) { + ret = vb2_queue_init(q); + if (ret) { v4l2_err(&vpfe_dev->v4l2_dev, "vb2_queue_init() failed\n"); vb2_dma_contig_cleanup_ctx(vpfe_dev->pdev); - return vb2_queue_init(q); + return ret; } fh->io_allowed = 1; @@ -1533,8 +1546,9 @@ static int vpfe_streamoff(struct file *file, void *priv, return -EINVAL; } - if (mutex_lock_interruptible(&video->lock)) - return mutex_lock_interruptible(&video->lock); + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; vpfe_stop_capture(video); ret = vb2_streamoff(&video->buffer_queue, buf_type); diff --git a/drivers/staging/rdma/hfi1/TODO b/drivers/staging/rdma/hfi1/TODO index 05de0dad8762..4c6f1d7d2eaf 100644 --- a/drivers/staging/rdma/hfi1/TODO +++ b/drivers/staging/rdma/hfi1/TODO @@ -3,4 +3,4 @@ July, 2015 - Remove unneeded file entries in sysfs - Remove software processing of IB protocol and place in library for use by qib, ipath (if still present), hfi1, and eventually soft-roce - +- Replace incorrect uAPI diff --git a/drivers/staging/rdma/hfi1/file_ops.c b/drivers/staging/rdma/hfi1/file_ops.c index 8396dc5fb6c1..c1c5bf82addb 100644 --- a/drivers/staging/rdma/hfi1/file_ops.c +++ b/drivers/staging/rdma/hfi1/file_ops.c @@ -49,6 +49,8 @@ #include <linux/vmalloc.h> #include <linux/io.h> +#include <rdma/ib.h> + #include "hfi.h" #include "pio.h" #include "device.h" @@ -190,6 +192,10 @@ static ssize_t hfi1_file_write(struct file *fp, const char __user *data, int uctxt_required = 1; int must_be_root = 0; + /* FIXME: This interface cannot continue out of staging */ + if (WARN_ON_ONCE(!ib_safe_file_access(fp))) + return -EACCES; + if (count < sizeof(cmd)) { ret = -EINVAL; goto bail; @@ -791,15 +797,16 @@ static int hfi1_file_close(struct inode *inode, struct file *fp) spin_unlock_irqrestore(&dd->uctxt_lock, flags); dd->rcd[uctxt->ctxt] = NULL; + + hfi1_user_exp_rcv_free(fdata); + hfi1_clear_ctxt_pkey(dd, uctxt->ctxt); + uctxt->rcvwait_to = 0; uctxt->piowait_to = 0; uctxt->rcvnowait = 0; uctxt->pionowait = 0; uctxt->event_flags = 0; - hfi1_user_exp_rcv_free(fdata); - hfi1_clear_ctxt_pkey(dd, uctxt->ctxt); - hfi1_stats.sps_ctxts--; if (++dd->freectxts == dd->num_user_contexts) aspm_enable_all(dd); @@ -1127,27 +1134,13 @@ bail: static int user_init(struct file *fp) { - int ret; unsigned int rcvctrl_ops = 0; struct hfi1_filedata *fd = fp->private_data; struct hfi1_ctxtdata *uctxt = fd->uctxt; /* make sure that the context has already been setup */ - if (!test_bit(HFI1_CTXT_SETUP_DONE, &uctxt->event_flags)) { - ret = -EFAULT; - goto done; - } - - /* - * Subctxts don't need to initialize anything since master - * has done it. - */ - if (fd->subctxt) { - ret = wait_event_interruptible(uctxt->wait, !test_bit( - HFI1_CTXT_MASTER_UNINIT, - &uctxt->event_flags)); - goto expected; - } + if (!test_bit(HFI1_CTXT_SETUP_DONE, &uctxt->event_flags)) + return -EFAULT; /* initialize poll variables... */ uctxt->urgent = 0; @@ -1202,19 +1195,7 @@ static int user_init(struct file *fp) wake_up(&uctxt->wait); } -expected: - /* - * Expected receive has to be setup for all processes (including - * shared contexts). However, it has to be done after the master - * context has been fully configured as it depends on the - * eager/expected split of the RcvArray entries. - * Setting it up here ensures that the subcontexts will be waiting - * (due to the above wait_event_interruptible() until the master - * is setup. - */ - ret = hfi1_user_exp_rcv_init(fp); -done: - return ret; + return 0; } static int get_ctxt_info(struct file *fp, void __user *ubase, __u32 len) @@ -1261,7 +1242,7 @@ static int setup_ctxt(struct file *fp) int ret = 0; /* - * Context should be set up only once (including allocation and + * Context should be set up only once, including allocation and * programming of eager buffers. This is done if context sharing * is not requested or by the master process. */ @@ -1282,10 +1263,29 @@ static int setup_ctxt(struct file *fp) if (ret) goto done; } + } else { + ret = wait_event_interruptible(uctxt->wait, !test_bit( + HFI1_CTXT_MASTER_UNINIT, + &uctxt->event_flags)); + if (ret) + goto done; } + ret = hfi1_user_sdma_alloc_queues(uctxt, fp); if (ret) goto done; + /* + * Expected receive has to be setup for all processes (including + * shared contexts). However, it has to be done after the master + * context has been fully configured as it depends on the + * eager/expected split of the RcvArray entries. + * Setting it up here ensures that the subcontexts will be waiting + * (due to the above wait_event_interruptible() until the master + * is setup. + */ + ret = hfi1_user_exp_rcv_init(fp); + if (ret) + goto done; set_bit(HFI1_CTXT_SETUP_DONE, &uctxt->event_flags); done: @@ -1565,29 +1565,8 @@ static loff_t ui_lseek(struct file *filp, loff_t offset, int whence) { struct hfi1_devdata *dd = filp->private_data; - switch (whence) { - case SEEK_SET: - break; - case SEEK_CUR: - offset += filp->f_pos; - break; - case SEEK_END: - offset = ((dd->kregend - dd->kregbase) + DC8051_DATA_MEM_SIZE) - - offset; - break; - default: - return -EINVAL; - } - - if (offset < 0) - return -EINVAL; - - if (offset >= (dd->kregend - dd->kregbase) + DC8051_DATA_MEM_SIZE) - return -EINVAL; - - filp->f_pos = offset; - - return filp->f_pos; + return fixed_size_llseek(filp, offset, whence, + (dd->kregend - dd->kregbase) + DC8051_DATA_MEM_SIZE); } /* NOTE: assumes unsigned long is 8 bytes */ diff --git a/drivers/staging/rdma/hfi1/mmu_rb.c b/drivers/staging/rdma/hfi1/mmu_rb.c index c7ad0164ea9a..b3f0682a36c9 100644 --- a/drivers/staging/rdma/hfi1/mmu_rb.c +++ b/drivers/staging/rdma/hfi1/mmu_rb.c @@ -71,6 +71,7 @@ static inline void mmu_notifier_range_start(struct mmu_notifier *, struct mm_struct *, unsigned long, unsigned long); static void mmu_notifier_mem_invalidate(struct mmu_notifier *, + struct mm_struct *, unsigned long, unsigned long); static struct mmu_rb_node *__mmu_rb_search(struct mmu_rb_handler *, unsigned long, unsigned long); @@ -137,7 +138,7 @@ void hfi1_mmu_rb_unregister(struct rb_root *root) rbnode = rb_entry(node, struct mmu_rb_node, node); rb_erase(node, root); if (handler->ops->remove) - handler->ops->remove(root, rbnode, false); + handler->ops->remove(root, rbnode, NULL); } } @@ -176,7 +177,7 @@ unlock: return ret; } -/* Caller must host handler lock */ +/* Caller must hold handler lock */ static struct mmu_rb_node *__mmu_rb_search(struct mmu_rb_handler *handler, unsigned long addr, unsigned long len) @@ -200,15 +201,21 @@ static struct mmu_rb_node *__mmu_rb_search(struct mmu_rb_handler *handler, return node; } +/* Caller must *not* hold handler lock. */ static void __mmu_rb_remove(struct mmu_rb_handler *handler, - struct mmu_rb_node *node, bool arg) + struct mmu_rb_node *node, struct mm_struct *mm) { + unsigned long flags; + /* Validity of handler and node pointers has been checked by caller. */ hfi1_cdbg(MMU, "Removing node addr 0x%llx, len %u", node->addr, node->len); + spin_lock_irqsave(&handler->lock, flags); __mmu_int_rb_remove(node, handler->root); + spin_unlock_irqrestore(&handler->lock, flags); + if (handler->ops->remove) - handler->ops->remove(handler->root, node, arg); + handler->ops->remove(handler->root, node, mm); } struct mmu_rb_node *hfi1_mmu_rb_search(struct rb_root *root, unsigned long addr, @@ -231,14 +238,11 @@ struct mmu_rb_node *hfi1_mmu_rb_search(struct rb_root *root, unsigned long addr, void hfi1_mmu_rb_remove(struct rb_root *root, struct mmu_rb_node *node) { struct mmu_rb_handler *handler = find_mmu_handler(root); - unsigned long flags; if (!handler || !node) return; - spin_lock_irqsave(&handler->lock, flags); - __mmu_rb_remove(handler, node, false); - spin_unlock_irqrestore(&handler->lock, flags); + __mmu_rb_remove(handler, node, NULL); } static struct mmu_rb_handler *find_mmu_handler(struct rb_root *root) @@ -260,7 +264,7 @@ unlock: static inline void mmu_notifier_page(struct mmu_notifier *mn, struct mm_struct *mm, unsigned long addr) { - mmu_notifier_mem_invalidate(mn, addr, addr + PAGE_SIZE); + mmu_notifier_mem_invalidate(mn, mm, addr, addr + PAGE_SIZE); } static inline void mmu_notifier_range_start(struct mmu_notifier *mn, @@ -268,25 +272,31 @@ static inline void mmu_notifier_range_start(struct mmu_notifier *mn, unsigned long start, unsigned long end) { - mmu_notifier_mem_invalidate(mn, start, end); + mmu_notifier_mem_invalidate(mn, mm, start, end); } static void mmu_notifier_mem_invalidate(struct mmu_notifier *mn, + struct mm_struct *mm, unsigned long start, unsigned long end) { struct mmu_rb_handler *handler = container_of(mn, struct mmu_rb_handler, mn); struct rb_root *root = handler->root; - struct mmu_rb_node *node; + struct mmu_rb_node *node, *ptr = NULL; unsigned long flags; spin_lock_irqsave(&handler->lock, flags); - for (node = __mmu_int_rb_iter_first(root, start, end - 1); node; - node = __mmu_int_rb_iter_next(node, start, end - 1)) { + for (node = __mmu_int_rb_iter_first(root, start, end - 1); + node; node = ptr) { + /* Guard against node removal. */ + ptr = __mmu_int_rb_iter_next(node, start, end - 1); hfi1_cdbg(MMU, "Invalidating node addr 0x%llx, len %u", node->addr, node->len); - if (handler->ops->invalidate(root, node)) - __mmu_rb_remove(handler, node, true); + if (handler->ops->invalidate(root, node)) { + spin_unlock_irqrestore(&handler->lock, flags); + __mmu_rb_remove(handler, node, mm); + spin_lock_irqsave(&handler->lock, flags); + } } spin_unlock_irqrestore(&handler->lock, flags); } diff --git a/drivers/staging/rdma/hfi1/mmu_rb.h b/drivers/staging/rdma/hfi1/mmu_rb.h index f8523fdb8a18..19a306e83c7d 100644 --- a/drivers/staging/rdma/hfi1/mmu_rb.h +++ b/drivers/staging/rdma/hfi1/mmu_rb.h @@ -59,7 +59,8 @@ struct mmu_rb_node { struct mmu_rb_ops { bool (*filter)(struct mmu_rb_node *, unsigned long, unsigned long); int (*insert)(struct rb_root *, struct mmu_rb_node *); - void (*remove)(struct rb_root *, struct mmu_rb_node *, bool); + void (*remove)(struct rb_root *, struct mmu_rb_node *, + struct mm_struct *); int (*invalidate)(struct rb_root *, struct mmu_rb_node *); }; diff --git a/drivers/staging/rdma/hfi1/qp.c b/drivers/staging/rdma/hfi1/qp.c index 29a5ad28019b..dc9119e1b458 100644 --- a/drivers/staging/rdma/hfi1/qp.c +++ b/drivers/staging/rdma/hfi1/qp.c @@ -519,10 +519,12 @@ static void iowait_sdma_drained(struct iowait *wait) * do the flush work until that QP's * sdma work has finished. */ + spin_lock(&qp->s_lock); if (qp->s_flags & RVT_S_WAIT_DMA) { qp->s_flags &= ~RVT_S_WAIT_DMA; hfi1_schedule_send(qp); } + spin_unlock(&qp->s_lock); } /** diff --git a/drivers/staging/rdma/hfi1/user_exp_rcv.c b/drivers/staging/rdma/hfi1/user_exp_rcv.c index 0861e095df8d..8bd56d5c783d 100644 --- a/drivers/staging/rdma/hfi1/user_exp_rcv.c +++ b/drivers/staging/rdma/hfi1/user_exp_rcv.c @@ -87,7 +87,8 @@ static u32 find_phys_blocks(struct page **, unsigned, struct tid_pageset *); static int set_rcvarray_entry(struct file *, unsigned long, u32, struct tid_group *, struct page **, unsigned); static int mmu_rb_insert(struct rb_root *, struct mmu_rb_node *); -static void mmu_rb_remove(struct rb_root *, struct mmu_rb_node *, bool); +static void mmu_rb_remove(struct rb_root *, struct mmu_rb_node *, + struct mm_struct *); static int mmu_rb_invalidate(struct rb_root *, struct mmu_rb_node *); static int program_rcvarray(struct file *, unsigned long, struct tid_group *, struct tid_pageset *, unsigned, u16, struct page **, @@ -254,6 +255,8 @@ int hfi1_user_exp_rcv_free(struct hfi1_filedata *fd) struct hfi1_ctxtdata *uctxt = fd->uctxt; struct tid_group *grp, *gptr; + if (!test_bit(HFI1_CTXT_SETUP_DONE, &uctxt->event_flags)) + return 0; /* * The notifier would have been removed when the process'es mm * was freed. @@ -899,7 +902,7 @@ static int unprogram_rcvarray(struct file *fp, u32 tidinfo, if (!node || node->rcventry != (uctxt->expected_base + rcventry)) return -EBADF; if (HFI1_CAP_IS_USET(TID_UNMAP)) - mmu_rb_remove(&fd->tid_rb_root, &node->mmu, false); + mmu_rb_remove(&fd->tid_rb_root, &node->mmu, NULL); else hfi1_mmu_rb_remove(&fd->tid_rb_root, &node->mmu); @@ -965,7 +968,7 @@ static void unlock_exp_tids(struct hfi1_ctxtdata *uctxt, continue; if (HFI1_CAP_IS_USET(TID_UNMAP)) mmu_rb_remove(&fd->tid_rb_root, - &node->mmu, false); + &node->mmu, NULL); else hfi1_mmu_rb_remove(&fd->tid_rb_root, &node->mmu); @@ -1032,7 +1035,7 @@ static int mmu_rb_insert(struct rb_root *root, struct mmu_rb_node *node) } static void mmu_rb_remove(struct rb_root *root, struct mmu_rb_node *node, - bool notifier) + struct mm_struct *mm) { struct hfi1_filedata *fdata = container_of(root, struct hfi1_filedata, tid_rb_root); diff --git a/drivers/staging/rdma/hfi1/user_sdma.c b/drivers/staging/rdma/hfi1/user_sdma.c index ab6b6a42000f..d53a659548e0 100644 --- a/drivers/staging/rdma/hfi1/user_sdma.c +++ b/drivers/staging/rdma/hfi1/user_sdma.c @@ -278,7 +278,8 @@ static inline void pq_update(struct hfi1_user_sdma_pkt_q *); static void user_sdma_free_request(struct user_sdma_request *, bool); static int pin_vector_pages(struct user_sdma_request *, struct user_sdma_iovec *); -static void unpin_vector_pages(struct mm_struct *, struct page **, unsigned); +static void unpin_vector_pages(struct mm_struct *, struct page **, unsigned, + unsigned); static int check_header_template(struct user_sdma_request *, struct hfi1_pkt_header *, u32, u32); static int set_txreq_header(struct user_sdma_request *, @@ -299,7 +300,8 @@ static int defer_packet_queue( static void activate_packet_queue(struct iowait *, int); static bool sdma_rb_filter(struct mmu_rb_node *, unsigned long, unsigned long); static int sdma_rb_insert(struct rb_root *, struct mmu_rb_node *); -static void sdma_rb_remove(struct rb_root *, struct mmu_rb_node *, bool); +static void sdma_rb_remove(struct rb_root *, struct mmu_rb_node *, + struct mm_struct *); static int sdma_rb_invalidate(struct rb_root *, struct mmu_rb_node *); static struct mmu_rb_ops sdma_rb_ops = { @@ -1063,8 +1065,10 @@ static int pin_vector_pages(struct user_sdma_request *req, rb_node = hfi1_mmu_rb_search(&pq->sdma_rb_root, (unsigned long)iovec->iov.iov_base, iovec->iov.iov_len); - if (rb_node) + if (rb_node && !IS_ERR(rb_node)) node = container_of(rb_node, struct sdma_mmu_node, rb); + else + rb_node = NULL; if (!node) { node = kzalloc(sizeof(*node), GFP_KERNEL); @@ -1107,7 +1111,8 @@ retry: goto bail; } if (pinned != npages) { - unpin_vector_pages(current->mm, pages, pinned); + unpin_vector_pages(current->mm, pages, node->npages, + pinned); ret = -EFAULT; goto bail; } @@ -1147,9 +1152,9 @@ bail: } static void unpin_vector_pages(struct mm_struct *mm, struct page **pages, - unsigned npages) + unsigned start, unsigned npages) { - hfi1_release_user_pages(mm, pages, npages, 0); + hfi1_release_user_pages(mm, pages + start, npages, 0); kfree(pages); } @@ -1502,7 +1507,7 @@ static void user_sdma_free_request(struct user_sdma_request *req, bool unpin) &req->pq->sdma_rb_root, (unsigned long)req->iovs[i].iov.iov_base, req->iovs[i].iov.iov_len); - if (!mnode) + if (!mnode || IS_ERR(mnode)) continue; node = container_of(mnode, struct sdma_mmu_node, rb); @@ -1547,7 +1552,7 @@ static int sdma_rb_insert(struct rb_root *root, struct mmu_rb_node *mnode) } static void sdma_rb_remove(struct rb_root *root, struct mmu_rb_node *mnode, - bool notifier) + struct mm_struct *mm) { struct sdma_mmu_node *node = container_of(mnode, struct sdma_mmu_node, rb); @@ -1557,14 +1562,20 @@ static void sdma_rb_remove(struct rb_root *root, struct mmu_rb_node *mnode, node->pq->n_locked -= node->npages; spin_unlock(&node->pq->evict_lock); - unpin_vector_pages(notifier ? NULL : current->mm, node->pages, + /* + * If mm is set, we are being called by the MMU notifier and we + * should not pass a mm_struct to unpin_vector_page(). This is to + * prevent a deadlock when hfi1_release_user_pages() attempts to + * take the mmap_sem, which the MMU notifier has already taken. + */ + unpin_vector_pages(mm ? NULL : current->mm, node->pages, 0, node->npages); /* * If called by the MMU notifier, we have to adjust the pinned * page count ourselves. */ - if (notifier) - current->mm->pinned_vm -= node->npages; + if (mm) + mm->pinned_vm -= node->npages; kfree(node); } diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c index 36d07295f8e3..5e820b541506 100644 --- a/drivers/thermal/hisi_thermal.c +++ b/drivers/thermal/hisi_thermal.c @@ -68,12 +68,12 @@ static inline int _step_to_temp(int step) * Every step equals (1 * 200) / 255 celsius, and finally * need convert to millicelsius. */ - return (HISI_TEMP_BASE + (step * 200 / 255)) * 1000; + return (HISI_TEMP_BASE * 1000 + (step * 200000 / 255)); } static inline long _temp_to_step(long temp) { - return ((temp / 1000 - HISI_TEMP_BASE) * 255 / 200); + return ((temp - HISI_TEMP_BASE * 1000) * 255) / 200000; } static long hisi_thermal_get_sensor_temp(struct hisi_thermal_data *data, diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index f1db49625555..5133cd1e10b7 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -959,7 +959,7 @@ static DEVICE_ATTR(sustainable_power, S_IWUSR | S_IRUGO, sustainable_power_show, struct thermal_zone_device *tz = to_thermal_zone(dev); \ \ if (tz->tzp) \ - return sprintf(buf, "%u\n", tz->tzp->name); \ + return sprintf(buf, "%d\n", tz->tzp->name); \ else \ return -EIO; \ } \ diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 0058d9fbf931..cf0dc51a2690 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -626,7 +626,7 @@ static int pty_unix98_ioctl(struct tty_struct *tty, */ static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver, - struct inode *ptm_inode, int idx) + struct file *file, int idx) { /* Master must be open via /dev/ptmx */ return ERR_PTR(-EIO); @@ -642,12 +642,12 @@ static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver, */ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver, - struct inode *pts_inode, int idx) + struct file *file, int idx) { struct tty_struct *tty; mutex_lock(&devpts_mutex); - tty = devpts_get_priv(pts_inode); + tty = devpts_get_priv(file->f_path.dentry); mutex_unlock(&devpts_mutex); /* Master must be open before slave */ if (!tty) @@ -722,7 +722,7 @@ static int ptmx_open(struct inode *inode, struct file *filp) { struct pts_fs_info *fsi; struct tty_struct *tty; - struct inode *slave_inode; + struct dentry *dentry; int retval; int index; @@ -769,14 +769,12 @@ static int ptmx_open(struct inode *inode, struct file *filp) tty_add_file(tty, filp); - slave_inode = devpts_pty_new(fsi, - MKDEV(UNIX98_PTY_SLAVE_MAJOR, index), index, - tty->link); - if (IS_ERR(slave_inode)) { - retval = PTR_ERR(slave_inode); + dentry = devpts_pty_new(fsi, index, tty->link); + if (IS_ERR(dentry)) { + retval = PTR_ERR(dentry); goto err_release; } - tty->link->driver_data = slave_inode; + tty->link->driver_data = dentry; retval = ptm_driver->ops->open(tty, filp); if (retval) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 9b04d72e752e..24d5491ef0da 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1367,12 +1367,12 @@ static ssize_t tty_line_name(struct tty_driver *driver, int index, char *p) * Locking: tty_mutex must be held. If the tty is found, bump the tty kref. */ static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, - struct inode *inode, int idx) + struct file *file, int idx) { struct tty_struct *tty; if (driver->ops->lookup) - tty = driver->ops->lookup(driver, inode, idx); + tty = driver->ops->lookup(driver, file, idx); else tty = driver->ttys[idx]; @@ -2040,7 +2040,7 @@ static struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode, } /* check whether we're reopening an existing tty */ - tty = tty_driver_lookup_tty(driver, inode, index); + tty = tty_driver_lookup_tty(driver, filp, index); if (IS_ERR(tty)) { mutex_unlock(&tty_mutex); goto out; diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 14718a9ffcfb..460c855be0d0 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -249,18 +249,12 @@ static int usb_port_runtime_suspend(struct device *dev) return retval; } - -static int usb_port_prepare(struct device *dev) -{ - return 1; -} #endif static const struct dev_pm_ops usb_port_pm_ops = { #ifdef CONFIG_PM .runtime_suspend = usb_port_runtime_suspend, .runtime_resume = usb_port_runtime_resume, - .prepare = usb_port_prepare, #endif }; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index dcb85e3cd5a7..479187c32571 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -312,13 +312,7 @@ static int usb_dev_uevent(struct device *dev, struct kobj_uevent_env *env) static int usb_dev_prepare(struct device *dev) { - struct usb_device *udev = to_usb_device(dev); - - /* Return 0 if the current wakeup setting is wrong, otherwise 1 */ - if (udev->do_remote_wakeup != device_may_wakeup(dev)) - return 0; - - return 1; + return 0; /* Implement eventually? */ } static void usb_dev_complete(struct device *dev) diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index 5e5a8fa005f8..bc8889956d17 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -83,9 +83,9 @@ static int jz4740_musb_init(struct musb *musb) { usb_phy_generic_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); - if (!musb->xceiv) { + if (IS_ERR(musb->xceiv)) { pr_err("HS UDC: no transceiver configured\n"); - return -ENODEV; + return PTR_ERR(musb->xceiv); } /* Silicon does not implement ConfigData register. diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 87bd578799a8..152865b36522 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1164,12 +1164,12 @@ static int musb_gadget_disable(struct usb_ep *ep) musb_writew(epio, MUSB_RXMAXP, 0); } - musb_ep->desc = NULL; - musb_ep->end_point.desc = NULL; - /* abort all pending DMA and requests */ nuke(musb_ep, -ESHUTDOWN); + musb_ep->desc = NULL; + musb_ep->end_point.desc = NULL; + schedule_work(&musb->irq_work); spin_unlock_irqrestore(&(musb->lock), flags); diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 58487a473521..2f8ad7f1f482 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2735,7 +2735,7 @@ static const struct hc_driver musb_hc_driver = { .description = "musb-hcd", .product_desc = "MUSB HDRC host driver", .hcd_priv_size = sizeof(struct musb *), - .flags = HCD_USB2 | HCD_MEMORY | HCD_BH, + .flags = HCD_USB2 | HCD_MEMORY, /* not using irq handler or reset hooks from usbcore, since * those must be shared with peripheral code for OTG configs diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index dd47823bb014..7c9f25e9c422 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -109,6 +109,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */ { USB_DEVICE(0x10C4, 0x8281) }, /* Nanotec Plug & Drive */ { USB_DEVICE(0x10C4, 0x8293) }, /* Telegesis ETRX2USB */ + { USB_DEVICE(0x10C4, 0x82F4) }, /* Starizona MicroTouch */ { USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */ { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ { USB_DEVICE(0x10C4, 0x8382) }, /* Cygnal Integrated Products, Inc. */ @@ -118,6 +119,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */ { USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */ { USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */ + { USB_DEVICE(0x10C4, 0x84B6) }, /* Starizona Hyperion */ { USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */ { USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */ { USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */ @@ -141,6 +143,8 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0xF004) }, /* Elan Digital Systems USBcount50 */ { USB_DEVICE(0x10C5, 0xEA61) }, /* Silicon Labs MobiData GPRS USB Modem */ { USB_DEVICE(0x10CE, 0xEA6A) }, /* Silicon Labs MobiData GPRS USB Modem 100EU */ + { USB_DEVICE(0x12B8, 0xEC60) }, /* Link G4 ECU */ + { USB_DEVICE(0x12B8, 0xEC62) }, /* Link G4+ ECU */ { USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */ { USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */ { USB_DEVICE(0x166A, 0x0201) }, /* Clipsal 5500PACA C-Bus Pascal Automation Controller */ diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 5c802d47892c..ca6bfddaacad 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -1006,7 +1006,7 @@ struct virtqueue *vring_create_virtqueue( const char *name) { struct virtqueue *vq; - void *queue; + void *queue = NULL; dma_addr_t dma_addr; size_t queue_size_in_bytes; struct vring vring; diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 9781e0dd59d6..d46839f51e73 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -151,6 +151,8 @@ static DECLARE_WAIT_QUEUE_HEAD(balloon_wq); static void balloon_process(struct work_struct *work); static DECLARE_DELAYED_WORK(balloon_worker, balloon_process); +static void release_memory_resource(struct resource *resource); + /* When ballooning out (allocating memory to return to Xen) we don't really want the kernel to try too hard since that can trigger the oom killer. */ #define GFP_BALLOON \ @@ -267,6 +269,20 @@ static struct resource *additional_memory_resource(phys_addr_t size) return NULL; } +#ifdef CONFIG_SPARSEMEM + { + unsigned long limit = 1UL << (MAX_PHYSMEM_BITS - PAGE_SHIFT); + unsigned long pfn = res->start >> PAGE_SHIFT; + + if (pfn > limit) { + pr_err("New System RAM resource outside addressable RAM (%lu > %lu)\n", + pfn, limit); + release_memory_resource(res); + return NULL; + } + } +#endif + return res; } diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c index 38272ad24551..f4edd6df3df2 100644 --- a/drivers/xen/evtchn.c +++ b/drivers/xen/evtchn.c @@ -316,7 +316,6 @@ static int evtchn_resize_ring(struct per_user_data *u) { unsigned int new_size; evtchn_port_t *new_ring, *old_ring; - unsigned int p, c; /* * Ensure the ring is large enough to capture all possible @@ -346,20 +345,17 @@ static int evtchn_resize_ring(struct per_user_data *u) /* * Copy the old ring contents to the new ring. * - * If the ring contents crosses the end of the current ring, - * it needs to be copied in two chunks. + * To take care of wrapping, a full ring, and the new index + * pointing into the second half, simply copy the old contents + * twice. * * +---------+ +------------------+ - * |34567 12| -> | 1234567 | - * +-----p-c-+ +------------------+ + * |34567 12| -> |34567 1234567 12| + * +-----p-c-+ +-------c------p---+ */ - p = evtchn_ring_offset(u, u->ring_prod); - c = evtchn_ring_offset(u, u->ring_cons); - if (p < c) { - memcpy(new_ring + c, u->ring + c, (u->ring_size - c) * sizeof(*u->ring)); - memcpy(new_ring + u->ring_size, u->ring, p * sizeof(*u->ring)); - } else - memcpy(new_ring + c, u->ring + c, (p - c) * sizeof(*u->ring)); + memcpy(new_ring, old_ring, u->ring_size * sizeof(*u->ring)); + memcpy(new_ring + u->ring_size, old_ring, + u->ring_size * sizeof(*u->ring)); u->ring = new_ring; u->ring_size = new_size; |