diff options
Diffstat (limited to 'drivers')
850 files changed, 21713 insertions, 11936 deletions
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 36b0e61..bbcc2b5 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -95,13 +95,14 @@ static struct acpi_driver acpi_ac_driver = { }; struct acpi_ac { - struct power_supply charger; + struct power_supply *charger; + struct power_supply_desc charger_desc; struct acpi_device * device; unsigned long long state; struct notifier_block battery_nb; }; -#define to_acpi_ac(x) container_of(x, struct acpi_ac, charger) +#define to_acpi_ac(x) power_supply_get_drvdata(x) #ifdef CONFIG_ACPI_PROCFS_POWER static const struct file_operations acpi_ac_fops = { @@ -275,7 +276,7 @@ static void acpi_ac_notify(struct acpi_device *device, u32 event) dev_name(&device->dev), event, (u32) ac->state); acpi_notifier_call_chain(device, event, (u32) ac->state); - kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); + kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE); } return; @@ -321,6 +322,7 @@ static struct dmi_system_id ac_dmi_table[] = { static int acpi_ac_add(struct acpi_device *device) { + struct power_supply_config psy_cfg = {}; int result = 0; struct acpi_ac *ac = NULL; @@ -341,19 +343,24 @@ static int acpi_ac_add(struct acpi_device *device) if (result) goto end; - ac->charger.name = acpi_device_bid(device); + psy_cfg.drv_data = ac; + + ac->charger_desc.name = acpi_device_bid(device); #ifdef CONFIG_ACPI_PROCFS_POWER result = acpi_ac_add_fs(ac); if (result) goto end; #endif - ac->charger.type = POWER_SUPPLY_TYPE_MAINS; - ac->charger.properties = ac_props; - ac->charger.num_properties = ARRAY_SIZE(ac_props); - ac->charger.get_property = get_ac_property; - result = power_supply_register(&ac->device->dev, &ac->charger); - if (result) + ac->charger_desc.type = POWER_SUPPLY_TYPE_MAINS; + ac->charger_desc.properties = ac_props; + ac->charger_desc.num_properties = ARRAY_SIZE(ac_props); + ac->charger_desc.get_property = get_ac_property; + ac->charger = power_supply_register(&ac->device->dev, + &ac->charger_desc, &psy_cfg); + if (IS_ERR(ac->charger)) { + result = PTR_ERR(ac->charger); goto end; + } printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device), acpi_device_bid(device), @@ -390,7 +397,7 @@ static int acpi_ac_resume(struct device *dev) if (acpi_ac_get_state(ac)) return 0; if (old_state != ac->state) - kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); + kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE); return 0; } #else @@ -407,8 +414,7 @@ static int acpi_ac_remove(struct acpi_device *device) ac = acpi_driver_data(device); - if (ac->charger.dev) - power_supply_unregister(&ac->charger); + power_supply_unregister(ac->charger); unregister_acpi_notifier(&ac->battery_nb); #ifdef CONFIG_ACPI_PROCFS_POWER diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 657964e..37fb190 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -65,6 +65,7 @@ struct lpss_private_data; struct lpss_device_desc { unsigned int flags; + const char *clk_con_id; unsigned int prv_offset; size_t prv_size_override; void (*setup)(struct lpss_private_data *pdata); @@ -140,6 +141,7 @@ static struct lpss_device_desc lpt_i2c_dev_desc = { static struct lpss_device_desc lpt_uart_dev_desc = { .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR, + .clk_con_id = "baudclk", .prv_offset = 0x800, .setup = lpss_uart_setup, }; @@ -156,6 +158,7 @@ static struct lpss_device_desc byt_pwm_dev_desc = { static struct lpss_device_desc byt_uart_dev_desc = { .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, + .clk_con_id = "baudclk", .prv_offset = 0x800, .setup = lpss_uart_setup, }; @@ -313,7 +316,7 @@ out: return PTR_ERR(clk); pdata->clk = clk; - clk_register_clkdev(clk, NULL, devname); + clk_register_clkdev(clk, dev_desc->clk_con_id, devname); return 0; } diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index c7b105c..6bc9cbc 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -26,7 +26,7 @@ #include <linux/kthread.h> #include <linux/freezer.h> #include <linux/cpu.h> -#include <linux/clockchips.h> +#include <linux/tick.h> #include <linux/slab.h> #include <linux/acpi.h> #include <asm/mwait.h> @@ -41,8 +41,6 @@ static unsigned long power_saving_mwait_eax; static unsigned char tsc_detected_unstable; static unsigned char tsc_marked_unstable; -static unsigned char lapic_detected_unstable; -static unsigned char lapic_marked_unstable; static void power_saving_mwait_init(void) { @@ -82,13 +80,10 @@ static void power_saving_mwait_init(void) */ if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) tsc_detected_unstable = 1; - if (!boot_cpu_has(X86_FEATURE_ARAT)) - lapic_detected_unstable = 1; break; default: - /* TSC & LAPIC could halt in idle */ + /* TSC could halt in idle */ tsc_detected_unstable = 1; - lapic_detected_unstable = 1; } #endif } @@ -155,7 +150,6 @@ static int power_saving_thread(void *data) sched_setscheduler(current, SCHED_RR, ¶m); while (!kthread_should_stop()) { - int cpu; unsigned long expire_time; try_to_freeze(); @@ -177,28 +171,15 @@ static int power_saving_thread(void *data) mark_tsc_unstable("TSC halts in idle"); tsc_marked_unstable = 1; } - if (lapic_detected_unstable && !lapic_marked_unstable) { - int i; - /* LAPIC could halt in idle, so notify users */ - for_each_online_cpu(i) - clockevents_notify( - CLOCK_EVT_NOTIFY_BROADCAST_ON, - &i); - lapic_marked_unstable = 1; - } local_irq_disable(); - cpu = smp_processor_id(); - if (lapic_marked_unstable) - clockevents_notify( - CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu); + tick_broadcast_enable(); + tick_broadcast_enter(); stop_critical_timings(); mwait_idle_with_hints(power_saving_mwait_eax, 1); start_critical_timings(); - if (lapic_marked_unstable) - clockevents_notify( - CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu); + tick_broadcast_exit(); local_irq_enable(); if (time_before(expire_time, jiffies)) { diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index d98ba43..fdc16ce 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -117,7 +117,8 @@ enum { struct acpi_battery { struct mutex lock; struct mutex sysfs_lock; - struct power_supply bat; + struct power_supply *bat; + struct power_supply_desc bat_desc; struct acpi_device *device; struct notifier_block pm_nb; unsigned long update_time; @@ -149,7 +150,7 @@ struct acpi_battery { unsigned long flags; }; -#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat) +#define to_acpi_battery(x) power_supply_get_drvdata(x) static inline int acpi_battery_present(struct acpi_battery *battery) { @@ -608,40 +609,45 @@ static struct device_attribute alarm_attr = { static int sysfs_add_battery(struct acpi_battery *battery) { - int result; + struct power_supply_config psy_cfg = { .drv_data = battery, }; if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) { - battery->bat.properties = charge_battery_props; - battery->bat.num_properties = + battery->bat_desc.properties = charge_battery_props; + battery->bat_desc.num_properties = ARRAY_SIZE(charge_battery_props); } else { - battery->bat.properties = energy_battery_props; - battery->bat.num_properties = + battery->bat_desc.properties = energy_battery_props; + battery->bat_desc.num_properties = ARRAY_SIZE(energy_battery_props); } - battery->bat.name = acpi_device_bid(battery->device); - battery->bat.type = POWER_SUPPLY_TYPE_BATTERY; - battery->bat.get_property = acpi_battery_get_property; + battery->bat_desc.name = acpi_device_bid(battery->device); + battery->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; + battery->bat_desc.get_property = acpi_battery_get_property; - result = power_supply_register_no_ws(&battery->device->dev, &battery->bat); + battery->bat = power_supply_register_no_ws(&battery->device->dev, + &battery->bat_desc, &psy_cfg); - if (result) + if (IS_ERR(battery->bat)) { + int result = PTR_ERR(battery->bat); + + battery->bat = NULL; return result; - return device_create_file(battery->bat.dev, &alarm_attr); + } + return device_create_file(&battery->bat->dev, &alarm_attr); } static void sysfs_remove_battery(struct acpi_battery *battery) { mutex_lock(&battery->sysfs_lock); - if (!battery->bat.dev) { + if (!battery->bat) { mutex_unlock(&battery->sysfs_lock); return; } - device_remove_file(battery->bat.dev, &alarm_attr); - power_supply_unregister(&battery->bat); - battery->bat.dev = NULL; + device_remove_file(&battery->bat->dev, &alarm_attr); + power_supply_unregister(battery->bat); + battery->bat = NULL; mutex_unlock(&battery->sysfs_lock); } @@ -738,7 +744,7 @@ static int acpi_battery_update(struct acpi_battery *battery, bool resume) return result; acpi_battery_init_alarm(battery); } - if (!battery->bat.dev) { + if (!battery->bat) { result = sysfs_add_battery(battery); if (result) return result; @@ -764,7 +770,7 @@ static void acpi_battery_refresh(struct acpi_battery *battery) { int power_unit; - if (!battery->bat.dev) + if (!battery->bat) return; power_unit = battery->power_unit; @@ -1062,11 +1068,11 @@ static void acpi_battery_remove_fs(struct acpi_device *device) static void acpi_battery_notify(struct acpi_device *device, u32 event) { struct acpi_battery *battery = acpi_driver_data(device); - struct device *old; + struct power_supply *old; if (!battery) return; - old = battery->bat.dev; + old = battery->bat; /* * On Acer Aspire V5-573G notifications are sometimes triggered too * early. For example, when AC is unplugged and notification is @@ -1083,8 +1089,8 @@ static void acpi_battery_notify(struct acpi_device *device, u32 event) acpi_battery_present(battery)); acpi_notifier_call_chain(device, event, acpi_battery_present(battery)); /* acpi_battery_update could remove power_supply object */ - if (old && battery->bat.dev) - power_supply_changed(&battery->bat); + if (old && battery->bat) + power_supply_changed(battery->bat); } static int battery_notify(struct notifier_block *nb, @@ -1100,7 +1106,7 @@ static int battery_notify(struct notifier_block *nb, if (!acpi_battery_present(battery)) return 0; - if (!battery->bat.dev) { + if (battery->bat) { result = acpi_battery_get_info(battery); if (result) return result; diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index e7f718d..b1def41 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -485,6 +485,14 @@ void acpi_pci_irq_disable(struct pci_dev *dev) if (!pin || !dev->irq_managed || dev->irq <= 0) return; + /* Keep IOAPIC pin configuration when suspending */ + if (dev->dev.power.is_prepared) + return; +#ifdef CONFIG_PM + if (dev->dev.power.runtime_status == RPM_SUSPENDING) + return; +#endif + entry = acpi_pci_irq_lookup(dev, pin); if (!entry) return; @@ -505,6 +513,5 @@ void acpi_pci_irq_disable(struct pci_dev *dev) if (gsi >= 0) { acpi_unregister_gsi(gsi); dev->irq_managed = 0; - dev->irq = 0; } } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 68a5f71..1b5569c 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -423,8 +423,7 @@ out: } EXPORT_SYMBOL(acpi_pci_osc_control_set); -static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, - int *clear_aspm) +static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm) { u32 support, control, requested; acpi_status status; @@ -495,10 +494,12 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, decode_osc_control(root, "OS now controls", control); if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { /* - * We have ASPM control, but the FADT indicates - * that it's unsupported. Clear it. + * We have ASPM control, but the FADT indicates that + * it's unsupported. Leave existing configuration + * intact and prevent the OS from touching it. */ - *clear_aspm = 1; + dev_info(&device->dev, "FADT indicates ASPM is unsupported, using BIOS configuration\n"); + *no_aspm = 1; } } else { decode_osc_control(root, "OS requested", requested); @@ -525,7 +526,7 @@ static int acpi_pci_root_add(struct acpi_device *device, int result; struct acpi_pci_root *root; acpi_handle handle = device->handle; - int no_aspm = 0, clear_aspm = 0; + int no_aspm = 0; bool hotadd = system_state != SYSTEM_BOOTING; root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL); @@ -584,7 +585,7 @@ static int acpi_pci_root_add(struct acpi_device *device, root->mcfg_addr = acpi_pci_root_get_mcfg_addr(handle); - negotiate_os_control(root, &no_aspm, &clear_aspm); + negotiate_os_control(root, &no_aspm); /* * TBD: Need PCI interface for enumeration/configuration of roots. @@ -607,10 +608,6 @@ static int acpi_pci_root_add(struct acpi_device *device, goto remove_dmar; } - if (clear_aspm) { - dev_info(&device->dev, "Disabling ASPM (FADT indicates it is unsupported)\n"); - pcie_clear_aspm(root->bus); - } if (no_aspm) pcie_no_aspm(); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index c6bb9f1..39e0c8e 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -32,7 +32,7 @@ #include <linux/acpi.h> #include <linux/dmi.h> #include <linux/sched.h> /* need_resched() */ -#include <linux/clockchips.h> +#include <linux/tick.h> #include <linux/cpuidle.h> #include <linux/syscore_ops.h> #include <acpi/processor.h> @@ -157,12 +157,11 @@ static void lapic_timer_check_state(int state, struct acpi_processor *pr, static void __lapic_timer_propagate_broadcast(void *arg) { struct acpi_processor *pr = (struct acpi_processor *) arg; - unsigned long reason; - reason = pr->power.timer_broadcast_on_state < INT_MAX ? - CLOCK_EVT_NOTIFY_BROADCAST_ON : CLOCK_EVT_NOTIFY_BROADCAST_OFF; - - clockevents_notify(reason, &pr->id); + if (pr->power.timer_broadcast_on_state < INT_MAX) + tick_broadcast_enable(); + else + tick_broadcast_disable(); } static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) @@ -179,11 +178,10 @@ static void lapic_timer_state_broadcast(struct acpi_processor *pr, int state = cx - pr->power.states; if (state >= pr->power.timer_broadcast_on_state) { - unsigned long reason; - - reason = broadcast ? CLOCK_EVT_NOTIFY_BROADCAST_ENTER : - CLOCK_EVT_NOTIFY_BROADCAST_EXIT; - clockevents_notify(reason, &pr->id); + if (broadcast) + tick_broadcast_enter(); + else + tick_broadcast_exit(); } } @@ -922,7 +920,7 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) return -EINVAL; drv->safe_state_index = -1; - for (i = 0; i < CPUIDLE_STATE_MAX; i++) { + for (i = CPUIDLE_DRIVER_STATE_START; i < CPUIDLE_STATE_MAX; i++) { drv->states[i].name[0] = '\0'; drv->states[i].desc[0] = '\0'; } diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index c723668..5589a6e 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -42,8 +42,10 @@ static bool acpi_dev_resource_len_valid(u64 start, u64 end, u64 len, bool io) * CHECKME: len might be required to check versus a minimum * length as well. 1 for io is fine, but for memory it does * not make any sense at all. + * Note: some BIOSes report incorrect length for ACPI address space + * descriptor, so remove check of 'reslen == len' to avoid regression. */ - if (len && reslen && reslen == len && start <= end) + if (len && reslen && start <= end) return true; pr_debug("ACPI: invalid or unassigned resource %s [%016llx - %016llx] length [%016llx]\n", diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index a7a3edd..cd82762 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -74,7 +74,8 @@ static const struct acpi_device_id sbs_device_ids[] = { MODULE_DEVICE_TABLE(acpi, sbs_device_ids); struct acpi_battery { - struct power_supply bat; + struct power_supply *bat; + struct power_supply_desc bat_desc; struct acpi_sbs *sbs; unsigned long update_time; char name[8]; @@ -101,10 +102,10 @@ struct acpi_battery { u8 have_sysfs_alarm:1; }; -#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat) +#define to_acpi_battery(x) power_supply_get_drvdata(x) struct acpi_sbs { - struct power_supply charger; + struct power_supply *charger; struct acpi_device *device; struct acpi_smb_hc *hc; struct mutex lock; @@ -115,7 +116,7 @@ struct acpi_sbs { u8 charger_exists:1; }; -#define to_acpi_sbs(x) container_of(x, struct acpi_sbs, charger) +#define to_acpi_sbs(x) power_supply_get_drvdata(x) static int acpi_sbs_remove(struct acpi_device *device); static int acpi_battery_get_state(struct acpi_battery *battery); @@ -303,6 +304,13 @@ static enum power_supply_property sbs_energy_battery_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; +static const struct power_supply_desc acpi_sbs_charger_desc = { + .name = "sbs-charger", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = sbs_ac_props, + .num_properties = ARRAY_SIZE(sbs_ac_props), + .get_property = sbs_get_ac_property, +}; /* -------------------------------------------------------------------------- Smart Battery System Management @@ -519,6 +527,7 @@ static int acpi_battery_read(struct acpi_battery *battery) static int acpi_battery_add(struct acpi_sbs *sbs, int id) { struct acpi_battery *battery = &sbs->battery[id]; + struct power_supply_config psy_cfg = { .drv_data = battery, }; int result; battery->id = id; @@ -528,23 +537,27 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id) return result; sprintf(battery->name, ACPI_BATTERY_DIR_NAME, id); - battery->bat.name = battery->name; - battery->bat.type = POWER_SUPPLY_TYPE_BATTERY; + battery->bat_desc.name = battery->name; + battery->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; if (!acpi_battery_mode(battery)) { - battery->bat.properties = sbs_charge_battery_props; - battery->bat.num_properties = + battery->bat_desc.properties = sbs_charge_battery_props; + battery->bat_desc.num_properties = ARRAY_SIZE(sbs_charge_battery_props); } else { - battery->bat.properties = sbs_energy_battery_props; - battery->bat.num_properties = + battery->bat_desc.properties = sbs_energy_battery_props; + battery->bat_desc.num_properties = ARRAY_SIZE(sbs_energy_battery_props); } - battery->bat.get_property = acpi_sbs_battery_get_property; - result = power_supply_register(&sbs->device->dev, &battery->bat); - if (result) + battery->bat_desc.get_property = acpi_sbs_battery_get_property; + battery->bat = power_supply_register(&sbs->device->dev, + &battery->bat_desc, &psy_cfg); + if (IS_ERR(battery->bat)) { + result = PTR_ERR(battery->bat); + battery->bat = NULL; goto end; + } - result = device_create_file(battery->bat.dev, &alarm_attr); + result = device_create_file(&battery->bat->dev, &alarm_attr); if (result) goto end; battery->have_sysfs_alarm = 1; @@ -559,28 +572,29 @@ static void acpi_battery_remove(struct acpi_sbs *sbs, int id) { struct acpi_battery *battery = &sbs->battery[id]; - if (battery->bat.dev) { + if (battery->bat) { if (battery->have_sysfs_alarm) - device_remove_file(battery->bat.dev, &alarm_attr); - power_supply_unregister(&battery->bat); + device_remove_file(&battery->bat->dev, &alarm_attr); + power_supply_unregister(battery->bat); } } static int acpi_charger_add(struct acpi_sbs *sbs) { int result; + struct power_supply_config psy_cfg = { .drv_data = sbs, }; result = acpi_ac_get_present(sbs); if (result) goto end; sbs->charger_exists = 1; - sbs->charger.name = "sbs-charger"; - sbs->charger.type = POWER_SUPPLY_TYPE_MAINS; - sbs->charger.properties = sbs_ac_props; - sbs->charger.num_properties = ARRAY_SIZE(sbs_ac_props); - sbs->charger.get_property = sbs_get_ac_property; - power_supply_register(&sbs->device->dev, &sbs->charger); + sbs->charger = power_supply_register(&sbs->device->dev, + &acpi_sbs_charger_desc, &psy_cfg); + if (IS_ERR(sbs->charger)) { + result = PTR_ERR(sbs->charger); + sbs->charger = NULL; + } printk(KERN_INFO PREFIX "%s [%s]: AC Adapter [%s] (%s)\n", ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), ACPI_AC_DIR_NAME, sbs->charger_present ? "on-line" : "off-line"); @@ -590,8 +604,8 @@ static int acpi_charger_add(struct acpi_sbs *sbs) static void acpi_charger_remove(struct acpi_sbs *sbs) { - if (sbs->charger.dev) - power_supply_unregister(&sbs->charger); + if (sbs->charger) + power_supply_unregister(sbs->charger); } static void acpi_sbs_callback(void *context) @@ -605,7 +619,7 @@ static void acpi_sbs_callback(void *context) if (sbs->charger_exists) { acpi_ac_get_present(sbs); if (sbs->charger_present != saved_charger_state) - kobject_uevent(&sbs->charger.dev->kobj, KOBJ_CHANGE); + kobject_uevent(&sbs->charger->dev.kobj, KOBJ_CHANGE); } if (sbs->manager_present) { @@ -617,7 +631,7 @@ static void acpi_sbs_callback(void *context) acpi_battery_read(bat); if (saved_battery_state == bat->present) continue; - kobject_uevent(&bat->bat.dev->kobj, KOBJ_CHANGE); + kobject_uevent(&bat->bat->dev.kobj, KOBJ_CHANGE); } } } diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index debd309..26eb70c 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -2110,7 +2110,8 @@ static int __init intel_opregion_present(void) int acpi_video_register(void) { - int result = 0; + int ret; + if (register_count) { /* * if the function of acpi_video_register is already called, @@ -2122,9 +2123,9 @@ int acpi_video_register(void) mutex_init(&video_list_lock); INIT_LIST_HEAD(&video_bus_head); - result = acpi_bus_register_driver(&acpi_video_bus); - if (result < 0) - return -ENODEV; + ret = acpi_bus_register_driver(&acpi_video_bus); + if (ret) + return ret; /* * When the acpi_video_bus is loaded successfully, increase @@ -2176,6 +2177,17 @@ EXPORT_SYMBOL(acpi_video_unregister_backlight); static int __init acpi_video_init(void) { + /* + * Let the module load even if ACPI is disabled (e.g. due to + * a broken BIOS) so that i915.ko can still be loaded on such + * old systems without an AcpiOpRegion. + * + * acpi_video_register() will report -ENODEV later as well due + * to acpi_disabled when i915.ko tries to register itself afterwards. + */ + if (acpi_disabled) + return 0; + dmi_check_system(video_dmi_table); if (intel_opregion_present()) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 33b09b6..6607f3c 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -551,7 +551,6 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, { void *page_addr; unsigned long user_page_addr; - struct vm_struct tmp_area; struct page **page; struct mm_struct *mm; @@ -600,10 +599,11 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, proc->pid, page_addr); goto err_alloc_page_failed; } - tmp_area.addr = page_addr; - tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */; - ret = map_vm_area(&tmp_area, PAGE_KERNEL, page); - if (ret) { + ret = map_kernel_range_noflush((unsigned long)page_addr, + PAGE_SIZE, PAGE_KERNEL, page); + flush_cache_vmap((unsigned long)page_addr, + (unsigned long)page_addr + PAGE_SIZE); + if (ret != 1) { pr_err("%d: binder_alloc_buf failed to map page at %p in kernel\n", proc->pid, page_addr); goto err_map_kernel_failed; diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 4c35f08..23dac3b 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4204,9 +4204,18 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "PIONEER DVD-RW DVR-216D", NULL, ATA_HORKAGE_NOSETXFER }, /* devices that don't properly handle queued TRIM commands */ - { "Micron_M[56]*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | + { "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Crucial_CT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Micron_M5[15]0*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Crucial_CT*M550*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Crucial_CT*MX100*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Samsung SSD 850 PRO*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | ATA_HORKAGE_ZERO_AFTER_TRIM, }, - { "Crucial_CT*SSD*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, /* * As defined, the DRAT (Deterministic Read After Trim) and RZAT @@ -4226,6 +4235,8 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { */ { "INTEL*SSDSC2MH*", NULL, 0, }, + { "Micron*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Crucial*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, { "INTEL*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, { "SSD*INTEL*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, { "Samsung*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, @@ -4737,7 +4748,7 @@ struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev, int tag) return NULL; /* libsas case */ - if (!ap->scsi_host) { + if (ap->flags & ATA_FLAG_SAS_HOST) { tag = ata_sas_allocate_tag(ap); if (tag < 0) return NULL; @@ -4776,7 +4787,7 @@ void ata_qc_free(struct ata_queued_cmd *qc) tag = qc->tag; if (likely(ata_tag_valid(tag))) { qc->tag = ATA_TAG_POISON; - if (!ap->scsi_host) + if (ap->flags & ATA_FLAG_SAS_HOST) ata_sas_free_tag(tag, ap); } } diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index f9054cd..5389579 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -869,6 +869,8 @@ try_offline_again: */ ata_msleep(ap, 1); + sata_set_spd(link); + /* * Now, bring the host controller online again, this can take time * as PHY reset and communication establishment, 1st D2H FIS and diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index ba4abbe..45937f8 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2242,7 +2242,7 @@ static void rtpm_status_str(struct seq_file *s, struct device *dev) } static int pm_genpd_summary_one(struct seq_file *s, - struct generic_pm_domain *gpd) + struct generic_pm_domain *genpd) { static const char * const status_lookup[] = { [GPD_STATE_ACTIVE] = "on", @@ -2256,26 +2256,26 @@ static int pm_genpd_summary_one(struct seq_file *s, struct gpd_link *link; int ret; - ret = mutex_lock_interruptible(&gpd->lock); + ret = mutex_lock_interruptible(&genpd->lock); if (ret) return -ERESTARTSYS; - if (WARN_ON(gpd->status >= ARRAY_SIZE(status_lookup))) + if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup))) goto exit; - seq_printf(s, "%-30s %-15s ", gpd->name, status_lookup[gpd->status]); + seq_printf(s, "%-30s %-15s ", genpd->name, status_lookup[genpd->status]); /* * Modifications on the list require holding locks on both * master and slave, so we are safe. - * Also gpd->name is immutable. + * Also genpd->name is immutable. */ - list_for_each_entry(link, &gpd->master_links, master_node) { + list_for_each_entry(link, &genpd->master_links, master_node) { seq_printf(s, "%s", link->slave->name); - if (!list_is_last(&link->master_node, &gpd->master_links)) + if (!list_is_last(&link->master_node, &genpd->master_links)) seq_puts(s, ", "); } - list_for_each_entry(pm_data, &gpd->dev_list, list_node) { + list_for_each_entry(pm_data, &genpd->dev_list, list_node) { kobj_path = kobject_get_path(&pm_data->dev->kobj, GFP_KERNEL); if (kobj_path == NULL) continue; @@ -2287,14 +2287,14 @@ static int pm_genpd_summary_one(struct seq_file *s, seq_puts(s, "\n"); exit: - mutex_unlock(&gpd->lock); + mutex_unlock(&genpd->lock); return 0; } static int pm_genpd_summary_show(struct seq_file *s, void *data) { - struct generic_pm_domain *gpd; + struct generic_pm_domain *genpd; int ret = 0; seq_puts(s, " domain status slaves\n"); @@ -2305,8 +2305,8 @@ static int pm_genpd_summary_show(struct seq_file *s, void *data) if (ret) return -ERESTARTSYS; - list_for_each_entry(gpd, &gpd_list, gpd_list_node) { - ret = pm_genpd_summary_one(s, gpd); + list_for_each_entry(genpd, &gpd_list, gpd_list_node) { + ret = pm_genpd_summary_one(s, genpd); if (ret) break; } diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index c2744b3..aab7158 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -730,6 +730,7 @@ void pm_system_wakeup(void) pm_abort_suspend = true; freeze_wake(); } +EXPORT_SYMBOL_GPL(pm_system_wakeup); void pm_wakeup_clear(void) { diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index 0a53365..609e4c8 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -1,3 +1,6 @@ +# For include/trace/define_trace.h to include trace.h +CFLAGS_regmap.o := -I$(src) + obj-$(CONFIG_REGMAP) += regmap.o regcache.o obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index beb8b27..a13587b 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -243,4 +243,12 @@ extern struct regcache_ops regcache_rbtree_ops; extern struct regcache_ops regcache_lzo_ops; extern struct regcache_ops regcache_flat_ops; +static inline const char *regmap_name(const struct regmap *map) +{ + if (map->dev) + return dev_name(map->dev); + + return map->name; +} + #endif diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index d453a2c..81751a4 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -307,7 +307,7 @@ static int regcache_rbtree_insert_to_block(struct regmap *map, if (pos == 0) { memmove(blk + offset * map->cache_word_size, blk, rbnode->blklen * map->cache_word_size); - bitmap_shift_right(present, present, offset, blklen); + bitmap_shift_left(present, present, offset, blklen); } /* update the rbnode block, its size and the base register */ diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index f373c35..7eb7b3b 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -15,8 +15,8 @@ #include <linux/export.h> #include <linux/slab.h> #include <linux/sort.h> -#include <trace/events/regmap.h> +#include "trace.h" #include "internal.h" static const struct regcache_ops *cache_types[] = { @@ -218,7 +218,7 @@ int regcache_read(struct regmap *map, ret = map->cache_ops->read(map, reg, value); if (ret == 0) - trace_regmap_reg_read_cache(map->dev, reg, *value); + trace_regmap_reg_read_cache(map, reg, *value); return ret; } @@ -311,7 +311,7 @@ int regcache_sync(struct regmap *map) dev_dbg(map->dev, "Syncing %s cache\n", map->cache_ops->name); name = map->cache_ops->name; - trace_regcache_sync(map->dev, name, "start"); + trace_regcache_sync(map, name, "start"); if (!map->cache_dirty) goto out; @@ -346,7 +346,7 @@ out: regmap_async_complete(map); - trace_regcache_sync(map->dev, name, "stop"); + trace_regcache_sync(map, name, "stop"); return ret; } @@ -381,7 +381,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min, name = map->cache_ops->name; dev_dbg(map->dev, "Syncing %s cache from %d-%d\n", name, min, max); - trace_regcache_sync(map->dev, name, "start region"); + trace_regcache_sync(map, name, "start region"); if (!map->cache_dirty) goto out; @@ -401,7 +401,7 @@ out: regmap_async_complete(map); - trace_regcache_sync(map->dev, name, "stop region"); + trace_regcache_sync(map, name, "stop region"); return ret; } @@ -428,7 +428,7 @@ int regcache_drop_region(struct regmap *map, unsigned int min, map->lock(map->lock_arg); - trace_regcache_drop_region(map->dev, min, max); + trace_regcache_drop_region(map, min, max); ret = map->cache_ops->drop(map, min, max); @@ -455,7 +455,7 @@ void regcache_cache_only(struct regmap *map, bool enable) map->lock(map->lock_arg); WARN_ON(map->cache_bypass && enable); map->cache_only = enable; - trace_regmap_cache_only(map->dev, enable); + trace_regmap_cache_only(map, enable); map->unlock(map->lock_arg); } EXPORT_SYMBOL_GPL(regcache_cache_only); @@ -493,7 +493,7 @@ void regcache_cache_bypass(struct regmap *map, bool enable) map->lock(map->lock_arg); WARN_ON(map->cache_only && enable); map->cache_bypass = enable; - trace_regmap_cache_bypass(map->dev, enable); + trace_regmap_cache_bypass(map, enable); map->unlock(map->lock_arg); } EXPORT_SYMBOL_GPL(regcache_cache_bypass); @@ -608,7 +608,8 @@ static int regcache_sync_block_single(struct regmap *map, void *block, for (i = start; i < end; i++) { regtmp = block_base + (i * map->reg_stride); - if (!regcache_reg_present(cache_present, i)) + if (!regcache_reg_present(cache_present, i) || + !regmap_writeable(map, regtmp)) continue; val = regcache_get_val(map, block, i); @@ -677,7 +678,8 @@ static int regcache_sync_block_raw(struct regmap *map, void *block, for (i = start; i < end; i++) { regtmp = block_base + (i * map->reg_stride); - if (!regcache_reg_present(cache_present, i)) { + if (!regcache_reg_present(cache_present, i) || + !regmap_writeable(map, regtmp)) { ret = regcache_sync_block_raw_flush(map, &data, base, regtmp); if (ret != 0) diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 6299a50..a6c3f75 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -499,7 +499,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, goto err_alloc; } - ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags, + ret = request_threaded_irq(irq, NULL, regmap_irq_thread, + irq_flags | IRQF_ONESHOT, chip->name, d); if (ret != 0) { dev_err(map->dev, "Failed to request IRQ %d for %s: %d\n", diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index f99b098..6273ff0 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -20,7 +20,7 @@ #include <linux/sched.h> #define CREATE_TRACE_POINTS -#include <trace/events/regmap.h> +#include "trace.h" #include "internal.h" @@ -1281,7 +1281,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, if (map->async && map->bus->async_write) { struct regmap_async *async; - trace_regmap_async_write_start(map->dev, reg, val_len); + trace_regmap_async_write_start(map, reg, val_len); spin_lock_irqsave(&map->async_lock, flags); async = list_first_entry_or_null(&map->async_free, @@ -1339,8 +1339,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, return ret; } - trace_regmap_hw_write_start(map->dev, reg, - val_len / map->format.val_bytes); + trace_regmap_hw_write_start(map, reg, val_len / map->format.val_bytes); /* If we're doing a single register write we can probably just * send the work_buf directly, otherwise try to do a gather @@ -1372,8 +1371,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, kfree(buf); } - trace_regmap_hw_write_done(map->dev, reg, - val_len / map->format.val_bytes); + trace_regmap_hw_write_done(map, reg, val_len / map->format.val_bytes); return ret; } @@ -1407,12 +1405,12 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg, map->format.format_write(map, reg, val); - trace_regmap_hw_write_start(map->dev, reg, 1); + trace_regmap_hw_write_start(map, reg, 1); ret = map->bus->write(map->bus_context, map->work_buf, map->format.buf_size); - trace_regmap_hw_write_done(map->dev, reg, 1); + trace_regmap_hw_write_done(map, reg, 1); return ret; } @@ -1470,7 +1468,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, dev_info(map->dev, "%x <= %x\n", reg, val); #endif - trace_regmap_reg_write(map->dev, reg, val); + trace_regmap_reg_write(map, reg, val); return map->reg_write(context, reg, val); } @@ -1773,7 +1771,7 @@ static int _regmap_raw_multi_reg_write(struct regmap *map, for (i = 0; i < num_regs; i++) { int reg = regs[i].reg; int val = regs[i].def; - trace_regmap_hw_write_start(map->dev, reg, 1); + trace_regmap_hw_write_start(map, reg, 1); map->format.format_reg(u8, reg, map->reg_shift); u8 += reg_bytes + pad_bytes; map->format.format_val(u8, val, 0); @@ -1788,7 +1786,7 @@ static int _regmap_raw_multi_reg_write(struct regmap *map, for (i = 0; i < num_regs; i++) { int reg = regs[i].reg; - trace_regmap_hw_write_done(map->dev, reg, 1); + trace_regmap_hw_write_done(map, reg, 1); } return ret; } @@ -2059,15 +2057,13 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, */ u8[0] |= map->read_flag_mask; - trace_regmap_hw_read_start(map->dev, reg, - val_len / map->format.val_bytes); + trace_regmap_hw_read_start(map, reg, val_len / map->format.val_bytes); ret = map->bus->read(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes, val, val_len); - trace_regmap_hw_read_done(map->dev, reg, - val_len / map->format.val_bytes); + trace_regmap_hw_read_done(map, reg, val_len / map->format.val_bytes); return ret; } @@ -2123,7 +2119,7 @@ static int _regmap_read(struct regmap *map, unsigned int reg, dev_info(map->dev, "%x => %x\n", reg, *val); #endif - trace_regmap_reg_read(map->dev, reg, *val); + trace_regmap_reg_read(map, reg, *val); if (!map->cache_bypass) regcache_write(map, reg, *val); @@ -2480,7 +2476,7 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret) struct regmap *map = async->map; bool wake; - trace_regmap_async_io_complete(map->dev); + trace_regmap_async_io_complete(map); spin_lock(&map->async_lock); list_move(&async->list, &map->async_free); @@ -2525,7 +2521,7 @@ int regmap_async_complete(struct regmap *map) if (!map->bus || !map->bus->async_write) return 0; - trace_regmap_async_complete_start(map->dev); + trace_regmap_async_complete_start(map); wait_event(map->async_waitq, regmap_async_is_done(map)); @@ -2534,7 +2530,7 @@ int regmap_async_complete(struct regmap *map) map->async_ret = 0; spin_unlock_irqrestore(&map->async_lock, flags); - trace_regmap_async_complete_done(map->dev); + trace_regmap_async_complete_done(map); return ret; } diff --git a/drivers/base/regmap/trace.h b/drivers/base/regmap/trace.h new file mode 100644 index 0000000..64586a1 --- /dev/null +++ b/drivers/base/regmap/trace.h @@ -0,0 +1,257 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM regmap + +#if !defined(_TRACE_REGMAP_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_REGMAP_H + +#include <linux/ktime.h> +#include <linux/tracepoint.h> + +#include "internal.h" + +/* + * Log register events + */ +DECLARE_EVENT_CLASS(regmap_reg, + + TP_PROTO(struct regmap *map, unsigned int reg, + unsigned int val), + + TP_ARGS(map, reg, val), + + TP_STRUCT__entry( + __string( name, regmap_name(map) ) + __field( unsigned int, reg ) + __field( unsigned int, val ) + ), + + TP_fast_assign( + __assign_str(name, regmap_name(map)); + __entry->reg = reg; + __entry->val = val; + ), + + TP_printk("%s reg=%x val=%x", __get_str(name), + (unsigned int)__entry->reg, + (unsigned int)__entry->val) +); + +DEFINE_EVENT(regmap_reg, regmap_reg_write, + + TP_PROTO(struct regmap *map, unsigned int reg, + unsigned int val), + + TP_ARGS(map, reg, val) + +); + +DEFINE_EVENT(regmap_reg, regmap_reg_read, + + TP_PROTO(struct regmap *map, unsigned int reg, + unsigned int val), + + TP_ARGS(map, reg, val) + +); + +DEFINE_EVENT(regmap_reg, regmap_reg_read_cache, + + TP_PROTO(struct regmap *map, unsigned int reg, + unsigned int val), + + TP_ARGS(map, reg, val) + +); + +DECLARE_EVENT_CLASS(regmap_block, + + TP_PROTO(struct regmap *map, unsigned int reg, int count), + + TP_ARGS(map, reg, count), + + TP_STRUCT__entry( + __string( name, regmap_name(map) ) + __field( unsigned int, reg ) + __field( int, count ) + ), + + TP_fast_assign( + __assign_str(name, regmap_name(map)); + __entry->reg = reg; + __entry->count = count; + ), + + TP_printk("%s reg=%x count=%d", __get_str(name), + (unsigned int)__entry->reg, + (int)__entry->count) +); + +DEFINE_EVENT(regmap_block, regmap_hw_read_start, + + TP_PROTO(struct regmap *map, unsigned int reg, int count), + + TP_ARGS(map, reg, count) +); + +DEFINE_EVENT(regmap_block, regmap_hw_read_done, + + TP_PROTO(struct regmap *map, unsigned int reg, int count), + + TP_ARGS(map, reg, count) +); + +DEFINE_EVENT(regmap_block, regmap_hw_write_start, + + TP_PROTO(struct regmap *map, unsigned int reg, int count), + + TP_ARGS(map, reg, count) +); + +DEFINE_EVENT(regmap_block, regmap_hw_write_done, + + TP_PROTO(struct regmap *map, unsigned int reg, int count), + + TP_ARGS(map, reg, count) +); + +TRACE_EVENT(regcache_sync, + + TP_PROTO(struct regmap *map, const char *type, + const char *status), + + TP_ARGS(map, type, status), + + TP_STRUCT__entry( + __string( name, regmap_name(map) ) + __string( status, status ) + __string( type, type ) + __field( int, type ) + ), + + TP_fast_assign( + __assign_str(name, regmap_name(map)); + __assign_str(status, status); + __assign_str(type, type); + ), + + TP_printk("%s type=%s status=%s", __get_str(name), + __get_str(type), __get_str(status)) +); + +DECLARE_EVENT_CLASS(regmap_bool, + + TP_PROTO(struct regmap *map, bool flag), + + TP_ARGS(map, flag), + + TP_STRUCT__entry( + __string( name, regmap_name(map) ) + __field( int, flag ) + ), + + TP_fast_assign( + __assign_str(name, regmap_name(map)); + __entry->flag = flag; + ), + + TP_printk("%s flag=%d", __get_str(name), + (int)__entry->flag) +); + +DEFINE_EVENT(regmap_bool, regmap_cache_only, + + TP_PROTO(struct regmap *map, bool flag), + + TP_ARGS(map, flag) + +); + +DEFINE_EVENT(regmap_bool, regmap_cache_bypass, + + TP_PROTO(struct regmap *map, bool flag), + + TP_ARGS(map, flag) + +); + +DECLARE_EVENT_CLASS(regmap_async, + + TP_PROTO(struct regmap *map), + + TP_ARGS(map), + + TP_STRUCT__entry( + __string( name, regmap_name(map) ) + ), + + TP_fast_assign( + __assign_str(name, regmap_name(map)); + ), + + TP_printk("%s", __get_str(name)) +); + +DEFINE_EVENT(regmap_block, regmap_async_write_start, + + TP_PROTO(struct regmap *map, unsigned int reg, int count), + + TP_ARGS(map, reg, count) +); + +DEFINE_EVENT(regmap_async, regmap_async_io_complete, + + TP_PROTO(struct regmap *map), + + TP_ARGS(map) + +); + +DEFINE_EVENT(regmap_async, regmap_async_complete_start, + + TP_PROTO(struct regmap *map), + + TP_ARGS(map) + +); + +DEFINE_EVENT(regmap_async, regmap_async_complete_done, + + TP_PROTO(struct regmap *map), + + TP_ARGS(map) + +); + +TRACE_EVENT(regcache_drop_region, + + TP_PROTO(struct regmap *map, unsigned int from, + unsigned int to), + + TP_ARGS(map, from, to), + + TP_STRUCT__entry( + __string( name, regmap_name(map) ) + __field( unsigned int, from ) + __field( unsigned int, to ) + ), + + TP_fast_assign( + __assign_str(name, regmap_name(map)); + __entry->from = from; + __entry->to = to; + ), + + TP_printk("%s %u-%u", __get_str(name), (unsigned int)__entry->from, + (unsigned int)__entry->to) +); + +#endif /* _TRACE_REGMAP_H */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +/* This part must be outside protection */ +#include <trace/define_trace.h> diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 2b94403..f749df9 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -405,8 +405,8 @@ static int cpqarray_register_ctlr(int i, struct pci_dev *pdev) goto Enomem4; } hba[i]->access.set_intr_mask(hba[i], 0); - if (request_irq(hba[i]->intr, do_ida_intr, - IRQF_DISABLED|IRQF_SHARED, hba[i]->devname, hba[i])) + if (request_irq(hba[i]->intr, do_ida_intr, IRQF_SHARED, + hba[i]->devname, hba[i])) { printk(KERN_ERR "cpqarray: Unable to get irq %d for %s\n", hba[i]->intr, hba[i]->devname); diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 4bc2a5c..a98c41f 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -803,10 +803,6 @@ static int __init nbd_init(void) return -EINVAL; } - nbd_dev = kcalloc(nbds_max, sizeof(*nbd_dev), GFP_KERNEL); - if (!nbd_dev) - return -ENOMEM; - part_shift = 0; if (max_part > 0) { part_shift = fls(max_part); @@ -828,6 +824,10 @@ static int __init nbd_init(void) if (nbds_max > 1UL << (MINORBITS - part_shift)) return -EINVAL; + nbd_dev = kcalloc(nbds_max, sizeof(*nbd_dev), GFP_KERNEL); + if (!nbd_dev) + return -ENOMEM; + for (i = 0; i < nbds_max; i++) { struct gendisk *disk = alloc_disk(1 << part_shift); if (!disk) diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c index b64bccb..e23be20 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c @@ -482,6 +482,7 @@ static int nvme_error_status(u16 status) } } +#ifdef CONFIG_BLK_DEV_INTEGRITY static void nvme_dif_prep(u32 p, u32 v, struct t10_pi_tuple *pi) { if (be32_to_cpu(pi->ref_tag) == v) @@ -538,6 +539,58 @@ static void nvme_dif_remap(struct request *req, kunmap_atomic(pmap); } +static int nvme_noop_verify(struct blk_integrity_iter *iter) +{ + return 0; +} + +static int nvme_noop_generate(struct blk_integrity_iter *iter) +{ + return 0; +} + +struct blk_integrity nvme_meta_noop = { + .name = "NVME_META_NOOP", + .generate_fn = nvme_noop_generate, + .verify_fn = nvme_noop_verify, +}; + +static void nvme_init_integrity(struct nvme_ns *ns) +{ + struct blk_integrity integrity; + + switch (ns->pi_type) { + case NVME_NS_DPS_PI_TYPE3: + integrity = t10_pi_type3_crc; + break; + case NVME_NS_DPS_PI_TYPE1: + case NVME_NS_DPS_PI_TYPE2: + integrity = t10_pi_type1_crc; + break; + default: + integrity = nvme_meta_noop; + break; + } + integrity.tuple_size = ns->ms; + blk_integrity_register(ns->disk, &integrity); + blk_queue_max_integrity_segments(ns->queue, 1); +} +#else /* CONFIG_BLK_DEV_INTEGRITY */ +static void nvme_dif_remap(struct request *req, + void (*dif_swap)(u32 p, u32 v, struct t10_pi_tuple *pi)) +{ +} +static void nvme_dif_prep(u32 p, u32 v, struct t10_pi_tuple *pi) +{ +} +static void nvme_dif_complete(u32 p, u32 v, struct t10_pi_tuple *pi) +{ +} +static void nvme_init_integrity(struct nvme_ns *ns) +{ +} +#endif + static void req_completion(struct nvme_queue *nvmeq, void *ctx, struct nvme_completion *cqe) { @@ -1959,43 +2012,6 @@ static void nvme_config_discard(struct nvme_ns *ns) queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, ns->queue); } -static int nvme_noop_verify(struct blk_integrity_iter *iter) -{ - return 0; -} - -static int nvme_noop_generate(struct blk_integrity_iter *iter) -{ - return 0; -} - -struct blk_integrity nvme_meta_noop = { - .name = "NVME_META_NOOP", - .generate_fn = nvme_noop_generate, - .verify_fn = nvme_noop_verify, -}; - -static void nvme_init_integrity(struct nvme_ns *ns) -{ - struct blk_integrity integrity; - - switch (ns->pi_type) { - case NVME_NS_DPS_PI_TYPE3: - integrity = t10_pi_type3_crc; - break; - case NVME_NS_DPS_PI_TYPE1: - case NVME_NS_DPS_PI_TYPE2: - integrity = t10_pi_type1_crc; - break; - default: - integrity = nvme_meta_noop; - break; - } - integrity.tuple_size = ns->ms; - blk_integrity_register(ns->disk, &integrity); - blk_queue_max_integrity_segments(ns->queue, 1); -} - static int nvme_revalidate_disk(struct gendisk *disk) { struct nvme_ns *ns = disk->private_data; @@ -2036,7 +2052,8 @@ static int nvme_revalidate_disk(struct gendisk *disk) pi_type = ns->ms == sizeof(struct t10_pi_tuple) ? id->dps & NVME_NS_DPS_PI_MASK : 0; - if (disk->integrity && (ns->pi_type != pi_type || ns->ms != old_ms || + if (blk_get_integrity(disk) && (ns->pi_type != pi_type || + ns->ms != old_ms || bs != queue_logical_block_size(disk->queue) || (ns->ms && id->flbas & NVME_NS_FLBAS_META_EXT))) blk_integrity_unregister(disk); @@ -2044,11 +2061,11 @@ static int nvme_revalidate_disk(struct gendisk *disk) ns->pi_type = pi_type; blk_queue_logical_block_size(ns->queue, bs); - if (ns->ms && !disk->integrity && (disk->flags & GENHD_FL_UP) && + if (ns->ms && !blk_get_integrity(disk) && (disk->flags & GENHD_FL_UP) && !(id->flbas & NVME_NS_FLBAS_META_EXT)) nvme_init_integrity(ns); - if (id->ncap == 0 || (ns->ms && !disk->integrity)) + if (id->ncap == 0 || (ns->ms && !blk_get_integrity(disk))) set_capacity(disk, 0); else set_capacity(disk, le64_to_cpup(&id->nsze) << (ns->lba_shift - 9)); @@ -2652,7 +2669,7 @@ static void nvme_dev_remove(struct nvme_dev *dev) list_for_each_entry(ns, &dev->namespaces, list) { if (ns->disk->flags & GENHD_FL_UP) { - if (ns->disk->integrity) + if (blk_get_integrity(ns->disk)) blk_integrity_unregister(ns->disk); del_gendisk(ns->disk); } @@ -2986,6 +3003,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) } get_device(dev->device); + INIT_LIST_HEAD(&dev->node); INIT_WORK(&dev->probe_work, nvme_async_probe); schedule_work(&dev->probe_work); return 0; diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 8e233ed..871bd35 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -528,7 +528,7 @@ out_cleanup: static inline void update_used_max(struct zram *zram, const unsigned long pages) { - int old_max, cur_max; + unsigned long old_max, cur_max; old_max = atomic_long_read(&zram->stats.max_used_pages); diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index b876888..8bfc4c2 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -272,6 +272,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL }, /* Intel Bluetooth devices */ + { USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR }, { USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL }, { USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL }, { USB_DEVICE(0x8087, 0x0a2b), .driver_info = BTUSB_INTEL_NEW }, diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 029bc73..11f7982 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -284,7 +284,7 @@ static int omap_l3_probe(struct platform_device *pdev) */ l3->debug_irq = platform_get_irq(pdev, 0); ret = devm_request_irq(l3->dev, l3->debug_irq, l3_interrupt_handler, - IRQF_DISABLED, "l3-dbg-irq", l3); + 0x0, "l3-dbg-irq", l3); if (ret) { dev_err(l3->dev, "request_irq failed for %d\n", l3->debug_irq); @@ -293,7 +293,7 @@ static int omap_l3_probe(struct platform_device *pdev) l3->app_irq = platform_get_irq(pdev, 1); ret = devm_request_irq(l3->dev, l3->app_irq, l3_interrupt_handler, - IRQF_DISABLED, "l3-app-irq", l3); + 0x0, "l3-app-irq", l3); if (ret) dev_err(l3->dev, "request_irq failed for %d\n", l3->app_irq); diff --git a/drivers/bus/omap_l3_smx.c b/drivers/bus/omap_l3_smx.c index 597fdae..360a5c0 100644 --- a/drivers/bus/omap_l3_smx.c +++ b/drivers/bus/omap_l3_smx.c @@ -251,18 +251,16 @@ static int omap3_l3_probe(struct platform_device *pdev) } l3->debug_irq = platform_get_irq(pdev, 0); - ret = request_irq(l3->debug_irq, omap3_l3_app_irq, - IRQF_DISABLED | IRQF_TRIGGER_RISING, - "l3-debug-irq", l3); + ret = request_irq(l3->debug_irq, omap3_l3_app_irq, IRQF_TRIGGER_RISING, + "l3-debug-irq", l3); if (ret) { dev_err(&pdev->dev, "couldn't request debug irq\n"); goto err1; } l3->app_irq = platform_get_irq(pdev, 1); - ret = request_irq(l3->app_irq, omap3_l3_app_irq, - IRQF_DISABLED | IRQF_TRIGGER_RISING, - "l3-app-irq", l3); + ret = request_irq(l3->app_irq, omap3_l3_app_irq, IRQF_TRIGGER_RISING, + "l3-app-irq", l3); if (ret) { dev_err(&pdev->dev, "couldn't request app irq\n"); goto err2; diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c index 79524ed..8753b0f 100644 --- a/drivers/char/ipmi/ipmi_powernv.c +++ b/drivers/char/ipmi/ipmi_powernv.c @@ -125,6 +125,7 @@ static int ipmi_powernv_recv(struct ipmi_smi_powernv *smi) spin_lock_irqsave(&smi->msg_lock, flags); if (!smi->cur_msg) { + spin_unlock_irqrestore(&smi->msg_lock, flags); pr_warn("no current message?\n"); return 0; } diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index f6646ed..518585c 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -263,6 +263,11 @@ struct smi_info { bool supports_event_msg_buff; /* + * Can we clear the global enables receive irq bit? + */ + bool cannot_clear_recv_irq_bit; + + /* * Did we get an attention that we did not handle? */ bool got_attn; @@ -461,6 +466,9 @@ static void smi_mod_timer(struct smi_info *smi_info, unsigned long new_val) * allocate messages, we just leave them in the BMC and run the system * polled until we can allocate some memory. Once we have some * memory, we will re-enable the interrupt. + * + * Note that we cannot just use disable_irq(), since the interrupt may + * be shared. */ static inline bool disable_si_irq(struct smi_info *smi_info) { @@ -549,20 +557,15 @@ static u8 current_global_enables(struct smi_info *smi_info, u8 base, if (smi_info->supports_event_msg_buff) enables |= IPMI_BMC_EVT_MSG_BUFF; - else - enables &= ~IPMI_BMC_EVT_MSG_BUFF; - if (smi_info->irq && !smi_info->interrupt_disabled) + if ((smi_info->irq && !smi_info->interrupt_disabled) || + smi_info->cannot_clear_recv_irq_bit) enables |= IPMI_BMC_RCV_MSG_INTR; - else - enables &= ~IPMI_BMC_RCV_MSG_INTR; if (smi_info->supports_event_msg_buff && smi_info->irq && !smi_info->interrupt_disabled) enables |= IPMI_BMC_EVT_MSG_INTR; - else - enables &= ~IPMI_BMC_EVT_MSG_INTR; *irq_on = enables & (IPMI_BMC_EVT_MSG_INTR | IPMI_BMC_RCV_MSG_INTR); @@ -2900,6 +2903,96 @@ static int try_get_dev_id(struct smi_info *smi_info) return rv; } +/* + * Some BMCs do not support clearing the receive irq bit in the global + * enables (even if they don't support interrupts on the BMC). Check + * for this and handle it properly. + */ +static void check_clr_rcv_irq(struct smi_info *smi_info) +{ + unsigned char msg[3]; + unsigned char *resp; + unsigned long resp_len; + int rv; + + resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); + if (!resp) { + printk(KERN_WARNING PFX "Out of memory allocating response for" + " global enables command, cannot check recv irq bit" + " handling.\n"); + return; + } + + msg[0] = IPMI_NETFN_APP_REQUEST << 2; + msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; + smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2); + + rv = wait_for_msg_done(smi_info); + if (rv) { + printk(KERN_WARNING PFX "Error getting response from get" + " global enables command, cannot check recv irq bit" + " handling.\n"); + goto out; + } + + resp_len = smi_info->handlers->get_result(smi_info->si_sm, + resp, IPMI_MAX_MSG_LENGTH); + + if (resp_len < 4 || + resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || + resp[1] != IPMI_GET_BMC_GLOBAL_ENABLES_CMD || + resp[2] != 0) { + printk(KERN_WARNING PFX "Invalid return from get global" + " enables command, cannot check recv irq bit" + " handling.\n"); + rv = -EINVAL; + goto out; + } + + if ((resp[3] & IPMI_BMC_RCV_MSG_INTR) == 0) + /* Already clear, should work ok. */ + goto out; + + msg[0] = IPMI_NETFN_APP_REQUEST << 2; + msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; + msg[2] = resp[3] & ~IPMI_BMC_RCV_MSG_INTR; + smi_info->handlers->start_transaction(smi_info->si_sm, msg, 3); + + rv = wait_for_msg_done(smi_info); + if (rv) { + printk(KERN_WARNING PFX "Error getting response from set" + " global enables command, cannot check recv irq bit" + " handling.\n"); + goto out; + } + + resp_len = smi_info->handlers->get_result(smi_info->si_sm, + resp, IPMI_MAX_MSG_LENGTH); + + if (resp_len < 3 || + resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || + resp[1] != IPMI_SET_BMC_GLOBAL_ENABLES_CMD) { + printk(KERN_WARNING PFX "Invalid return from get global" + " enables command, cannot check recv irq bit" + " handling.\n"); + rv = -EINVAL; + goto out; + } + + if (resp[2] != 0) { + /* + * An error when setting the event buffer bit means + * clearing the bit is not supported. + */ + printk(KERN_WARNING PFX "The BMC does not support clearing" + " the recv irq bit, compensating, but the BMC needs to" + " be fixed.\n"); + smi_info->cannot_clear_recv_irq_bit = true; + } + out: + kfree(resp); +} + static int try_enable_event_buffer(struct smi_info *smi_info) { unsigned char msg[3]; @@ -3395,6 +3488,8 @@ static int try_smi_init(struct smi_info *new_smi) goto out_err; } + check_clr_rcv_irq(new_smi); + setup_oem_data_handler(new_smi); setup_xaction_handlers(new_smi); diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index f6e378d..f40e3bd 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -468,11 +468,13 @@ static int ipmi_ssif_thread(void *data) int result; /* Wait for something to do */ - wait_for_completion(&ssif_info->wake_thread); - init_completion(&ssif_info->wake_thread); - + result = wait_for_completion_interruptible( + &ssif_info->wake_thread); if (ssif_info->stopping) break; + if (result == -ERESTARTSYS) + continue; + init_completion(&ssif_info->wake_thread); if (ssif_info->i2c_read_write == I2C_SMBUS_WRITE) { result = i2c_smbus_write_block_data( diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 1d278cc..e096e9c 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -140,24 +140,24 @@ static int tpm_dev_add_device(struct tpm_chip *chip) { int rc; - rc = device_add(&chip->dev); + rc = cdev_add(&chip->cdev, chip->dev.devt, 1); if (rc) { dev_err(&chip->dev, - "unable to device_register() %s, major %d, minor %d, err=%d\n", + "unable to cdev_add() %s, major %d, minor %d, err=%d\n", chip->devname, MAJOR(chip->dev.devt), MINOR(chip->dev.devt), rc); + device_unregister(&chip->dev); return rc; } - rc = cdev_add(&chip->cdev, chip->dev.devt, 1); + rc = device_add(&chip->dev); if (rc) { dev_err(&chip->dev, - "unable to cdev_add() %s, major %d, minor %d, err=%d\n", + "unable to device_register() %s, major %d, minor %d, err=%d\n", chip->devname, MAJOR(chip->dev.devt), MINOR(chip->dev.devt), rc); - device_unregister(&chip->dev); return rc; } @@ -174,27 +174,17 @@ static void tpm_dev_del_device(struct tpm_chip *chip) * tpm_chip_register() - create a character device for the TPM chip * @chip: TPM chip to use. * - * Creates a character device for the TPM chip and adds sysfs interfaces for - * the device, PPI and TCPA. As the last step this function adds the - * chip to the list of TPM chips available for use. + * Creates a character device for the TPM chip and adds sysfs attributes for + * the device. As the last step this function adds the chip to the list of TPM + * chips available for in-kernel use. * - * NOTE: This function should be only called after the chip initialization - * is complete. - * - * Called from tpm_<specific>.c probe function only for devices - * the driver has determined it should claim. Prior to calling - * this function the specific probe function has called pci_enable_device - * upon errant exit from this function specific probe function should call - * pci_disable_device + * This function should be only called after the chip initialization is + * complete. */ int tpm_chip_register(struct tpm_chip *chip) { int rc; - rc = tpm_dev_add_device(chip); - if (rc) - return rc; - /* Populate sysfs for TPM1 devices. */ if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { rc = tpm_sysfs_add_device(chip); @@ -208,6 +198,10 @@ int tpm_chip_register(struct tpm_chip *chip) chip->bios_dir = tpm_bios_log_setup(chip->devname); } + rc = tpm_dev_add_device(chip); + if (rc) + return rc; + /* Make the chip available. */ spin_lock(&driver_lock); list_add_rcu(&chip->list, &tpm_chip_list); diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index b1e53e3..42ffa5e7 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -124,7 +124,7 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) { struct ibmvtpm_dev *ibmvtpm; struct ibmvtpm_crq crq; - u64 *word = (u64 *) &crq; + __be64 *word = (__be64 *)&crq; int rc; ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip); @@ -145,11 +145,11 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) memcpy((void *)ibmvtpm->rtce_buf, (void *)buf, count); crq.valid = (u8)IBMVTPM_VALID_CMD; crq.msg = (u8)VTPM_TPM_COMMAND; - crq.len = (u16)count; - crq.data = ibmvtpm->rtce_dma_handle; + crq.len = cpu_to_be16(count); + crq.data = cpu_to_be32(ibmvtpm->rtce_dma_handle); - rc = ibmvtpm_send_crq(ibmvtpm->vdev, cpu_to_be64(word[0]), - cpu_to_be64(word[1])); + rc = ibmvtpm_send_crq(ibmvtpm->vdev, be64_to_cpu(word[0]), + be64_to_cpu(word[1])); if (rc != H_SUCCESS) { dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc); rc = 0; diff --git a/drivers/char/tpm/tpm_ibmvtpm.h b/drivers/char/tpm/tpm_ibmvtpm.h index f595f14..6af9289 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.h +++ b/drivers/char/tpm/tpm_ibmvtpm.h @@ -22,9 +22,9 @@ struct ibmvtpm_crq { u8 valid; u8 msg; - u16 len; - u32 data; - u64 reserved; + __be16 len; + __be32 data; + __be64 reserved; } __attribute__((packed, aligned(8))); struct ibmvtpm_crq_queue { diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index fae2dbb..72d7028 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -142,6 +142,7 @@ struct ports_device { * notification */ struct work_struct control_work; + struct work_struct config_work; struct list_head ports; @@ -1837,10 +1838,21 @@ static void config_intr(struct virtio_device *vdev) portdev = vdev->priv; + if (!use_multiport(portdev)) + schedule_work(&portdev->config_work); +} + +static void config_work_handler(struct work_struct *work) +{ + struct ports_device *portdev; + + portdev = container_of(work, struct ports_device, control_work); if (!use_multiport(portdev)) { + struct virtio_device *vdev; struct port *port; u16 rows, cols; + vdev = portdev->vdev; virtio_cread(vdev, struct virtio_console_config, cols, &cols); virtio_cread(vdev, struct virtio_console_config, rows, &rows); @@ -2040,12 +2052,14 @@ static int virtcons_probe(struct virtio_device *vdev) virtio_device_ready(portdev->vdev); + INIT_WORK(&portdev->config_work, &config_work_handler); + INIT_WORK(&portdev->control_work, &control_work_handler); + if (multiport) { unsigned int nr_added_bufs; spin_lock_init(&portdev->c_ivq_lock); spin_lock_init(&portdev->c_ovq_lock); - INIT_WORK(&portdev->control_work, &control_work_handler); nr_added_bufs = fill_queue(portdev->c_ivq, &portdev->c_ivq_lock); @@ -2113,6 +2127,8 @@ static void virtcons_remove(struct virtio_device *vdev) /* Finish up work that's lined up */ if (use_multiport(portdev)) cancel_work_sync(&portdev->control_work); + else + cancel_work_sync(&portdev->config_work); list_for_each_entry_safe(port, port2, &portdev->ports, list) unplug_port(port); @@ -2164,6 +2180,7 @@ static int virtcons_freeze(struct virtio_device *vdev) virtqueue_disable_cb(portdev->c_ivq); cancel_work_sync(&portdev->control_work); + cancel_work_sync(&portdev->config_work); /* * Once more: if control_work_handler() was running, it would * enable the cb as the last step. diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index f07c815..3f27d21 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -89,12 +89,29 @@ static int pmc_irq_set_type(struct irq_data *d, unsigned type) return 0; } +static void pmc_irq_suspend(struct irq_data *d) +{ + struct at91_pmc *pmc = irq_data_get_irq_chip_data(d); + + pmc->imr = pmc_read(pmc, AT91_PMC_IMR); + pmc_write(pmc, AT91_PMC_IDR, pmc->imr); +} + +static void pmc_irq_resume(struct irq_data *d) +{ + struct at91_pmc *pmc = irq_data_get_irq_chip_data(d); + + pmc_write(pmc, AT91_PMC_IER, pmc->imr); +} + static struct irq_chip pmc_irq = { .name = "PMC", .irq_disable = pmc_irq_mask, .irq_mask = pmc_irq_mask, .irq_unmask = pmc_irq_unmask, .irq_set_type = pmc_irq_set_type, + .irq_suspend = pmc_irq_suspend, + .irq_resume = pmc_irq_resume, }; static struct lock_class_key pmc_lock_class; @@ -224,7 +241,8 @@ static struct at91_pmc *__init at91_pmc_init(struct device_node *np, goto out_free_pmc; pmc_write(pmc, AT91_PMC_IDR, 0xffffffff); - if (request_irq(pmc->virq, pmc_irq_handler, IRQF_SHARED, "pmc", pmc)) + if (request_irq(pmc->virq, pmc_irq_handler, + IRQF_SHARED | IRQF_COND_SUSPEND, "pmc", pmc)) goto out_remove_irqdomain; return pmc; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 52d2041..69abb08 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -33,6 +33,7 @@ struct at91_pmc { spinlock_t lock; const struct at91_pmc_caps *caps; struct irq_domain *irqdomain; + u32 imr; }; static inline void pmc_lock(struct at91_pmc *pmc) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index db7f8bc..25006a8 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -144,12 +144,6 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, divider->flags); } -/* - * The reverse of DIV_ROUND_UP: The maximum number which - * divided by m is r - */ -#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1) - static bool _is_valid_table_div(const struct clk_div_table *table, unsigned int div) { @@ -225,19 +219,24 @@ static int _div_round_closest(const struct clk_div_table *table, unsigned long parent_rate, unsigned long rate, unsigned long flags) { - int up, down, div; + int up, down; + unsigned long up_rate, down_rate; - up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate); + up = DIV_ROUND_UP(parent_rate, rate); + down = parent_rate / rate; if (flags & CLK_DIVIDER_POWER_OF_TWO) { - up = __roundup_pow_of_two(div); - down = __rounddown_pow_of_two(div); + up = __roundup_pow_of_two(up); + down = __rounddown_pow_of_two(down); } else if (table) { - up = _round_up_table(table, div); - down = _round_down_table(table, div); + up = _round_up_table(table, up); + down = _round_down_table(table, down); } - return (up - div) <= (div - down) ? up : down; + up_rate = DIV_ROUND_UP(parent_rate, up); + down_rate = DIV_ROUND_UP(parent_rate, down); + + return (rate - up_rate) <= (down_rate - rate) ? up : down; } static int _div_round(const struct clk_div_table *table, @@ -313,7 +312,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, return i; } parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), - MULT_ROUND_UP(rate, i)); + rate * i); now = DIV_ROUND_UP(parent_rate, i); if (_is_best_div(rate, now, best, flags)) { bestdiv = i; @@ -353,7 +352,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, bestdiv = readl(divider->reg) >> divider->shift; bestdiv &= div_mask(divider->width); bestdiv = _get_div(divider->table, bestdiv, divider->flags); - return bestdiv; + return DIV_ROUND_UP(*prate, bestdiv); } return divider_round_rate(hw, rate, prate, divider->table, diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index eb01529..237f23f 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1350,7 +1350,6 @@ static unsigned long clk_core_get_rate(struct clk_core *clk) return rate; } -EXPORT_SYMBOL_GPL(clk_core_get_rate); /** * clk_get_rate - return the rate of clk @@ -2171,6 +2170,32 @@ int clk_get_phase(struct clk *clk) } /** + * clk_is_match - check if two clk's point to the same hardware clock + * @p: clk compared against q + * @q: clk compared against p + * + * Returns true if the two struct clk pointers both point to the same hardware + * clock node. Put differently, returns true if struct clk *p and struct clk *q + * share the same struct clk_core object. + * + * Returns false otherwise. Note that two NULL clks are treated as matching. + */ +bool clk_is_match(const struct clk *p, const struct clk *q) +{ + /* trivial case: identical struct clk's or both NULL */ + if (p == q) + return true; + + /* true if clk->core pointers match. Avoid derefing garbage */ + if (!IS_ERR_OR_NULL(p) && !IS_ERR_OR_NULL(q)) + if (p->core == q->core) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(clk_is_match); + +/** * __clk_init - initialize the data structures in a struct clk * @dev: device initializing this clk, placeholder for now * @clk: clk being initialized diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c index b0b562b..e60feff 100644 --- a/drivers/clk/qcom/gcc-msm8960.c +++ b/drivers/clk/qcom/gcc-msm8960.c @@ -48,6 +48,17 @@ static struct clk_pll pll3 = { }, }; +static struct clk_regmap pll4_vote = { + .enable_reg = 0x34c0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pll4_vote", + .parent_names = (const char *[]){ "pll4" }, + .num_parents = 1, + .ops = &clk_pll_vote_ops, + }, +}; + static struct clk_pll pll8 = { .l_reg = 0x3144, .m_reg = 0x3148, @@ -3023,6 +3034,7 @@ static struct clk_branch rpm_msg_ram_h_clk = { static struct clk_regmap *gcc_msm8960_clks[] = { [PLL3] = &pll3.clkr, + [PLL4_VOTE] = &pll4_vote, [PLL8] = &pll8.clkr, [PLL8_VOTE] = &pll8_vote, [PLL14] = &pll14.clkr, @@ -3247,6 +3259,7 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { static struct clk_regmap *gcc_apq8064_clks[] = { [PLL3] = &pll3.clkr, + [PLL4_VOTE] = &pll4_vote, [PLL8] = &pll8.clkr, [PLL8_VOTE] = &pll8_vote, [PLL14] = &pll14.clkr, diff --git a/drivers/clk/qcom/lcc-ipq806x.c b/drivers/clk/qcom/lcc-ipq806x.c index 121ffde..c9ff27b 100644 --- a/drivers/clk/qcom/lcc-ipq806x.c +++ b/drivers/clk/qcom/lcc-ipq806x.c @@ -462,7 +462,6 @@ static struct platform_driver lcc_ipq806x_driver = { .remove = lcc_ipq806x_remove, .driver = { .name = "lcc-ipq806x", - .owner = THIS_MODULE, .of_match_table = lcc_ipq806x_match_table, }, }; diff --git a/drivers/clk/qcom/lcc-msm8960.c b/drivers/clk/qcom/lcc-msm8960.c index a75a408..e2c8632 100644 --- a/drivers/clk/qcom/lcc-msm8960.c +++ b/drivers/clk/qcom/lcc-msm8960.c @@ -417,8 +417,8 @@ static struct clk_rcg slimbus_src = { .mnctr_en_bit = 8, .mnctr_reset_bit = 7, .mnctr_mode_shift = 5, - .n_val_shift = 16, - .m_val_shift = 16, + .n_val_shift = 24, + .m_val_shift = 8, .width = 8, }, .p = { @@ -547,7 +547,7 @@ static int lcc_msm8960_probe(struct platform_device *pdev) return PTR_ERR(regmap); /* Use the correct frequency plan depending on speed of PLL4 */ - val = regmap_read(regmap, 0x4, &val); + regmap_read(regmap, 0x4, &val); if (val == 0x12) { slimbus_src.freq_tbl = clk_tbl_aif_osr_492; mi2s_osr_src.freq_tbl = clk_tbl_aif_osr_492; @@ -574,7 +574,6 @@ static struct platform_driver lcc_msm8960_driver = { .remove = lcc_msm8960_remove, .driver = { .name = "lcc-msm8960", - .owner = THIS_MODULE, .of_match_table = lcc_msm8960_match_table, }, }; diff --git a/drivers/clk/ti/fapll.c b/drivers/clk/ti/fapll.c index 6ef8963..d216406 100644 --- a/drivers/clk/ti/fapll.c +++ b/drivers/clk/ti/fapll.c @@ -84,7 +84,7 @@ static int ti_fapll_enable(struct clk_hw *hw) struct fapll_data *fd = to_fapll(hw); u32 v = readl_relaxed(fd->base); - v |= (1 << FAPLL_MAIN_PLLEN); + v |= FAPLL_MAIN_PLLEN; writel_relaxed(v, fd->base); return 0; @@ -95,7 +95,7 @@ static void ti_fapll_disable(struct clk_hw *hw) struct fapll_data *fd = to_fapll(hw); u32 v = readl_relaxed(fd->base); - v &= ~(1 << FAPLL_MAIN_PLLEN); + v &= ~FAPLL_MAIN_PLLEN; writel_relaxed(v, fd->base); } @@ -104,7 +104,7 @@ static int ti_fapll_is_enabled(struct clk_hw *hw) struct fapll_data *fd = to_fapll(hw); u32 v = readl_relaxed(fd->base); - return v & (1 << FAPLL_MAIN_PLLEN); + return v & FAPLL_MAIN_PLLEN; } static unsigned long ti_fapll_recalc_rate(struct clk_hw *hw, diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 1c2506f..a0b036c 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -63,6 +63,11 @@ config VT8500_TIMER config CADENCE_TTC_TIMER bool +config ASM9260_TIMER + bool + select CLKSRC_MMIO + select CLKSRC_OF + config CLKSRC_NOMADIK_MTU bool depends on (ARCH_NOMADIK || ARCH_U8500) @@ -187,6 +192,7 @@ config SYS_SUPPORTS_EM_STI config SH_TIMER_CMT bool "Renesas CMT timer driver" if COMPILE_TEST depends on GENERIC_CLOCKEVENTS + depends on HAS_IOMEM default SYS_SUPPORTS_SH_CMT help This enables build of a clocksource and clockevent driver for @@ -196,6 +202,7 @@ config SH_TIMER_CMT config SH_TIMER_MTU2 bool "Renesas MTU2 timer driver" if COMPILE_TEST depends on GENERIC_CLOCKEVENTS + depends on HAS_IOMEM default SYS_SUPPORTS_SH_MTU2 help This enables build of a clockevent driver for the Multi-Function @@ -205,6 +212,7 @@ config SH_TIMER_MTU2 config SH_TIMER_TMU bool "Renesas TMU timer driver" if COMPILE_TEST depends on GENERIC_CLOCKEVENTS + depends on HAS_IOMEM default SYS_SUPPORTS_SH_TMU help This enables build of a clocksource and clockevent driver for @@ -245,15 +253,4 @@ config CLKSRC_PXA help This enables OST0 support available on PXA and SA-11x0 platforms. - -config ASM9260_TIMER - bool "Alphascale ASM9260 timer driver" - depends on GENERIC_CLOCKEVENTS - select CLKSRC_MMIO - select CLKSRC_OF - default y if MACH_ASM9260 - help - This enables build of a clocksource and clockevent driver for - the 32-bit System Timer hardware available on a Alphascale ASM9260. - endmenu diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index a3025e7..2664696 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -661,17 +661,17 @@ static const struct of_device_id arch_timer_mem_of_match[] __initconst = { }; static bool __init -arch_timer_probed(int type, const struct of_device_id *matches) +arch_timer_needs_probing(int type, const struct of_device_id *matches) { struct device_node *dn; - bool probed = true; + bool needs_probing = false; dn = of_find_matching_node(NULL, matches); if (dn && of_device_is_available(dn) && !(arch_timers_present & type)) - probed = false; + needs_probing = true; of_node_put(dn); - return probed; + return needs_probing; } static void __init arch_timer_common_init(void) @@ -680,9 +680,9 @@ static void __init arch_timer_common_init(void) /* Wait until both nodes are probed if we have two timers */ if ((arch_timers_present & mask) != mask) { - if (!arch_timer_probed(ARCH_MEM_TIMER, arch_timer_mem_of_match)) + if (arch_timer_needs_probing(ARCH_MEM_TIMER, arch_timer_mem_of_match)) return; - if (!arch_timer_probed(ARCH_CP15_TIMER, arch_timer_of_match)) + if (arch_timer_needs_probing(ARCH_CP15_TIMER, arch_timer_of_match)) return; } diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c index d305fb0..a19a3f6 100644 --- a/drivers/clocksource/dw_apb_timer_of.c +++ b/drivers/clocksource/dw_apb_timer_of.c @@ -108,7 +108,7 @@ static void __init add_clocksource(struct device_node *source_timer) static u64 notrace read_sched_clock(void) { - return ~__raw_readl(sched_io_base); + return ~readl_relaxed(sched_io_base); } static const struct of_device_id sptimer_ids[] __initconst = { diff --git a/drivers/clocksource/em_sti.c b/drivers/clocksource/em_sti.c index d0a7bd6..dc3c6ee 100644 --- a/drivers/clocksource/em_sti.c +++ b/drivers/clocksource/em_sti.c @@ -210,7 +210,7 @@ static int em_sti_clocksource_enable(struct clocksource *cs) ret = em_sti_start(p, USER_CLOCKSOURCE); if (!ret) - __clocksource_updatefreq_hz(cs, p->rate); + __clocksource_update_freq_hz(cs, p->rate); return ret; } diff --git a/drivers/clocksource/mtk_timer.c b/drivers/clocksource/mtk_timer.c index 32a3d25..68ab423 100644 --- a/drivers/clocksource/mtk_timer.c +++ b/drivers/clocksource/mtk_timer.c @@ -224,6 +224,8 @@ static void __init mtk_timer_init(struct device_node *node) } rate = clk_get_rate(clk); + mtk_timer_global_reset(evt); + if (request_irq(evt->dev.irq, mtk_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, "mtk_timer", evt)) { pr_warn("failed to setup irq %d\n", evt->dev.irq); @@ -232,8 +234,6 @@ static void __init mtk_timer_init(struct device_node *node) evt->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); - mtk_timer_global_reset(evt); - /* Configure clock source */ mtk_timer_setup(evt, GPT_CLK_SRC, TIMER_CTRL_OP_FREERUN); clocksource_mmio_init(evt->gpt_base + TIMER_CNT_REG(GPT_CLK_SRC), @@ -241,10 +241,11 @@ static void __init mtk_timer_init(struct device_node *node) /* Configure clock event */ mtk_timer_setup(evt, GPT_CLK_EVT, TIMER_CTRL_OP_REPEAT); - mtk_timer_enable_irq(evt, GPT_CLK_EVT); - clockevents_config_and_register(&evt->dev, rate, 0x3, 0xffffffff); + + mtk_timer_enable_irq(evt, GPT_CLK_EVT); + return; err_clk_disable: diff --git a/drivers/clocksource/pxa_timer.c b/drivers/clocksource/pxa_timer.c index 941f3f3..d9438af 100644 --- a/drivers/clocksource/pxa_timer.c +++ b/drivers/clocksource/pxa_timer.c @@ -163,7 +163,7 @@ static struct irqaction pxa_ost0_irq = { .dev_id = &ckevt_pxa_osmr0, }; -static void pxa_timer_common_init(int irq, unsigned long clock_tick_rate) +static void __init pxa_timer_common_init(int irq, unsigned long clock_tick_rate) { timer_writel(0, OIER); timer_writel(OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3, OSSR); diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 2bd13b5..b8ff3c6 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -641,7 +641,7 @@ static int sh_cmt_clocksource_enable(struct clocksource *cs) ret = sh_cmt_start(ch, FLAG_CLOCKSOURCE); if (!ret) { - __clocksource_updatefreq_hz(cs, ch->rate); + __clocksource_update_freq_hz(cs, ch->rate); ch->cs_enabled = true; } return ret; diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index f150ca82..b6b8fa3 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -272,7 +272,7 @@ static int sh_tmu_clocksource_enable(struct clocksource *cs) ret = sh_tmu_enable(ch); if (!ret) { - __clocksource_updatefreq_hz(cs, ch->rate); + __clocksource_update_freq_hz(cs, ch->rate); ch->cs_enabled = true; } diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c index f4a9c00..1928a89 100644 --- a/drivers/clocksource/sun4i_timer.c +++ b/drivers/clocksource/sun4i_timer.c @@ -170,7 +170,15 @@ static void __init sun4i_timer_init(struct device_node *node) TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M), timer_base + TIMER_CTL_REG(1)); - sched_clock_register(sun4i_timer_sched_read, 32, rate); + /* + * sched_clock_register does not have priorities, and on sun6i and + * later there is a better sched_clock registered by arm_arch_timer.c + */ + if (of_machine_is_compatible("allwinner,sun4i-a10") || + of_machine_is_compatible("allwinner,sun5i-a13") || + of_machine_is_compatible("allwinner,sun5i-a10s")) + sched_clock_register(sun4i_timer_sched_read, 32, rate); + clocksource_mmio_init(timer_base + TIMER_CNTVAL_REG(1), node->name, rate, 350, 32, clocksource_mmio_readl_down); diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c index d2616ef..5a112d7 100644 --- a/drivers/clocksource/tegra20_timer.c +++ b/drivers/clocksource/tegra20_timer.c @@ -51,15 +51,15 @@ static void __iomem *timer_reg_base; static void __iomem *rtc_base; -static struct timespec persistent_ts; +static struct timespec64 persistent_ts; static u64 persistent_ms, last_persistent_ms; static struct delay_timer tegra_delay_timer; #define timer_writel(value, reg) \ - __raw_writel(value, timer_reg_base + (reg)) + writel_relaxed(value, timer_reg_base + (reg)) #define timer_readl(reg) \ - __raw_readl(timer_reg_base + (reg)) + readl_relaxed(timer_reg_base + (reg)) static int tegra_timer_set_next_event(unsigned long cycles, struct clock_event_device *evt) @@ -120,26 +120,25 @@ static u64 tegra_rtc_read_ms(void) } /* - * tegra_read_persistent_clock - Return time from a persistent clock. + * tegra_read_persistent_clock64 - Return time from a persistent clock. * * Reads the time from a source which isn't disabled during PM, the * 32k sync timer. Convert the cycles elapsed since last read into - * nsecs and adds to a monotonically increasing timespec. + * nsecs and adds to a monotonically increasing timespec64. * Care must be taken that this funciton is not called while the * tegra_rtc driver could be executing to avoid race conditions * on the RTC shadow register */ -static void tegra_read_persistent_clock(struct timespec *ts) +static void tegra_read_persistent_clock64(struct timespec64 *ts) { u64 delta; - struct timespec *tsp = &persistent_ts; last_persistent_ms = persistent_ms; persistent_ms = tegra_rtc_read_ms(); delta = persistent_ms - last_persistent_ms; - timespec_add_ns(tsp, delta * NSEC_PER_MSEC); - *ts = *tsp; + timespec64_add_ns(&persistent_ts, delta * NSEC_PER_MSEC); + *ts = persistent_ts; } static unsigned long tegra_delay_timer_read_counter_long(void) @@ -252,7 +251,7 @@ static void __init tegra20_init_rtc(struct device_node *np) else clk_prepare_enable(clk); - register_persistent_clock(NULL, tegra_read_persistent_clock); + register_persistent_clock(NULL, tegra_read_persistent_clock64); } CLOCKSOURCE_OF_DECLARE(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc); diff --git a/drivers/clocksource/time-efm32.c b/drivers/clocksource/time-efm32.c index bba62f9..5b6e3d5 100644 --- a/drivers/clocksource/time-efm32.c +++ b/drivers/clocksource/time-efm32.c @@ -111,7 +111,7 @@ static irqreturn_t efm32_clock_event_handler(int irq, void *dev_id) static struct efm32_clock_event_ddata clock_event_ddata = { .evtdev = { .name = "efm32 clockevent", - .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_MODE_PERIODIC, + .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, .set_mode = efm32_clock_event_set_mode, .set_next_event = efm32_clock_event_set_next_event, .rating = 200, @@ -225,12 +225,12 @@ static int __init efm32_clockevent_init(struct device_node *np) clock_event_ddata.base = base; clock_event_ddata.periodic_top = DIV_ROUND_CLOSEST(rate, 1024 * HZ); - setup_irq(irq, &efm32_clock_event_irq); - clockevents_config_and_register(&clock_event_ddata.evtdev, DIV_ROUND_CLOSEST(rate, 1024), 0xf, 0xffff); + setup_irq(irq, &efm32_clock_event_irq); + return 0; err_get_irq: diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c index b5b4d45..c0304ff 100644 --- a/drivers/clocksource/timer-atmel-pit.c +++ b/drivers/clocksource/timer-atmel-pit.c @@ -61,12 +61,12 @@ static inline struct pit_data *clkevt_to_pit_data(struct clock_event_device *clk static inline unsigned int pit_read(void __iomem *base, unsigned int reg_offset) { - return __raw_readl(base + reg_offset); + return readl_relaxed(base + reg_offset); } static inline void pit_write(void __iomem *base, unsigned int reg_offset, unsigned long value) { - __raw_writel(value, base + reg_offset); + writel_relaxed(value, base + reg_offset); } /* diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c index 0226844..28aa4b7 100644 --- a/drivers/clocksource/timer-sun5i.c +++ b/drivers/clocksource/timer-sun5i.c @@ -17,7 +17,7 @@ #include <linux/irq.h> #include <linux/irqreturn.h> #include <linux/reset.h> -#include <linux/sched_clock.h> +#include <linux/slab.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> @@ -37,8 +37,31 @@ #define TIMER_SYNC_TICKS 3 -static void __iomem *timer_base; -static u32 ticks_per_jiffy; +struct sun5i_timer { + void __iomem *base; + struct clk *clk; + struct notifier_block clk_rate_cb; + u32 ticks_per_jiffy; +}; + +#define to_sun5i_timer(x) \ + container_of(x, struct sun5i_timer, clk_rate_cb) + +struct sun5i_timer_clksrc { + struct sun5i_timer timer; + struct clocksource clksrc; +}; + +#define to_sun5i_timer_clksrc(x) \ + container_of(x, struct sun5i_timer_clksrc, clksrc) + +struct sun5i_timer_clkevt { + struct sun5i_timer timer; + struct clock_event_device clkevt; +}; + +#define to_sun5i_timer_clkevt(x) \ + container_of(x, struct sun5i_timer_clkevt, clkevt) /* * When we disable a timer, we need to wait at least for 2 cycles of @@ -46,30 +69,30 @@ static u32 ticks_per_jiffy; * that is already setup and runs at the same frequency than the other * timers, and we never will be disabled. */ -static void sun5i_clkevt_sync(void) +static void sun5i_clkevt_sync(struct sun5i_timer_clkevt *ce) { - u32 old = readl(timer_base + TIMER_CNTVAL_LO_REG(1)); + u32 old = readl(ce->timer.base + TIMER_CNTVAL_LO_REG(1)); - while ((old - readl(timer_base + TIMER_CNTVAL_LO_REG(1))) < TIMER_SYNC_TICKS) + while ((old - readl(ce->timer.base + TIMER_CNTVAL_LO_REG(1))) < TIMER_SYNC_TICKS) cpu_relax(); } -static void sun5i_clkevt_time_stop(u8 timer) +static void sun5i_clkevt_time_stop(struct sun5i_timer_clkevt *ce, u8 timer) { - u32 val = readl(timer_base + TIMER_CTL_REG(timer)); - writel(val & ~TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(timer)); + u32 val = readl(ce->timer.base + TIMER_CTL_REG(timer)); + writel(val & ~TIMER_CTL_ENABLE, ce->timer.base + TIMER_CTL_REG(timer)); - sun5i_clkevt_sync(); + sun5i_clkevt_sync(ce); } -static void sun5i_clkevt_time_setup(u8 timer, u32 delay) +static void sun5i_clkevt_time_setup(struct sun5i_timer_clkevt *ce, u8 timer, u32 delay) { - writel(delay, timer_base + TIMER_INTVAL_LO_REG(timer)); + writel(delay, ce->timer.base + TIMER_INTVAL_LO_REG(timer)); } -static void sun5i_clkevt_time_start(u8 timer, bool periodic) +static void sun5i_clkevt_time_start(struct sun5i_timer_clkevt *ce, u8 timer, bool periodic) { - u32 val = readl(timer_base + TIMER_CTL_REG(timer)); + u32 val = readl(ce->timer.base + TIMER_CTL_REG(timer)); if (periodic) val &= ~TIMER_CTL_ONESHOT; @@ -77,80 +100,230 @@ static void sun5i_clkevt_time_start(u8 timer, bool periodic) val |= TIMER_CTL_ONESHOT; writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, - timer_base + TIMER_CTL_REG(timer)); + ce->timer.base + TIMER_CTL_REG(timer)); } static void sun5i_clkevt_mode(enum clock_event_mode mode, - struct clock_event_device *clk) + struct clock_event_device *clkevt) { + struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt); + switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - sun5i_clkevt_time_stop(0); - sun5i_clkevt_time_setup(0, ticks_per_jiffy); - sun5i_clkevt_time_start(0, true); + sun5i_clkevt_time_stop(ce, 0); + sun5i_clkevt_time_setup(ce, 0, ce->timer.ticks_per_jiffy); + sun5i_clkevt_time_start(ce, 0, true); break; case CLOCK_EVT_MODE_ONESHOT: - sun5i_clkevt_time_stop(0); - sun5i_clkevt_time_start(0, false); + sun5i_clkevt_time_stop(ce, 0); + sun5i_clkevt_time_start(ce, 0, false); break; case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: default: - sun5i_clkevt_time_stop(0); + sun5i_clkevt_time_stop(ce, 0); break; } } static int sun5i_clkevt_next_event(unsigned long evt, - struct clock_event_device *unused) + struct clock_event_device *clkevt) { - sun5i_clkevt_time_stop(0); - sun5i_clkevt_time_setup(0, evt - TIMER_SYNC_TICKS); - sun5i_clkevt_time_start(0, false); + struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt); + + sun5i_clkevt_time_stop(ce, 0); + sun5i_clkevt_time_setup(ce, 0, evt - TIMER_SYNC_TICKS); + sun5i_clkevt_time_start(ce, 0, false); return 0; } -static struct clock_event_device sun5i_clockevent = { - .name = "sun5i_tick", - .rating = 340, - .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, - .set_mode = sun5i_clkevt_mode, - .set_next_event = sun5i_clkevt_next_event, -}; - - static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id) { - struct clock_event_device *evt = (struct clock_event_device *)dev_id; + struct sun5i_timer_clkevt *ce = (struct sun5i_timer_clkevt *)dev_id; - writel(0x1, timer_base + TIMER_IRQ_ST_REG); - evt->event_handler(evt); + writel(0x1, ce->timer.base + TIMER_IRQ_ST_REG); + ce->clkevt.event_handler(&ce->clkevt); return IRQ_HANDLED; } -static struct irqaction sun5i_timer_irq = { - .name = "sun5i_timer0", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = sun5i_timer_interrupt, - .dev_id = &sun5i_clockevent, -}; +static cycle_t sun5i_clksrc_read(struct clocksource *clksrc) +{ + struct sun5i_timer_clksrc *cs = to_sun5i_timer_clksrc(clksrc); + + return ~readl(cs->timer.base + TIMER_CNTVAL_LO_REG(1)); +} -static u64 sun5i_timer_sched_read(void) +static int sun5i_rate_cb_clksrc(struct notifier_block *nb, + unsigned long event, void *data) { - return ~readl(timer_base + TIMER_CNTVAL_LO_REG(1)); + struct clk_notifier_data *ndata = data; + struct sun5i_timer *timer = to_sun5i_timer(nb); + struct sun5i_timer_clksrc *cs = container_of(timer, struct sun5i_timer_clksrc, timer); + + switch (event) { + case PRE_RATE_CHANGE: + clocksource_unregister(&cs->clksrc); + break; + + case POST_RATE_CHANGE: + clocksource_register_hz(&cs->clksrc, ndata->new_rate); + break; + + default: + break; + } + + return NOTIFY_DONE; +} + +static int __init sun5i_setup_clocksource(struct device_node *node, + void __iomem *base, + struct clk *clk, int irq) +{ + struct sun5i_timer_clksrc *cs; + unsigned long rate; + int ret; + + cs = kzalloc(sizeof(*cs), GFP_KERNEL); + if (!cs) + return -ENOMEM; + + ret = clk_prepare_enable(clk); + if (ret) { + pr_err("Couldn't enable parent clock\n"); + goto err_free; + } + + rate = clk_get_rate(clk); + + cs->timer.base = base; + cs->timer.clk = clk; + cs->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clksrc; + cs->timer.clk_rate_cb.next = NULL; + + ret = clk_notifier_register(clk, &cs->timer.clk_rate_cb); + if (ret) { + pr_err("Unable to register clock notifier.\n"); + goto err_disable_clk; + } + + writel(~0, base + TIMER_INTVAL_LO_REG(1)); + writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, + base + TIMER_CTL_REG(1)); + + cs->clksrc.name = node->name; + cs->clksrc.rating = 340; + cs->clksrc.read = sun5i_clksrc_read; + cs->clksrc.mask = CLOCKSOURCE_MASK(32); + cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS; + + ret = clocksource_register_hz(&cs->clksrc, rate); + if (ret) { + pr_err("Couldn't register clock source.\n"); + goto err_remove_notifier; + } + + return 0; + +err_remove_notifier: + clk_notifier_unregister(clk, &cs->timer.clk_rate_cb); +err_disable_clk: + clk_disable_unprepare(clk); +err_free: + kfree(cs); + return ret; +} + +static int sun5i_rate_cb_clkevt(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct sun5i_timer *timer = to_sun5i_timer(nb); + struct sun5i_timer_clkevt *ce = container_of(timer, struct sun5i_timer_clkevt, timer); + + if (event == POST_RATE_CHANGE) { + clockevents_update_freq(&ce->clkevt, ndata->new_rate); + ce->timer.ticks_per_jiffy = DIV_ROUND_UP(ndata->new_rate, HZ); + } + + return NOTIFY_DONE; +} + +static int __init sun5i_setup_clockevent(struct device_node *node, void __iomem *base, + struct clk *clk, int irq) +{ + struct sun5i_timer_clkevt *ce; + unsigned long rate; + int ret; + u32 val; + + ce = kzalloc(sizeof(*ce), GFP_KERNEL); + if (!ce) + return -ENOMEM; + + ret = clk_prepare_enable(clk); + if (ret) { + pr_err("Couldn't enable parent clock\n"); + goto err_free; + } + + rate = clk_get_rate(clk); + + ce->timer.base = base; + ce->timer.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); + ce->timer.clk = clk; + ce->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clkevt; + ce->timer.clk_rate_cb.next = NULL; + + ret = clk_notifier_register(clk, &ce->timer.clk_rate_cb); + if (ret) { + pr_err("Unable to register clock notifier.\n"); + goto err_disable_clk; + } + + ce->clkevt.name = node->name; + ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + ce->clkevt.set_next_event = sun5i_clkevt_next_event; + ce->clkevt.set_mode = sun5i_clkevt_mode; + ce->clkevt.rating = 340; + ce->clkevt.irq = irq; + ce->clkevt.cpumask = cpu_possible_mask; + + /* Enable timer0 interrupt */ + val = readl(base + TIMER_IRQ_EN_REG); + writel(val | TIMER_IRQ_EN(0), base + TIMER_IRQ_EN_REG); + + clockevents_config_and_register(&ce->clkevt, rate, + TIMER_SYNC_TICKS, 0xffffffff); + + ret = request_irq(irq, sun5i_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, + "sun5i_timer0", ce); + if (ret) { + pr_err("Unable to register interrupt\n"); + goto err_remove_notifier; + } + + return 0; + +err_remove_notifier: + clk_notifier_unregister(clk, &ce->timer.clk_rate_cb); +err_disable_clk: + clk_disable_unprepare(clk); +err_free: + kfree(ce); + return ret; } static void __init sun5i_timer_init(struct device_node *node) { struct reset_control *rstc; - unsigned long rate; + void __iomem *timer_base; struct clk *clk; - int ret, irq; - u32 val; + int irq; - timer_base = of_iomap(node, 0); + timer_base = of_io_request_and_map(node, 0, of_node_full_name(node)); if (!timer_base) panic("Can't map registers"); @@ -161,36 +334,13 @@ static void __init sun5i_timer_init(struct device_node *node) clk = of_clk_get(node, 0); if (IS_ERR(clk)) panic("Can't get timer clock"); - clk_prepare_enable(clk); - rate = clk_get_rate(clk); rstc = of_reset_control_get(node, NULL); if (!IS_ERR(rstc)) reset_control_deassert(rstc); - writel(~0, timer_base + TIMER_INTVAL_LO_REG(1)); - writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, - timer_base + TIMER_CTL_REG(1)); - - sched_clock_register(sun5i_timer_sched_read, 32, rate); - clocksource_mmio_init(timer_base + TIMER_CNTVAL_LO_REG(1), node->name, - rate, 340, 32, clocksource_mmio_readl_down); - - ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); - - ret = setup_irq(irq, &sun5i_timer_irq); - if (ret) - pr_warn("failed to setup irq %d\n", irq); - - /* Enable timer0 interrupt */ - val = readl(timer_base + TIMER_IRQ_EN_REG); - writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG); - - sun5i_clockevent.cpumask = cpu_possible_mask; - sun5i_clockevent.irq = irq; - - clockevents_config_and_register(&sun5i_clockevent, rate, - TIMER_SYNC_TICKS, 0xffffffff); + sun5i_setup_clocksource(node, timer_base, clk, irq); + sun5i_setup_clockevent(node, timer_base, clk, irq); } CLOCKSOURCE_OF_DECLARE(sun5i_a13, "allwinner,sun5i-a13-hstimer", sun5i_timer_init); diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 28e59a4..8ae655c 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1698,15 +1698,18 @@ void cpufreq_resume(void) || __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS)) pr_err("%s: Failed to start governor for policy: %p\n", __func__, policy); - - /* - * schedule call cpufreq_update_policy() for boot CPU, i.e. last - * policy in list. It will verify that the current freq is in - * sync with what we believe it to be. - */ - if (list_is_last(&policy->policy_list, &cpufreq_policy_list)) - schedule_work(&policy->update); } + + /* + * schedule call cpufreq_update_policy() for first-online CPU, as that + * wouldn't be hotplugged-out on suspend. It will verify that the + * current freq is in sync with what we believe it to be. + */ + policy = cpufreq_cpu_get_raw(cpumask_first(cpu_online_mask)); + if (WARN_ON(!policy)) + return; + + schedule_work(&policy->update); } /** diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index 5e98c6b..82d2fbb 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -159,7 +159,7 @@ static struct cpufreq_driver exynos_driver = { static int exynos_cpufreq_probe(struct platform_device *pdev) { - struct device_node *cpus, *np; + struct device_node *cpu0; int ret = -EINVAL; exynos_info = kzalloc(sizeof(*exynos_info), GFP_KERNEL); @@ -206,28 +206,19 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) if (ret) goto err_cpufreq_reg; - cpus = of_find_node_by_path("/cpus"); - if (!cpus) { - pr_err("failed to find cpus node\n"); + cpu0 = of_get_cpu_node(0, NULL); + if (!cpu0) { + pr_err("failed to find cpu0 node\n"); return 0; } - np = of_get_next_child(cpus, NULL); - if (!np) { - pr_err("failed to find cpus child node\n"); - of_node_put(cpus); - return 0; - } - - if (of_find_property(np, "#cooling-cells", NULL)) { - cdev = of_cpufreq_cooling_register(np, + if (of_find_property(cpu0, "#cooling-cells", NULL)) { + cdev = of_cpufreq_cooling_register(cpu0, cpu_present_mask); if (IS_ERR(cdev)) pr_err("running cpufreq without cooling device: %ld\n", PTR_ERR(cdev)); } - of_node_put(np); - of_node_put(cpus); return 0; diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/ppc-corenet-cpufreq.c index bee5df7..7cb4b76 100644 --- a/drivers/cpufreq/ppc-corenet-cpufreq.c +++ b/drivers/cpufreq/ppc-corenet-cpufreq.c @@ -22,6 +22,8 @@ #include <linux/smp.h> #include <sysdev/fsl_soc.h> +#include <asm/smp.h> /* for get_hard_smp_processor_id() in UP configs */ + /** * struct cpu_data - per CPU data struct * @parent: the parent node of cpu clock diff --git a/drivers/cpuidle/cpuidle-mvebu-v7.c b/drivers/cpuidle/cpuidle-mvebu-v7.c index 38e6861..980151f 100644 --- a/drivers/cpuidle/cpuidle-mvebu-v7.c +++ b/drivers/cpuidle/cpuidle-mvebu-v7.c @@ -37,11 +37,11 @@ static int mvebu_v7_enter_idle(struct cpuidle_device *dev, deepidle = true; ret = mvebu_v7_cpu_suspend(deepidle); + cpu_pm_exit(); + if (ret) return ret; - cpu_pm_exit(); - return index; } @@ -50,17 +50,17 @@ static struct cpuidle_driver armadaxp_idle_driver = { .states[0] = ARM_CPUIDLE_WFI_STATE, .states[1] = { .enter = mvebu_v7_enter_idle, - .exit_latency = 10, + .exit_latency = 100, .power_usage = 50, - .target_residency = 100, + .target_residency = 1000, .name = "MV CPU IDLE", .desc = "CPU power down", }, .states[2] = { .enter = mvebu_v7_enter_idle, - .exit_latency = 100, + .exit_latency = 1000, .power_usage = 5, - .target_residency = 1000, + .target_residency = 10000, .flags = MVEBU_V7_FLAG_DEEP_IDLE, .name = "MV CPU DEEP IDLE", .desc = "CPU and L2 Fabric power down", diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 4d53458..7a73a27 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -44,6 +44,12 @@ void disable_cpuidle(void) off = 1; } +bool cpuidle_not_available(struct cpuidle_driver *drv, + struct cpuidle_device *dev) +{ + return off || !initialized || !drv || !dev || !dev->enabled; +} + /** * cpuidle_play_dead - cpu off-lining * @@ -66,14 +72,8 @@ int cpuidle_play_dead(void) return -ENODEV; } -/** - * cpuidle_find_deepest_state - Find deepest state meeting specific conditions. - * @drv: cpuidle driver for the given CPU. - * @dev: cpuidle device for the given CPU. - * @freeze: Whether or not the state should be suitable for suspend-to-idle. - */ -static int cpuidle_find_deepest_state(struct cpuidle_driver *drv, - struct cpuidle_device *dev, bool freeze) +static int find_deepest_state(struct cpuidle_driver *drv, + struct cpuidle_device *dev, bool freeze) { unsigned int latency_req = 0; int i, ret = freeze ? -1 : CPUIDLE_DRIVER_STATE_START - 1; @@ -92,6 +92,17 @@ static int cpuidle_find_deepest_state(struct cpuidle_driver *drv, return ret; } +/** + * cpuidle_find_deepest_state - Find the deepest available idle state. + * @drv: cpuidle driver for the given CPU. + * @dev: cpuidle device for the given CPU. + */ +int cpuidle_find_deepest_state(struct cpuidle_driver *drv, + struct cpuidle_device *dev) +{ + return find_deepest_state(drv, dev, false); +} + static void enter_freeze_proper(struct cpuidle_driver *drv, struct cpuidle_device *dev, int index) { @@ -113,15 +124,14 @@ static void enter_freeze_proper(struct cpuidle_driver *drv, /** * cpuidle_enter_freeze - Enter an idle state suitable for suspend-to-idle. + * @drv: cpuidle driver for the given CPU. + * @dev: cpuidle device for the given CPU. * * If there are states with the ->enter_freeze callback, find the deepest of - * them and enter it with frozen tick. Otherwise, find the deepest state - * available and enter it normally. + * them and enter it with frozen tick. */ -void cpuidle_enter_freeze(void) +int cpuidle_enter_freeze(struct cpuidle_driver *drv, struct cpuidle_device *dev) { - struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); - struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); int index; /* @@ -129,24 +139,11 @@ void cpuidle_enter_freeze(void) * that interrupts won't be enabled when it exits and allows the tick to * be frozen safely. */ - index = cpuidle_find_deepest_state(drv, dev, true); - if (index >= 0) { - enter_freeze_proper(drv, dev, index); - return; - } - - /* - * It is not safe to freeze the tick, find the deepest state available - * at all and try to enter it normally. - */ - index = cpuidle_find_deepest_state(drv, dev, false); + index = find_deepest_state(drv, dev, true); if (index >= 0) - cpuidle_enter(drv, dev, index); - else - arch_cpu_idle(); + enter_freeze_proper(drv, dev, index); - /* Interrupts are enabled again here. */ - local_irq_disable(); + return index; } /** @@ -205,12 +202,6 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, */ int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) { - if (off || !initialized) - return -ENODEV; - - if (!drv || !dev || !dev->enabled) - return -EBUSY; - return cpuidle_curr_governor->select(drv, dev); } @@ -339,9 +330,6 @@ int cpuidle_enable_device(struct cpuidle_device *dev) if (!dev->registered) return -EINVAL; - if (!dev->state_count) - dev->state_count = drv->state_count; - ret = cpuidle_add_device_sysfs(dev); if (ret) return ret; diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 2697e87..5db1478 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -13,7 +13,7 @@ #include <linux/sched.h> #include <linux/cpuidle.h> #include <linux/cpumask.h> -#include <linux/clockchips.h> +#include <linux/tick.h> #include "cpuidle.h" @@ -130,21 +130,20 @@ static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) #endif /** - * cpuidle_setup_broadcast_timer - enable/disable the broadcast timer + * cpuidle_setup_broadcast_timer - enable/disable the broadcast timer on a cpu * @arg: a void pointer used to match the SMP cross call API * - * @arg is used as a value of type 'long' with one of the two values: - * - CLOCK_EVT_NOTIFY_BROADCAST_ON - * - CLOCK_EVT_NOTIFY_BROADCAST_OFF + * If @arg is NULL broadcast is disabled otherwise enabled * - * Set the broadcast timer notification for the current CPU. This function - * is executed per CPU by an SMP cross call. It not supposed to be called - * directly. + * This function is executed per CPU by an SMP cross call. It's not + * supposed to be called directly. */ static void cpuidle_setup_broadcast_timer(void *arg) { - int cpu = smp_processor_id(); - clockevents_notify((long)(arg), &cpu); + if (arg) + tick_broadcast_enable(); + else + tick_broadcast_disable(); } /** @@ -239,7 +238,7 @@ static int __cpuidle_register_driver(struct cpuidle_driver *drv) if (drv->bctimer) on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer, - (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1); + (void *)1, 1); poll_idle_init(drv); @@ -263,7 +262,7 @@ static void __cpuidle_unregister_driver(struct cpuidle_driver *drv) if (drv->bctimer) { drv->bctimer = 0; on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer, - (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1); + NULL, 1); } __cpuidle_unset_driver(drv); diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 97c5903..832a2c3 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -401,7 +401,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device) struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device); /* state statistics */ - for (i = 0; i < device->state_count; i++) { + for (i = 0; i < drv->state_count; i++) { kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL); if (!kobj) goto error_state; @@ -433,9 +433,10 @@ error_state: */ static void cpuidle_remove_state_sysfs(struct cpuidle_device *device) { + struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device); int i; - for (i = 0; i < device->state_count; i++) + for (i = 0; i < drv->state_count; i++) cpuidle_free_state_kobj(device, i); } diff --git a/drivers/dma-buf/fence.c b/drivers/dma-buf/fence.c index e554111..50ef8bd 100644 --- a/drivers/dma-buf/fence.c +++ b/drivers/dma-buf/fence.c @@ -159,6 +159,9 @@ fence_wait_timeout(struct fence *fence, bool intr, signed long timeout) if (WARN_ON(timeout < 0)) return -EINVAL; + if (timeout == 0) + return fence_is_signaled(fence); + trace_fence_wait_start(fence); ret = fence->ops->wait(fence, intr, timeout); trace_fence_wait_end(fence); diff --git a/drivers/dma-buf/reservation.c b/drivers/dma-buf/reservation.c index 3c97c8f..39920d7 100644 --- a/drivers/dma-buf/reservation.c +++ b/drivers/dma-buf/reservation.c @@ -327,6 +327,9 @@ long reservation_object_wait_timeout_rcu(struct reservation_object *obj, unsigned seq, shared_count, i = 0; long ret = timeout; + if (!timeout) + return reservation_object_test_signaled_rcu(obj, wait_all); + retry: fence = NULL; shared_count = 0; @@ -402,8 +405,6 @@ reservation_object_test_signaled_single(struct fence *passed_fence) int ret = 1; if (!test_bit(FENCE_FLAG_SIGNALED_BIT, &lfence->flags)) { - int ret; - fence = fence_get_rcu(lfence); if (!fence) return -1; diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index a874b6e..942ca54 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -51,19 +51,6 @@ config INTEL_MIC_X100_DMA OS and tools for MIC to use with this driver are available from <http://software.intel.com/en-us/mic-developer>. -config INTEL_MID_DMAC - tristate "Intel MID DMA support for Peripheral DMA controllers" - depends on PCI && X86 - select DMA_ENGINE - default n - help - Enable support for the Intel(R) MID DMA engine present - in Intel MID chipsets. - - Say Y here if you have such a chipset. - - If unsure, say N. - config ASYNC_TX_ENABLE_CHANNEL_SWITCH bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index f915f61..539d482 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -6,7 +6,6 @@ obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o obj-$(CONFIG_DMA_ACPI) += acpi-dma.o obj-$(CONFIG_DMA_OF) += of-dma.o -obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o obj-$(CONFIG_DMATEST) += dmatest.o obj-$(CONFIG_INTEL_IOATDMA) += ioat/ obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 4a5fd24..83aa55d 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -97,6 +97,12 @@ #define DRIVER_NAME "pl08xdmac" +#define PL80X_DMA_BUSWIDTHS \ + BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) | \ + BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) + static struct amba_driver pl08x_amba_driver; struct pl08x_driver_data; @@ -2070,6 +2076,10 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) pl08x->memcpy.device_pause = pl08x_pause; pl08x->memcpy.device_resume = pl08x_resume; pl08x->memcpy.device_terminate_all = pl08x_terminate_all; + pl08x->memcpy.src_addr_widths = PL80X_DMA_BUSWIDTHS; + pl08x->memcpy.dst_addr_widths = PL80X_DMA_BUSWIDTHS; + pl08x->memcpy.directions = BIT(DMA_MEM_TO_MEM); + pl08x->memcpy.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; /* Initialize slave engine */ dma_cap_set(DMA_SLAVE, pl08x->slave.cap_mask); @@ -2086,6 +2096,10 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) pl08x->slave.device_pause = pl08x_pause; pl08x->slave.device_resume = pl08x_resume; pl08x->slave.device_terminate_all = pl08x_terminate_all; + pl08x->slave.src_addr_widths = PL80X_DMA_BUSWIDTHS; + pl08x->slave.dst_addr_widths = PL80X_DMA_BUSWIDTHS; + pl08x->slave.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + pl08x->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; /* Get the platform data */ pl08x->pd = dev_get_platdata(&adev->dev); diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 1e1a4c5..0b4fc6f 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -238,93 +238,126 @@ static void atc_dostart(struct at_dma_chan *atchan, struct at_desc *first) } /* - * atc_get_current_descriptors - - * locate the descriptor which equal to physical address in DSCR - * @atchan: the channel we want to start - * @dscr_addr: physical descriptor address in DSCR + * atc_get_desc_by_cookie - get the descriptor of a cookie + * @atchan: the DMA channel + * @cookie: the cookie to get the descriptor for */ -static struct at_desc *atc_get_current_descriptors(struct at_dma_chan *atchan, - u32 dscr_addr) +static struct at_desc *atc_get_desc_by_cookie(struct at_dma_chan *atchan, + dma_cookie_t cookie) { - struct at_desc *desc, *_desc, *child, *desc_cur = NULL; + struct at_desc *desc, *_desc; - list_for_each_entry_safe(desc, _desc, &atchan->active_list, desc_node) { - if (desc->lli.dscr == dscr_addr) { - desc_cur = desc; - break; - } + list_for_each_entry_safe(desc, _desc, &atchan->queue, desc_node) { + if (desc->txd.cookie == cookie) + return desc; + } - list_for_each_entry(child, &desc->tx_list, desc_node) { - if (child->lli.dscr == dscr_addr) { - desc_cur = child; - break; - } - } + list_for_each_entry_safe(desc, _desc, &atchan->active_list, desc_node) { + if (desc->txd.cookie == cookie) + return desc; } - return desc_cur; + return NULL; } -/* - * atc_get_bytes_left - - * Get the number of bytes residue in dma buffer, - * @chan: the channel we want to start +/** + * atc_calc_bytes_left - calculates the number of bytes left according to the + * value read from CTRLA. + * + * @current_len: the number of bytes left before reading CTRLA + * @ctrla: the value of CTRLA + * @desc: the descriptor containing the transfer width + */ +static inline int atc_calc_bytes_left(int current_len, u32 ctrla, + struct at_desc *desc) +{ + return current_len - ((ctrla & ATC_BTSIZE_MAX) << desc->tx_width); +} + +/** + * atc_calc_bytes_left_from_reg - calculates the number of bytes left according + * to the current value of CTRLA. + * + * @current_len: the number of bytes left before reading CTRLA + * @atchan: the channel to read CTRLA for + * @desc: the descriptor containing the transfer width + */ +static inline int atc_calc_bytes_left_from_reg(int current_len, + struct at_dma_chan *atchan, struct at_desc *desc) +{ + u32 ctrla = channel_readl(atchan, CTRLA); + + return atc_calc_bytes_left(current_len, ctrla, desc); +} + +/** + * atc_get_bytes_left - get the number of bytes residue for a cookie + * @chan: DMA channel + * @cookie: transaction identifier to check status of */ -static int atc_get_bytes_left(struct dma_chan *chan) +static int atc_get_bytes_left(struct dma_chan *chan, dma_cookie_t cookie) { struct at_dma_chan *atchan = to_at_dma_chan(chan); - struct at_dma *atdma = to_at_dma(chan->device); - int chan_id = atchan->chan_common.chan_id; struct at_desc *desc_first = atc_first_active(atchan); - struct at_desc *desc_cur; - int ret = 0, count = 0; + struct at_desc *desc; + int ret; + u32 ctrla, dscr; /* - * Initialize necessary values in the first time. - * remain_desc record remain desc length. + * If the cookie doesn't match to the currently running transfer then + * we can return the total length of the associated DMA transfer, + * because it is still queued. */ - if (atchan->remain_desc == 0) - /* First descriptor embedds the transaction length */ - atchan->remain_desc = desc_first->len; + desc = atc_get_desc_by_cookie(atchan, cookie); + if (desc == NULL) + return -EINVAL; + else if (desc != desc_first) + return desc->total_len; - /* - * This happens when current descriptor transfer complete. - * The residual buffer size should reduce current descriptor length. - */ - if (unlikely(test_bit(ATC_IS_BTC, &atchan->status))) { - clear_bit(ATC_IS_BTC, &atchan->status); - desc_cur = atc_get_current_descriptors(atchan, - channel_readl(atchan, DSCR)); - if (!desc_cur) { - ret = -EINVAL; - goto out; - } + /* cookie matches to the currently running transfer */ + ret = desc_first->total_len; - count = (desc_cur->lli.ctrla & ATC_BTSIZE_MAX) - << desc_first->tx_width; - if (atchan->remain_desc < count) { - ret = -EINVAL; - goto out; + if (desc_first->lli.dscr) { + /* hardware linked list transfer */ + + /* + * Calculate the residue by removing the length of the child + * descriptors already transferred from the total length. + * To get the current child descriptor we can use the value of + * the channel's DSCR register and compare it against the value + * of the hardware linked list structure of each child + * descriptor. + */ + + ctrla = channel_readl(atchan, CTRLA); + rmb(); /* ensure CTRLA is read before DSCR */ + dscr = channel_readl(atchan, DSCR); + + /* for the first descriptor we can be more accurate */ + if (desc_first->lli.dscr == dscr) + return atc_calc_bytes_left(ret, ctrla, desc_first); + + ret -= desc_first->len; + list_for_each_entry(desc, &desc_first->tx_list, desc_node) { + if (desc->lli.dscr == dscr) + break; + + ret -= desc->len; } - atchan->remain_desc -= count; - ret = atchan->remain_desc; - } else { /* - * Get residual bytes when current - * descriptor transfer in progress. + * For the last descriptor in the chain we can calculate + * the remaining bytes using the channel's register. + * Note that the transfer width of the first and last + * descriptor may differ. */ - count = (channel_readl(atchan, CTRLA) & ATC_BTSIZE_MAX) - << (desc_first->tx_width); - ret = atchan->remain_desc - count; + if (!desc->lli.dscr) + ret = atc_calc_bytes_left_from_reg(ret, atchan, desc); + } else { + /* single transfer */ + ret = atc_calc_bytes_left_from_reg(ret, atchan, desc_first); } - /* - * Check fifo empty. - */ - if (!(dma_readl(atdma, CHSR) & AT_DMA_EMPT(chan_id))) - atc_issue_pending(chan); -out: return ret; } @@ -539,8 +572,6 @@ static irqreturn_t at_dma_interrupt(int irq, void *dev_id) /* Give information to tasklet */ set_bit(ATC_IS_ERROR, &atchan->status); } - if (pending & AT_DMA_BTC(i)) - set_bit(ATC_IS_BTC, &atchan->status); tasklet_schedule(&atchan->tasklet); ret = IRQ_HANDLED; } @@ -653,14 +684,18 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, desc->lli.ctrlb = ctrlb; desc->txd.cookie = 0; + desc->len = xfer_count << src_width; atc_desc_chain(&first, &prev, desc); } /* First descriptor of the chain embedds additional information */ first->txd.cookie = -EBUSY; - first->len = len; + first->total_len = len; + + /* set transfer width for the calculation of the residue */ first->tx_width = src_width; + prev->tx_width = src_width; /* set end-of-link to the last link descriptor of list*/ set_desc_eol(desc); @@ -752,6 +787,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | ATC_SRC_WIDTH(mem_width) | len >> mem_width; desc->lli.ctrlb = ctrlb; + desc->len = len; atc_desc_chain(&first, &prev, desc); total_len += len; @@ -792,6 +828,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | ATC_DST_WIDTH(mem_width) | len >> reg_width; desc->lli.ctrlb = ctrlb; + desc->len = len; atc_desc_chain(&first, &prev, desc); total_len += len; @@ -806,8 +843,11 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, /* First descriptor of the chain embedds additional information */ first->txd.cookie = -EBUSY; - first->len = total_len; + first->total_len = total_len; + + /* set transfer width for the calculation of the residue */ first->tx_width = reg_width; + prev->tx_width = reg_width; /* first link descriptor of list is responsible of flags */ first->txd.flags = flags; /* client is in control of this ack */ @@ -872,6 +912,7 @@ atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc, | ATC_FC_MEM2PER | ATC_SIF(atchan->mem_if) | ATC_DIF(atchan->per_if); + desc->len = period_len; break; case DMA_DEV_TO_MEM: @@ -883,6 +924,7 @@ atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc, | ATC_FC_PER2MEM | ATC_SIF(atchan->per_if) | ATC_DIF(atchan->mem_if); + desc->len = period_len; break; default: @@ -964,7 +1006,7 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, /* First descriptor of the chain embedds additional information */ first->txd.cookie = -EBUSY; - first->len = buf_len; + first->total_len = buf_len; first->tx_width = reg_width; return &first->txd; @@ -1118,7 +1160,7 @@ atc_tx_status(struct dma_chan *chan, spin_lock_irqsave(&atchan->lock, flags); /* Get number of bytes left in the active transactions */ - bytes = atc_get_bytes_left(chan); + bytes = atc_get_bytes_left(chan, cookie); spin_unlock_irqrestore(&atchan->lock, flags); @@ -1214,7 +1256,6 @@ static int atc_alloc_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&atchan->lock, flags); atchan->descs_allocated = i; - atchan->remain_desc = 0; list_splice(&tmp_list, &atchan->free_list); dma_cookie_init(chan); spin_unlock_irqrestore(&atchan->lock, flags); @@ -1257,7 +1298,6 @@ static void atc_free_chan_resources(struct dma_chan *chan) list_splice_init(&atchan->free_list, &list); atchan->descs_allocated = 0; atchan->status = 0; - atchan->remain_desc = 0; dev_vdbg(chan2dev(chan), "free_chan_resources: done\n"); } diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index d6bba6c..2727ca5 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -181,8 +181,9 @@ struct at_lli { * @at_lli: hardware lli structure * @txd: support for the async_tx api * @desc_node: node on the channed descriptors list - * @len: total transaction bytecount + * @len: descriptor byte count * @tx_width: transfer width + * @total_len: total transaction byte count */ struct at_desc { /* FIRST values the hardware uses */ @@ -194,6 +195,7 @@ struct at_desc { struct list_head desc_node; size_t len; u32 tx_width; + size_t total_len; }; static inline struct at_desc * @@ -213,7 +215,6 @@ txd_to_at_desc(struct dma_async_tx_descriptor *txd) enum atc_status { ATC_IS_ERROR = 0, ATC_IS_PAUSED = 1, - ATC_IS_BTC = 2, ATC_IS_CYCLIC = 24, }; @@ -231,7 +232,6 @@ enum atc_status { * @save_cfg: configuration register that is saved on suspend/resume cycle * @save_dscr: for cyclic operations, preserve next descriptor address in * the cyclic list on suspend/resume cycle - * @remain_desc: to save remain desc length * @dma_sconfig: configuration for slave transfers, passed via * .device_config * @lock: serializes enqueue/dequeue operations to descriptors lists @@ -251,7 +251,6 @@ struct at_dma_chan { struct tasklet_struct tasklet; u32 save_cfg; u32 save_dscr; - u32 remain_desc; struct dma_slave_config dma_sconfig; spinlock_t lock; diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index 09e2825..d9891d3 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -664,7 +664,6 @@ at_xdmac_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, struct at_xdmac_desc *first = NULL, *prev = NULL; unsigned int periods = buf_len / period_len; int i; - u32 cfg; dev_dbg(chan2dev(chan), "%s: buf_addr=%pad, buf_len=%zd, period_len=%zd, dir=%s, flags=0x%lx\n", __func__, &buf_addr, buf_len, period_len, @@ -700,17 +699,17 @@ at_xdmac_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, if (direction == DMA_DEV_TO_MEM) { desc->lld.mbr_sa = atchan->per_src_addr; desc->lld.mbr_da = buf_addr + i * period_len; - cfg = atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG]; + desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG]; } else { desc->lld.mbr_sa = buf_addr + i * period_len; desc->lld.mbr_da = atchan->per_dst_addr; - cfg = atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG]; + desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG]; } desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV1 | AT_XDMAC_MBR_UBC_NDEN | AT_XDMAC_MBR_UBC_NSEN | AT_XDMAC_MBR_UBC_NDE - | period_len >> at_xdmac_get_dwidth(cfg); + | period_len >> at_xdmac_get_dwidth(desc->lld.mbr_cfg); dev_dbg(chan2dev(chan), "%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x\n", diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index 0723096..c92d6a7 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -475,6 +475,7 @@ static int bcm2835_dma_terminate_all(struct dma_chan *chan) * c->desc is NULL and exit.) */ if (c->desc) { + bcm2835_dma_desc_free(&c->desc->vd); c->desc = NULL; bcm2835_dma_abort(c->chan_base); diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c index 512cb8e..ceedafb 100644 --- a/drivers/dma/cppi41.c +++ b/drivers/dma/cppi41.c @@ -903,6 +903,11 @@ static const struct cppi_glue_infos *get_glue_info(struct device *dev) return of_id->data; } +#define CPPI41_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) + static int cppi41_dma_probe(struct platform_device *pdev) { struct cppi41_dd *cdd; @@ -926,6 +931,10 @@ static int cppi41_dma_probe(struct platform_device *pdev) cdd->ddev.device_issue_pending = cppi41_dma_issue_pending; cdd->ddev.device_prep_slave_sg = cppi41_dma_prep_slave_sg; cdd->ddev.device_terminate_all = cppi41_stop_chan; + cdd->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + cdd->ddev.src_addr_widths = CPPI41_DMA_BUSWIDTHS; + cdd->ddev.dst_addr_widths = CPPI41_DMA_BUSWIDTHS; + cdd->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; cdd->ddev.dev = dev; INIT_LIST_HEAD(&cdd->ddev.channels); cpp41_dma_info.dma_cap = cdd->ddev.cap_mask; diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c index 4527a3e..8488441 100644 --- a/drivers/dma/dma-jz4740.c +++ b/drivers/dma/dma-jz4740.c @@ -511,6 +511,9 @@ static void jz4740_dma_desc_free(struct virt_dma_desc *vdesc) kfree(container_of(vdesc, struct jz4740_dma_desc, vdesc)); } +#define JZ4740_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) + static int jz4740_dma_probe(struct platform_device *pdev) { struct jz4740_dmaengine_chan *chan; @@ -548,6 +551,10 @@ static int jz4740_dma_probe(struct platform_device *pdev) dd->device_prep_dma_cyclic = jz4740_dma_prep_dma_cyclic; dd->device_config = jz4740_dma_slave_config; dd->device_terminate_all = jz4740_dma_terminate_all; + dd->src_addr_widths = JZ4740_DMA_BUSWIDTHS; + dd->dst_addr_widths = JZ4740_DMA_BUSWIDTHS; + dd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + dd->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; dd->dev = &pdev->dev; INIT_LIST_HEAD(&dd->channels); diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index f15712f..ac336a9 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -859,9 +859,6 @@ int dma_async_device_register(struct dma_device *device) BUG_ON(!device->device_issue_pending); BUG_ON(!device->dev); - WARN(dma_has_cap(DMA_SLAVE, device->cap_mask) && !device->directions, - "this driver doesn't support generic slave capabilities reporting\n"); - /* note: this only matters in the * CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH=n case */ diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 455b7a4..a8ad052 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -626,7 +626,7 @@ static irqreturn_t dw_dma_interrupt(int irq, void *dev_id) dev_vdbg(dw->dma.dev, "%s: status=0x%x\n", __func__, status); /* Check if we have any interrupt from the DMAC */ - if (!status) + if (!status || !dw->in_use) return IRQ_NONE; /* diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c index 6565a36..b2c3ae0 100644 --- a/drivers/dma/dw/platform.c +++ b/drivers/dma/dw/platform.c @@ -26,6 +26,8 @@ #include "internal.h" +#define DRV_NAME "dw_dmac" + static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { @@ -284,7 +286,7 @@ static struct platform_driver dw_driver = { .remove = dw_remove, .shutdown = dw_shutdown, .driver = { - .name = "dw_dmac", + .name = DRV_NAME, .pm = &dw_dev_pm_ops, .of_match_table = of_match_ptr(dw_dma_of_id_table), .acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table), @@ -305,3 +307,4 @@ module_exit(dw_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller platform driver"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 276157f..53dbd3b 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -260,6 +260,13 @@ static int edma_terminate_all(struct dma_chan *chan) */ if (echan->edesc) { int cyclic = echan->edesc->cyclic; + + /* + * free the running request descriptor + * since it is not in any of the vdesc lists + */ + edma_desc_free(&echan->edesc->vdesc); + echan->edesc = NULL; edma_stop(echan->ch_num); /* Move the cyclic channel back to default queue */ diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 18c0a13..66a0efb 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -531,6 +531,10 @@ static int sdma_run_channel0(struct sdma_engine *sdma) dev_err(sdma->dev, "Timeout waiting for CH0 ready\n"); } + /* Set bits of CONFIG register with dynamic context switching */ + if (readl(sdma->regs + SDMA_H_CONFIG) == 0) + writel_relaxed(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG); + return ret ? 0 : -ETIMEDOUT; } @@ -1394,9 +1398,6 @@ static int sdma_init(struct sdma_engine *sdma) writel_relaxed(ccb_phys, sdma->regs + SDMA_H_C0PTR); - /* Set bits of CONFIG register with given context switching mode */ - writel_relaxed(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG); - /* Initializes channel's priorities */ sdma_set_channel_priority(&sdma->channel[0], 7); diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c deleted file mode 100644 index 5aaead9..0000000 --- a/drivers/dma/intel_mid_dma.c +++ /dev/null @@ -1,1447 +0,0 @@ -/* - * intel_mid_dma.c - Intel Langwell DMA Drivers - * - * Copyright (C) 2008-10 Intel Corp - * Author: Vinod Koul <vinod.koul@intel.com> - * The driver design is based on dw_dmac driver - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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; 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * - */ -#include <linux/pci.h> -#include <linux/interrupt.h> -#include <linux/pm_runtime.h> -#include <linux/intel_mid_dma.h> -#include <linux/module.h> - -#include "dmaengine.h" - -#define MAX_CHAN 4 /*max ch across controllers*/ -#include "intel_mid_dma_regs.h" - -#define INTEL_MID_DMAC1_ID 0x0814 -#define INTEL_MID_DMAC2_ID 0x0813 -#define INTEL_MID_GP_DMAC2_ID 0x0827 -#define INTEL_MFLD_DMAC1_ID 0x0830 -#define LNW_PERIPHRAL_MASK_BASE 0xFFAE8008 -#define LNW_PERIPHRAL_MASK_SIZE 0x10 -#define LNW_PERIPHRAL_STATUS 0x0 -#define LNW_PERIPHRAL_MASK 0x8 - -struct intel_mid_dma_probe_info { - u8 max_chan; - u8 ch_base; - u16 block_size; - u32 pimr_mask; -}; - -#define INFO(_max_chan, _ch_base, _block_size, _pimr_mask) \ - ((kernel_ulong_t)&(struct intel_mid_dma_probe_info) { \ - .max_chan = (_max_chan), \ - .ch_base = (_ch_base), \ - .block_size = (_block_size), \ - .pimr_mask = (_pimr_mask), \ - }) - -/***************************************************************************** -Utility Functions*/ -/** - * get_ch_index - convert status to channel - * @status: status mask - * @base: dma ch base value - * - * Modify the status mask and return the channel index needing - * attention (or -1 if neither) - */ -static int get_ch_index(int *status, unsigned int base) -{ - int i; - for (i = 0; i < MAX_CHAN; i++) { - if (*status & (1 << (i + base))) { - *status = *status & ~(1 << (i + base)); - pr_debug("MDMA: index %d New status %x\n", i, *status); - return i; - } - } - return -1; -} - -/** - * get_block_ts - calculates dma transaction length - * @len: dma transfer length - * @tx_width: dma transfer src width - * @block_size: dma controller max block size - * - * Based on src width calculate the DMA trsaction length in data items - * return data items or FFFF if exceeds max length for block - */ -static int get_block_ts(int len, int tx_width, int block_size) -{ - int byte_width = 0, block_ts = 0; - - switch (tx_width) { - case DMA_SLAVE_BUSWIDTH_1_BYTE: - byte_width = 1; - break; - case DMA_SLAVE_BUSWIDTH_2_BYTES: - byte_width = 2; - break; - case DMA_SLAVE_BUSWIDTH_4_BYTES: - default: - byte_width = 4; - break; - } - - block_ts = len/byte_width; - if (block_ts > block_size) - block_ts = 0xFFFF; - return block_ts; -} - -/***************************************************************************** -DMAC1 interrupt Functions*/ - -/** - * dmac1_mask_periphral_intr - mask the periphral interrupt - * @mid: dma device for which masking is required - * - * Masks the DMA periphral interrupt - * this is valid for DMAC1 family controllers only - * This controller should have periphral mask registers already mapped - */ -static void dmac1_mask_periphral_intr(struct middma_device *mid) -{ - u32 pimr; - - if (mid->pimr_mask) { - pimr = readl(mid->mask_reg + LNW_PERIPHRAL_MASK); - pimr |= mid->pimr_mask; - writel(pimr, mid->mask_reg + LNW_PERIPHRAL_MASK); - } - return; -} - -/** - * dmac1_unmask_periphral_intr - unmask the periphral interrupt - * @midc: dma channel for which masking is required - * - * UnMasks the DMA periphral interrupt, - * this is valid for DMAC1 family controllers only - * This controller should have periphral mask registers already mapped - */ -static void dmac1_unmask_periphral_intr(struct intel_mid_dma_chan *midc) -{ - u32 pimr; - struct middma_device *mid = to_middma_device(midc->chan.device); - - if (mid->pimr_mask) { - pimr = readl(mid->mask_reg + LNW_PERIPHRAL_MASK); - pimr &= ~mid->pimr_mask; - writel(pimr, mid->mask_reg + LNW_PERIPHRAL_MASK); - } - return; -} - -/** - * enable_dma_interrupt - enable the periphral interrupt - * @midc: dma channel for which enable interrupt is required - * - * Enable the DMA periphral interrupt, - * this is valid for DMAC1 family controllers only - * This controller should have periphral mask registers already mapped - */ -static void enable_dma_interrupt(struct intel_mid_dma_chan *midc) -{ - dmac1_unmask_periphral_intr(midc); - - /*en ch interrupts*/ - iowrite32(UNMASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_TFR); - iowrite32(UNMASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_ERR); - return; -} - -/** - * disable_dma_interrupt - disable the periphral interrupt - * @midc: dma channel for which disable interrupt is required - * - * Disable the DMA periphral interrupt, - * this is valid for DMAC1 family controllers only - * This controller should have periphral mask registers already mapped - */ -static void disable_dma_interrupt(struct intel_mid_dma_chan *midc) -{ - /*Check LPE PISR, make sure fwd is disabled*/ - iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_BLOCK); - iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_TFR); - iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_ERR); - return; -} - -/***************************************************************************** -DMA channel helper Functions*/ -/** - * mid_desc_get - get a descriptor - * @midc: dma channel for which descriptor is required - * - * Obtain a descriptor for the channel. Returns NULL if none are free. - * Once the descriptor is returned it is private until put on another - * list or freed - */ -static struct intel_mid_dma_desc *midc_desc_get(struct intel_mid_dma_chan *midc) -{ - struct intel_mid_dma_desc *desc, *_desc; - struct intel_mid_dma_desc *ret = NULL; - - spin_lock_bh(&midc->lock); - list_for_each_entry_safe(desc, _desc, &midc->free_list, desc_node) { - if (async_tx_test_ack(&desc->txd)) { - list_del(&desc->desc_node); - ret = desc; - break; - } - } - spin_unlock_bh(&midc->lock); - return ret; -} - -/** - * mid_desc_put - put a descriptor - * @midc: dma channel for which descriptor is required - * @desc: descriptor to put - * - * Return a descriptor from lwn_desc_get back to the free pool - */ -static void midc_desc_put(struct intel_mid_dma_chan *midc, - struct intel_mid_dma_desc *desc) -{ - if (desc) { - spin_lock_bh(&midc->lock); - list_add_tail(&desc->desc_node, &midc->free_list); - spin_unlock_bh(&midc->lock); - } -} -/** - * midc_dostart - begin a DMA transaction - * @midc: channel for which txn is to be started - * @first: first descriptor of series - * - * Load a transaction into the engine. This must be called with midc->lock - * held and bh disabled. - */ -static void midc_dostart(struct intel_mid_dma_chan *midc, - struct intel_mid_dma_desc *first) -{ - struct middma_device *mid = to_middma_device(midc->chan.device); - - /* channel is idle */ - if (midc->busy && test_ch_en(midc->dma_base, midc->ch_id)) { - /*error*/ - pr_err("ERR_MDMA: channel is busy in start\n"); - /* The tasklet will hopefully advance the queue... */ - return; - } - midc->busy = true; - /*write registers and en*/ - iowrite32(first->sar, midc->ch_regs + SAR); - iowrite32(first->dar, midc->ch_regs + DAR); - iowrite32(first->lli_phys, midc->ch_regs + LLP); - iowrite32(first->cfg_hi, midc->ch_regs + CFG_HIGH); - iowrite32(first->cfg_lo, midc->ch_regs + CFG_LOW); - iowrite32(first->ctl_lo, midc->ch_regs + CTL_LOW); - iowrite32(first->ctl_hi, midc->ch_regs + CTL_HIGH); - pr_debug("MDMA:TX SAR %x,DAR %x,CFGL %x,CFGH %x,CTLH %x, CTLL %x\n", - (int)first->sar, (int)first->dar, first->cfg_hi, - first->cfg_lo, first->ctl_hi, first->ctl_lo); - first->status = DMA_IN_PROGRESS; - - iowrite32(ENABLE_CHANNEL(midc->ch_id), mid->dma_base + DMA_CHAN_EN); -} - -/** - * midc_descriptor_complete - process completed descriptor - * @midc: channel owning the descriptor - * @desc: the descriptor itself - * - * Process a completed descriptor and perform any callbacks upon - * the completion. The completion handling drops the lock during the - * callbacks but must be called with the lock held. - */ -static void midc_descriptor_complete(struct intel_mid_dma_chan *midc, - struct intel_mid_dma_desc *desc) - __releases(&midc->lock) __acquires(&midc->lock) -{ - struct dma_async_tx_descriptor *txd = &desc->txd; - dma_async_tx_callback callback_txd = NULL; - struct intel_mid_dma_lli *llitem; - void *param_txd = NULL; - - dma_cookie_complete(txd); - callback_txd = txd->callback; - param_txd = txd->callback_param; - - if (desc->lli != NULL) { - /*clear the DONE bit of completed LLI in memory*/ - llitem = desc->lli + desc->current_lli; - llitem->ctl_hi &= CLEAR_DONE; - if (desc->current_lli < desc->lli_length-1) - (desc->current_lli)++; - else - desc->current_lli = 0; - } - spin_unlock_bh(&midc->lock); - if (callback_txd) { - pr_debug("MDMA: TXD callback set ... calling\n"); - callback_txd(param_txd); - } - if (midc->raw_tfr) { - desc->status = DMA_COMPLETE; - if (desc->lli != NULL) { - pci_pool_free(desc->lli_pool, desc->lli, - desc->lli_phys); - pci_pool_destroy(desc->lli_pool); - desc->lli = NULL; - } - list_move(&desc->desc_node, &midc->free_list); - midc->busy = false; - } - spin_lock_bh(&midc->lock); - -} -/** - * midc_scan_descriptors - check the descriptors in channel - * mark completed when tx is completete - * @mid: device - * @midc: channel to scan - * - * Walk the descriptor chain for the device and process any entries - * that are complete. - */ -static void midc_scan_descriptors(struct middma_device *mid, - struct intel_mid_dma_chan *midc) -{ - struct intel_mid_dma_desc *desc = NULL, *_desc = NULL; - - /*tx is complete*/ - list_for_each_entry_safe(desc, _desc, &midc->active_list, desc_node) { - if (desc->status == DMA_IN_PROGRESS) - midc_descriptor_complete(midc, desc); - } - return; - } -/** - * midc_lli_fill_sg - Helper function to convert - * SG list to Linked List Items. - *@midc: Channel - *@desc: DMA descriptor - *@sglist: Pointer to SG list - *@sglen: SG list length - *@flags: DMA transaction flags - * - * Walk through the SG list and convert the SG list into Linked - * List Items (LLI). - */ -static int midc_lli_fill_sg(struct intel_mid_dma_chan *midc, - struct intel_mid_dma_desc *desc, - struct scatterlist *sglist, - unsigned int sglen, - unsigned int flags) -{ - struct intel_mid_dma_slave *mids; - struct scatterlist *sg; - dma_addr_t lli_next, sg_phy_addr; - struct intel_mid_dma_lli *lli_bloc_desc; - union intel_mid_dma_ctl_lo ctl_lo; - union intel_mid_dma_ctl_hi ctl_hi; - int i; - - pr_debug("MDMA: Entered midc_lli_fill_sg\n"); - mids = midc->mid_slave; - - lli_bloc_desc = desc->lli; - lli_next = desc->lli_phys; - - ctl_lo.ctl_lo = desc->ctl_lo; - ctl_hi.ctl_hi = desc->ctl_hi; - for_each_sg(sglist, sg, sglen, i) { - /*Populate CTL_LOW and LLI values*/ - if (i != sglen - 1) { - lli_next = lli_next + - sizeof(struct intel_mid_dma_lli); - } else { - /*Check for circular list, otherwise terminate LLI to ZERO*/ - if (flags & DMA_PREP_CIRCULAR_LIST) { - pr_debug("MDMA: LLI is configured in circular mode\n"); - lli_next = desc->lli_phys; - } else { - lli_next = 0; - ctl_lo.ctlx.llp_dst_en = 0; - ctl_lo.ctlx.llp_src_en = 0; - } - } - /*Populate CTL_HI values*/ - ctl_hi.ctlx.block_ts = get_block_ts(sg_dma_len(sg), - desc->width, - midc->dma->block_size); - /*Populate SAR and DAR values*/ - sg_phy_addr = sg_dma_address(sg); - if (desc->dirn == DMA_MEM_TO_DEV) { - lli_bloc_desc->sar = sg_phy_addr; - lli_bloc_desc->dar = mids->dma_slave.dst_addr; - } else if (desc->dirn == DMA_DEV_TO_MEM) { - lli_bloc_desc->sar = mids->dma_slave.src_addr; - lli_bloc_desc->dar = sg_phy_addr; - } - /*Copy values into block descriptor in system memroy*/ - lli_bloc_desc->llp = lli_next; - lli_bloc_desc->ctl_lo = ctl_lo.ctl_lo; - lli_bloc_desc->ctl_hi = ctl_hi.ctl_hi; - - lli_bloc_desc++; - } - /*Copy very first LLI values to descriptor*/ - desc->ctl_lo = desc->lli->ctl_lo; - desc->ctl_hi = desc->lli->ctl_hi; - desc->sar = desc->lli->sar; - desc->dar = desc->lli->dar; - - return 0; -} -/***************************************************************************** -DMA engine callback Functions*/ -/** - * intel_mid_dma_tx_submit - callback to submit DMA transaction - * @tx: dma engine descriptor - * - * Submit the DMA transaction for this descriptor, start if ch idle - */ -static dma_cookie_t intel_mid_dma_tx_submit(struct dma_async_tx_descriptor *tx) -{ - struct intel_mid_dma_desc *desc = to_intel_mid_dma_desc(tx); - struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(tx->chan); - dma_cookie_t cookie; - - spin_lock_bh(&midc->lock); - cookie = dma_cookie_assign(tx); - - if (list_empty(&midc->active_list)) - list_add_tail(&desc->desc_node, &midc->active_list); - else - list_add_tail(&desc->desc_node, &midc->queue); - - midc_dostart(midc, desc); - spin_unlock_bh(&midc->lock); - - return cookie; -} - -/** - * intel_mid_dma_issue_pending - callback to issue pending txn - * @chan: chan where pending trascation needs to be checked and submitted - * - * Call for scan to issue pending descriptors - */ -static void intel_mid_dma_issue_pending(struct dma_chan *chan) -{ - struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(chan); - - spin_lock_bh(&midc->lock); - if (!list_empty(&midc->queue)) - midc_scan_descriptors(to_middma_device(chan->device), midc); - spin_unlock_bh(&midc->lock); -} - -/** - * intel_mid_dma_tx_status - Return status of txn - * @chan: chan for where status needs to be checked - * @cookie: cookie for txn - * @txstate: DMA txn state - * - * Return status of DMA txn - */ -static enum dma_status intel_mid_dma_tx_status(struct dma_chan *chan, - dma_cookie_t cookie, - struct dma_tx_state *txstate) -{ - struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(chan); - enum dma_status ret; - - ret = dma_cookie_status(chan, cookie, txstate); - if (ret != DMA_COMPLETE) { - spin_lock_bh(&midc->lock); - midc_scan_descriptors(to_middma_device(chan->device), midc); - spin_unlock_bh(&midc->lock); - - ret = dma_cookie_status(chan, cookie, txstate); - } - - return ret; -} - -static int intel_mid_dma_config(struct dma_chan *chan, - struct dma_slave_config *slave) -{ - struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(chan); - struct intel_mid_dma_slave *mid_slave; - - BUG_ON(!midc); - BUG_ON(!slave); - pr_debug("MDMA: slave control called\n"); - - mid_slave = to_intel_mid_dma_slave(slave); - - BUG_ON(!mid_slave); - - midc->mid_slave = mid_slave; - return 0; -} - -static int intel_mid_dma_terminate_all(struct dma_chan *chan) -{ - struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(chan); - struct middma_device *mid = to_middma_device(chan->device); - struct intel_mid_dma_desc *desc, *_desc; - union intel_mid_dma_cfg_lo cfg_lo; - - spin_lock_bh(&midc->lock); - if (midc->busy == false) { - spin_unlock_bh(&midc->lock); - return 0; - } - /*Suspend and disable the channel*/ - cfg_lo.cfg_lo = ioread32(midc->ch_regs + CFG_LOW); - cfg_lo.cfgx.ch_susp = 1; - iowrite32(cfg_lo.cfg_lo, midc->ch_regs + CFG_LOW); - iowrite32(DISABLE_CHANNEL(midc->ch_id), mid->dma_base + DMA_CHAN_EN); - midc->busy = false; - /* Disable interrupts */ - disable_dma_interrupt(midc); - midc->descs_allocated = 0; - - spin_unlock_bh(&midc->lock); - list_for_each_entry_safe(desc, _desc, &midc->active_list, desc_node) { - if (desc->lli != NULL) { - pci_pool_free(desc->lli_pool, desc->lli, - desc->lli_phys); - pci_pool_destroy(desc->lli_pool); - desc->lli = NULL; - } - list_move(&desc->desc_node, &midc->free_list); - } - return 0; -} - - -/** - * intel_mid_dma_prep_memcpy - Prep memcpy txn - * @chan: chan for DMA transfer - * @dest: destn address - * @src: src address - * @len: DMA transfer len - * @flags: DMA flags - * - * Perform a DMA memcpy. Note we support slave periphral DMA transfers only - * The periphral txn details should be filled in slave structure properly - * Returns the descriptor for this txn - */ -static struct dma_async_tx_descriptor *intel_mid_dma_prep_memcpy( - struct dma_chan *chan, dma_addr_t dest, - dma_addr_t src, size_t len, unsigned long flags) -{ - struct intel_mid_dma_chan *midc; - struct intel_mid_dma_desc *desc = NULL; - struct intel_mid_dma_slave *mids; - union intel_mid_dma_ctl_lo ctl_lo; - union intel_mid_dma_ctl_hi ctl_hi; - union intel_mid_dma_cfg_lo cfg_lo; - union intel_mid_dma_cfg_hi cfg_hi; - enum dma_slave_buswidth width; - - pr_debug("MDMA: Prep for memcpy\n"); - BUG_ON(!chan); - if (!len) - return NULL; - - midc = to_intel_mid_dma_chan(chan); - BUG_ON(!midc); - - mids = midc->mid_slave; - BUG_ON(!mids); - - pr_debug("MDMA:called for DMA %x CH %d Length %zu\n", - midc->dma->pci_id, midc->ch_id, len); - pr_debug("MDMA:Cfg passed Mode %x, Dirn %x, HS %x, Width %x\n", - mids->cfg_mode, mids->dma_slave.direction, - mids->hs_mode, mids->dma_slave.src_addr_width); - - /*calculate CFG_LO*/ - if (mids->hs_mode == LNW_DMA_SW_HS) { - cfg_lo.cfg_lo = 0; - cfg_lo.cfgx.hs_sel_dst = 1; - cfg_lo.cfgx.hs_sel_src = 1; - } else if (mids->hs_mode == LNW_DMA_HW_HS) - cfg_lo.cfg_lo = 0x00000; - - /*calculate CFG_HI*/ - if (mids->cfg_mode == LNW_DMA_MEM_TO_MEM) { - /*SW HS only*/ - cfg_hi.cfg_hi = 0; - } else { - cfg_hi.cfg_hi = 0; - if (midc->dma->pimr_mask) { - cfg_hi.cfgx.protctl = 0x0; /*default value*/ - cfg_hi.cfgx.fifo_mode = 1; - if (mids->dma_slave.direction == DMA_MEM_TO_DEV) { - cfg_hi.cfgx.src_per = 0; - if (mids->device_instance == 0) - cfg_hi.cfgx.dst_per = 3; - if (mids->device_instance == 1) - cfg_hi.cfgx.dst_per = 1; - } else if (mids->dma_slave.direction == DMA_DEV_TO_MEM) { - if (mids->device_instance == 0) - cfg_hi.cfgx.src_per = 2; - if (mids->device_instance == 1) - cfg_hi.cfgx.src_per = 0; - cfg_hi.cfgx.dst_per = 0; - } - } else { - cfg_hi.cfgx.protctl = 0x1; /*default value*/ - cfg_hi.cfgx.src_per = cfg_hi.cfgx.dst_per = - midc->ch_id - midc->dma->chan_base; - } - } - - /*calculate CTL_HI*/ - ctl_hi.ctlx.reser = 0; - ctl_hi.ctlx.done = 0; - width = mids->dma_slave.src_addr_width; - - ctl_hi.ctlx.block_ts = get_block_ts(len, width, midc->dma->block_size); - pr_debug("MDMA:calc len %d for block size %d\n", - ctl_hi.ctlx.block_ts, midc->dma->block_size); - /*calculate CTL_LO*/ - ctl_lo.ctl_lo = 0; - ctl_lo.ctlx.int_en = 1; - ctl_lo.ctlx.dst_msize = mids->dma_slave.src_maxburst; - ctl_lo.ctlx.src_msize = mids->dma_slave.dst_maxburst; - - /* - * Here we need some translation from "enum dma_slave_buswidth" - * to the format for our dma controller - * standard intel_mid_dmac's format - * 1 Byte 0b000 - * 2 Bytes 0b001 - * 4 Bytes 0b010 - */ - ctl_lo.ctlx.dst_tr_width = mids->dma_slave.dst_addr_width / 2; - ctl_lo.ctlx.src_tr_width = mids->dma_slave.src_addr_width / 2; - - if (mids->cfg_mode == LNW_DMA_MEM_TO_MEM) { - ctl_lo.ctlx.tt_fc = 0; - ctl_lo.ctlx.sinc = 0; - ctl_lo.ctlx.dinc = 0; - } else { - if (mids->dma_slave.direction == DMA_MEM_TO_DEV) { - ctl_lo.ctlx.sinc = 0; - ctl_lo.ctlx.dinc = 2; - ctl_lo.ctlx.tt_fc = 1; - } else if (mids->dma_slave.direction == DMA_DEV_TO_MEM) { - ctl_lo.ctlx.sinc = 2; - ctl_lo.ctlx.dinc = 0; - ctl_lo.ctlx.tt_fc = 2; - } - } - - pr_debug("MDMA:Calc CTL LO %x, CTL HI %x, CFG LO %x, CFG HI %x\n", - ctl_lo.ctl_lo, ctl_hi.ctl_hi, cfg_lo.cfg_lo, cfg_hi.cfg_hi); - - enable_dma_interrupt(midc); - - desc = midc_desc_get(midc); - if (desc == NULL) - goto err_desc_get; - desc->sar = src; - desc->dar = dest ; - desc->len = len; - desc->cfg_hi = cfg_hi.cfg_hi; - desc->cfg_lo = cfg_lo.cfg_lo; - desc->ctl_lo = ctl_lo.ctl_lo; - desc->ctl_hi = ctl_hi.ctl_hi; - desc->width = width; - desc->dirn = mids->dma_slave.direction; - desc->lli_phys = 0; - desc->lli = NULL; - desc->lli_pool = NULL; - return &desc->txd; - -err_desc_get: - pr_err("ERR_MDMA: Failed to get desc\n"); - midc_desc_put(midc, desc); - return NULL; -} -/** - * intel_mid_dma_prep_slave_sg - Prep slave sg txn - * @chan: chan for DMA transfer - * @sgl: scatter gather list - * @sg_len: length of sg txn - * @direction: DMA transfer dirtn - * @flags: DMA flags - * @context: transfer context (ignored) - * - * Prepares LLI based periphral transfer - */ -static struct dma_async_tx_descriptor *intel_mid_dma_prep_slave_sg( - struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long flags, void *context) -{ - struct intel_mid_dma_chan *midc = NULL; - struct intel_mid_dma_slave *mids = NULL; - struct intel_mid_dma_desc *desc = NULL; - struct dma_async_tx_descriptor *txd = NULL; - union intel_mid_dma_ctl_lo ctl_lo; - - pr_debug("MDMA: Prep for slave SG\n"); - - if (!sg_len) { - pr_err("MDMA: Invalid SG length\n"); - return NULL; - } - midc = to_intel_mid_dma_chan(chan); - BUG_ON(!midc); - - mids = midc->mid_slave; - BUG_ON(!mids); - - if (!midc->dma->pimr_mask) { - /* We can still handle sg list with only one item */ - if (sg_len == 1) { - txd = intel_mid_dma_prep_memcpy(chan, - mids->dma_slave.dst_addr, - mids->dma_slave.src_addr, - sg_dma_len(sgl), - flags); - return txd; - } else { - pr_warn("MDMA: SG list is not supported by this controller\n"); - return NULL; - } - } - - pr_debug("MDMA: SG Length = %d, direction = %d, Flags = %#lx\n", - sg_len, direction, flags); - - txd = intel_mid_dma_prep_memcpy(chan, 0, 0, sg_dma_len(sgl), flags); - if (NULL == txd) { - pr_err("MDMA: Prep memcpy failed\n"); - return NULL; - } - - desc = to_intel_mid_dma_desc(txd); - desc->dirn = direction; - ctl_lo.ctl_lo = desc->ctl_lo; - ctl_lo.ctlx.llp_dst_en = 1; - ctl_lo.ctlx.llp_src_en = 1; - desc->ctl_lo = ctl_lo.ctl_lo; - desc->lli_length = sg_len; - desc->current_lli = 0; - /* DMA coherent memory pool for LLI descriptors*/ - desc->lli_pool = pci_pool_create("intel_mid_dma_lli_pool", - midc->dma->pdev, - (sizeof(struct intel_mid_dma_lli)*sg_len), - 32, 0); - if (NULL == desc->lli_pool) { - pr_err("MID_DMA:LLI pool create failed\n"); - return NULL; - } - - desc->lli = pci_pool_alloc(desc->lli_pool, GFP_KERNEL, &desc->lli_phys); - if (!desc->lli) { - pr_err("MID_DMA: LLI alloc failed\n"); - pci_pool_destroy(desc->lli_pool); - return NULL; - } - - midc_lli_fill_sg(midc, desc, sgl, sg_len, flags); - if (flags & DMA_PREP_INTERRUPT) { - iowrite32(UNMASK_INTR_REG(midc->ch_id), - midc->dma_base + MASK_BLOCK); - pr_debug("MDMA:Enabled Block interrupt\n"); - } - return &desc->txd; -} - -/** - * intel_mid_dma_free_chan_resources - Frees dma resources - * @chan: chan requiring attention - * - * Frees the allocated resources on this DMA chan - */ -static void intel_mid_dma_free_chan_resources(struct dma_chan *chan) -{ - struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(chan); - struct middma_device *mid = to_middma_device(chan->device); - struct intel_mid_dma_desc *desc, *_desc; - - if (true == midc->busy) { - /*trying to free ch in use!!!!!*/ - pr_err("ERR_MDMA: trying to free ch in use\n"); - } - spin_lock_bh(&midc->lock); - midc->descs_allocated = 0; - list_for_each_entry_safe(desc, _desc, &midc->active_list, desc_node) { - list_del(&desc->desc_node); - pci_pool_free(mid->dma_pool, desc, desc->txd.phys); - } - list_for_each_entry_safe(desc, _desc, &midc->free_list, desc_node) { - list_del(&desc->desc_node); - pci_pool_free(mid->dma_pool, desc, desc->txd.phys); - } - list_for_each_entry_safe(desc, _desc, &midc->queue, desc_node) { - list_del(&desc->desc_node); - pci_pool_free(mid->dma_pool, desc, desc->txd.phys); - } - spin_unlock_bh(&midc->lock); - midc->in_use = false; - midc->busy = false; - /* Disable CH interrupts */ - iowrite32(MASK_INTR_REG(midc->ch_id), mid->dma_base + MASK_BLOCK); - iowrite32(MASK_INTR_REG(midc->ch_id), mid->dma_base + MASK_ERR); - pm_runtime_put(&mid->pdev->dev); -} - -/** - * intel_mid_dma_alloc_chan_resources - Allocate dma resources - * @chan: chan requiring attention - * - * Allocates DMA resources on this chan - * Return the descriptors allocated - */ -static int intel_mid_dma_alloc_chan_resources(struct dma_chan *chan) -{ - struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(chan); - struct middma_device *mid = to_middma_device(chan->device); - struct intel_mid_dma_desc *desc; - dma_addr_t phys; - int i = 0; - - pm_runtime_get_sync(&mid->pdev->dev); - - if (mid->state == SUSPENDED) { - if (dma_resume(&mid->pdev->dev)) { - pr_err("ERR_MDMA: resume failed"); - return -EFAULT; - } - } - - /* ASSERT: channel is idle */ - if (test_ch_en(mid->dma_base, midc->ch_id)) { - /*ch is not idle*/ - pr_err("ERR_MDMA: ch not idle\n"); - pm_runtime_put(&mid->pdev->dev); - return -EIO; - } - dma_cookie_init(chan); - - spin_lock_bh(&midc->lock); - while (midc->descs_allocated < DESCS_PER_CHANNEL) { - spin_unlock_bh(&midc->lock); - desc = pci_pool_alloc(mid->dma_pool, GFP_KERNEL, &phys); - if (!desc) { - pr_err("ERR_MDMA: desc failed\n"); - pm_runtime_put(&mid->pdev->dev); - return -ENOMEM; - /*check*/ - } - dma_async_tx_descriptor_init(&desc->txd, chan); - desc->txd.tx_submit = intel_mid_dma_tx_submit; - desc->txd.flags = DMA_CTRL_ACK; - desc->txd.phys = phys; - spin_lock_bh(&midc->lock); - i = ++midc->descs_allocated; - list_add_tail(&desc->desc_node, &midc->free_list); - } - spin_unlock_bh(&midc->lock); - midc->in_use = true; - midc->busy = false; - pr_debug("MID_DMA: Desc alloc done ret: %d desc\n", i); - return i; -} - -/** - * midc_handle_error - Handle DMA txn error - * @mid: controller where error occurred - * @midc: chan where error occurred - * - * Scan the descriptor for error - */ -static void midc_handle_error(struct middma_device *mid, - struct intel_mid_dma_chan *midc) -{ - midc_scan_descriptors(mid, midc); -} - -/** - * dma_tasklet - DMA interrupt tasklet - * @data: tasklet arg (the controller structure) - * - * Scan the controller for interrupts for completion/error - * Clear the interrupt and call for handling completion/error - */ -static void dma_tasklet(unsigned long data) -{ - struct middma_device *mid = NULL; - struct intel_mid_dma_chan *midc = NULL; - u32 status, raw_tfr, raw_block; - int i; - - mid = (struct middma_device *)data; - if (mid == NULL) { - pr_err("ERR_MDMA: tasklet Null param\n"); - return; - } - pr_debug("MDMA: in tasklet for device %x\n", mid->pci_id); - raw_tfr = ioread32(mid->dma_base + RAW_TFR); - raw_block = ioread32(mid->dma_base + RAW_BLOCK); - status = raw_tfr | raw_block; - status &= mid->intr_mask; - while (status) { - /*txn interrupt*/ - i = get_ch_index(&status, mid->chan_base); - if (i < 0) { - pr_err("ERR_MDMA:Invalid ch index %x\n", i); - return; - } - midc = &mid->ch[i]; - if (midc == NULL) { - pr_err("ERR_MDMA:Null param midc\n"); - return; - } - pr_debug("MDMA:Tx complete interrupt %x, Ch No %d Index %d\n", - status, midc->ch_id, i); - midc->raw_tfr = raw_tfr; - midc->raw_block = raw_block; - spin_lock_bh(&midc->lock); - /*clearing this interrupts first*/ - iowrite32((1 << midc->ch_id), mid->dma_base + CLEAR_TFR); - if (raw_block) { - iowrite32((1 << midc->ch_id), - mid->dma_base + CLEAR_BLOCK); - } - midc_scan_descriptors(mid, midc); - pr_debug("MDMA:Scan of desc... complete, unmasking\n"); - iowrite32(UNMASK_INTR_REG(midc->ch_id), - mid->dma_base + MASK_TFR); - if (raw_block) { - iowrite32(UNMASK_INTR_REG(midc->ch_id), - mid->dma_base + MASK_BLOCK); - } - spin_unlock_bh(&midc->lock); - } - - status = ioread32(mid->dma_base + RAW_ERR); - status &= mid->intr_mask; - while (status) { - /*err interrupt*/ - i = get_ch_index(&status, mid->chan_base); - if (i < 0) { - pr_err("ERR_MDMA:Invalid ch index %x\n", i); - return; - } - midc = &mid->ch[i]; - if (midc == NULL) { - pr_err("ERR_MDMA:Null param midc\n"); - return; - } - pr_debug("MDMA:Tx complete interrupt %x, Ch No %d Index %d\n", - status, midc->ch_id, i); - - iowrite32((1 << midc->ch_id), mid->dma_base + CLEAR_ERR); - spin_lock_bh(&midc->lock); - midc_handle_error(mid, midc); - iowrite32(UNMASK_INTR_REG(midc->ch_id), - mid->dma_base + MASK_ERR); - spin_unlock_bh(&midc->lock); - } - pr_debug("MDMA:Exiting takslet...\n"); - return; -} - -static void dma_tasklet1(unsigned long data) -{ - pr_debug("MDMA:in takslet1...\n"); - return dma_tasklet(data); -} - -static void dma_tasklet2(unsigned long data) -{ - pr_debug("MDMA:in takslet2...\n"); - return dma_tasklet(data); -} - -/** - * intel_mid_dma_interrupt - DMA ISR - * @irq: IRQ where interrupt occurred - * @data: ISR cllback data (the controller structure) - * - * See if this is our interrupt if so then schedule the tasklet - * otherwise ignore - */ -static irqreturn_t intel_mid_dma_interrupt(int irq, void *data) -{ - struct middma_device *mid = data; - u32 tfr_status, err_status; - int call_tasklet = 0; - - tfr_status = ioread32(mid->dma_base + RAW_TFR); - err_status = ioread32(mid->dma_base + RAW_ERR); - if (!tfr_status && !err_status) - return IRQ_NONE; - - /*DMA Interrupt*/ - pr_debug("MDMA:Got an interrupt on irq %d\n", irq); - pr_debug("MDMA: Status %x, Mask %x\n", tfr_status, mid->intr_mask); - tfr_status &= mid->intr_mask; - if (tfr_status) { - /*need to disable intr*/ - iowrite32((tfr_status << INT_MASK_WE), mid->dma_base + MASK_TFR); - iowrite32((tfr_status << INT_MASK_WE), mid->dma_base + MASK_BLOCK); - pr_debug("MDMA: Calling tasklet %x\n", tfr_status); - call_tasklet = 1; - } - err_status &= mid->intr_mask; - if (err_status) { - iowrite32((err_status << INT_MASK_WE), - mid->dma_base + MASK_ERR); - call_tasklet = 1; - } - if (call_tasklet) - tasklet_schedule(&mid->tasklet); - - return IRQ_HANDLED; -} - -static irqreturn_t intel_mid_dma_interrupt1(int irq, void *data) -{ - return intel_mid_dma_interrupt(irq, data); -} - -static irqreturn_t intel_mid_dma_interrupt2(int irq, void *data) -{ - return intel_mid_dma_interrupt(irq, data); -} - -/** - * mid_setup_dma - Setup the DMA controller - * @pdev: Controller PCI device structure - * - * Initialize the DMA controller, channels, registers with DMA engine, - * ISR. Initialize DMA controller channels. - */ -static int mid_setup_dma(struct pci_dev *pdev) -{ - struct middma_device *dma = pci_get_drvdata(pdev); - int err, i; - - /* DMA coherent memory pool for DMA descriptor allocations */ - dma->dma_pool = pci_pool_create("intel_mid_dma_desc_pool", pdev, - sizeof(struct intel_mid_dma_desc), - 32, 0); - if (NULL == dma->dma_pool) { - pr_err("ERR_MDMA:pci_pool_create failed\n"); - err = -ENOMEM; - goto err_dma_pool; - } - - INIT_LIST_HEAD(&dma->common.channels); - dma->pci_id = pdev->device; - if (dma->pimr_mask) { - dma->mask_reg = ioremap(LNW_PERIPHRAL_MASK_BASE, - LNW_PERIPHRAL_MASK_SIZE); - if (dma->mask_reg == NULL) { - pr_err("ERR_MDMA:Can't map periphral intr space !!\n"); - err = -ENOMEM; - goto err_ioremap; - } - } else - dma->mask_reg = NULL; - - pr_debug("MDMA:Adding %d channel for this controller\n", dma->max_chan); - /*init CH structures*/ - dma->intr_mask = 0; - dma->state = RUNNING; - for (i = 0; i < dma->max_chan; i++) { - struct intel_mid_dma_chan *midch = &dma->ch[i]; - - midch->chan.device = &dma->common; - dma_cookie_init(&midch->chan); - midch->ch_id = dma->chan_base + i; - pr_debug("MDMA:Init CH %d, ID %d\n", i, midch->ch_id); - - midch->dma_base = dma->dma_base; - midch->ch_regs = dma->dma_base + DMA_CH_SIZE * midch->ch_id; - midch->dma = dma; - dma->intr_mask |= 1 << (dma->chan_base + i); - spin_lock_init(&midch->lock); - - INIT_LIST_HEAD(&midch->active_list); - INIT_LIST_HEAD(&midch->queue); - INIT_LIST_HEAD(&midch->free_list); - /*mask interrupts*/ - iowrite32(MASK_INTR_REG(midch->ch_id), - dma->dma_base + MASK_BLOCK); - iowrite32(MASK_INTR_REG(midch->ch_id), - dma->dma_base + MASK_SRC_TRAN); - iowrite32(MASK_INTR_REG(midch->ch_id), - dma->dma_base + MASK_DST_TRAN); - iowrite32(MASK_INTR_REG(midch->ch_id), - dma->dma_base + MASK_ERR); - iowrite32(MASK_INTR_REG(midch->ch_id), - dma->dma_base + MASK_TFR); - - disable_dma_interrupt(midch); - list_add_tail(&midch->chan.device_node, &dma->common.channels); - } - pr_debug("MDMA: Calc Mask as %x for this controller\n", dma->intr_mask); - - /*init dma structure*/ - dma_cap_zero(dma->common.cap_mask); - dma_cap_set(DMA_MEMCPY, dma->common.cap_mask); - dma_cap_set(DMA_SLAVE, dma->common.cap_mask); - dma_cap_set(DMA_PRIVATE, dma->common.cap_mask); - dma->common.dev = &pdev->dev; - - dma->common.device_alloc_chan_resources = - intel_mid_dma_alloc_chan_resources; - dma->common.device_free_chan_resources = - intel_mid_dma_free_chan_resources; - - dma->common.device_tx_status = intel_mid_dma_tx_status; - dma->common.device_prep_dma_memcpy = intel_mid_dma_prep_memcpy; - dma->common.device_issue_pending = intel_mid_dma_issue_pending; - dma->common.device_prep_slave_sg = intel_mid_dma_prep_slave_sg; - dma->common.device_config = intel_mid_dma_config; - dma->common.device_terminate_all = intel_mid_dma_terminate_all; - - /*enable dma cntrl*/ - iowrite32(REG_BIT0, dma->dma_base + DMA_CFG); - - /*register irq */ - if (dma->pimr_mask) { - pr_debug("MDMA:Requesting irq shared for DMAC1\n"); - err = request_irq(pdev->irq, intel_mid_dma_interrupt1, - IRQF_SHARED, "INTEL_MID_DMAC1", dma); - if (0 != err) - goto err_irq; - } else { - dma->intr_mask = 0x03; - pr_debug("MDMA:Requesting irq for DMAC2\n"); - err = request_irq(pdev->irq, intel_mid_dma_interrupt2, - IRQF_SHARED, "INTEL_MID_DMAC2", dma); - if (0 != err) - goto err_irq; - } - /*register device w/ engine*/ - err = dma_async_device_register(&dma->common); - if (0 != err) { - pr_err("ERR_MDMA:device_register failed: %d\n", err); - goto err_engine; - } - if (dma->pimr_mask) { - pr_debug("setting up tasklet1 for DMAC1\n"); - tasklet_init(&dma->tasklet, dma_tasklet1, (unsigned long)dma); - } else { - pr_debug("setting up tasklet2 for DMAC2\n"); - tasklet_init(&dma->tasklet, dma_tasklet2, (unsigned long)dma); - } - return 0; - -err_engine: - free_irq(pdev->irq, dma); -err_irq: - if (dma->mask_reg) - iounmap(dma->mask_reg); -err_ioremap: - pci_pool_destroy(dma->dma_pool); -err_dma_pool: - pr_err("ERR_MDMA:setup_dma failed: %d\n", err); - return err; - -} - -/** - * middma_shutdown - Shutdown the DMA controller - * @pdev: Controller PCI device structure - * - * Called by remove - * Unregister DMa controller, clear all structures and free interrupt - */ -static void middma_shutdown(struct pci_dev *pdev) -{ - struct middma_device *device = pci_get_drvdata(pdev); - - dma_async_device_unregister(&device->common); - pci_pool_destroy(device->dma_pool); - if (device->mask_reg) - iounmap(device->mask_reg); - if (device->dma_base) - iounmap(device->dma_base); - free_irq(pdev->irq, device); - return; -} - -/** - * intel_mid_dma_probe - PCI Probe - * @pdev: Controller PCI device structure - * @id: pci device id structure - * - * Initialize the PCI device, map BARs, query driver data. - * Call setup_dma to complete contoller and chan initilzation - */ -static int intel_mid_dma_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - struct middma_device *device; - u32 base_addr, bar_size; - struct intel_mid_dma_probe_info *info; - int err; - - pr_debug("MDMA: probe for %x\n", pdev->device); - info = (void *)id->driver_data; - pr_debug("MDMA: CH %d, base %d, block len %d, Periphral mask %x\n", - info->max_chan, info->ch_base, - info->block_size, info->pimr_mask); - - err = pci_enable_device(pdev); - if (err) - goto err_enable_device; - - err = pci_request_regions(pdev, "intel_mid_dmac"); - if (err) - goto err_request_regions; - - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (err) - goto err_set_dma_mask; - - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); - if (err) - goto err_set_dma_mask; - - device = kzalloc(sizeof(*device), GFP_KERNEL); - if (!device) { - pr_err("ERR_MDMA:kzalloc failed probe\n"); - err = -ENOMEM; - goto err_kzalloc; - } - device->pdev = pci_dev_get(pdev); - - base_addr = pci_resource_start(pdev, 0); - bar_size = pci_resource_len(pdev, 0); - device->dma_base = ioremap_nocache(base_addr, DMA_REG_SIZE); - if (!device->dma_base) { - pr_err("ERR_MDMA:ioremap failed\n"); - err = -ENOMEM; - goto err_ioremap; - } - pci_set_drvdata(pdev, device); - pci_set_master(pdev); - device->max_chan = info->max_chan; - device->chan_base = info->ch_base; - device->block_size = info->block_size; - device->pimr_mask = info->pimr_mask; - - err = mid_setup_dma(pdev); - if (err) - goto err_dma; - - pm_runtime_put_noidle(&pdev->dev); - pm_runtime_allow(&pdev->dev); - return 0; - -err_dma: - iounmap(device->dma_base); -err_ioremap: - pci_dev_put(pdev); - kfree(device); -err_kzalloc: -err_set_dma_mask: - pci_release_regions(pdev); - pci_disable_device(pdev); -err_request_regions: -err_enable_device: - pr_err("ERR_MDMA:Probe failed %d\n", err); - return err; -} - -/** - * intel_mid_dma_remove - PCI remove - * @pdev: Controller PCI device structure - * - * Free up all resources and data - * Call shutdown_dma to complete contoller and chan cleanup - */ -static void intel_mid_dma_remove(struct pci_dev *pdev) -{ - struct middma_device *device = pci_get_drvdata(pdev); - - pm_runtime_get_noresume(&pdev->dev); - pm_runtime_forbid(&pdev->dev); - middma_shutdown(pdev); - pci_dev_put(pdev); - kfree(device); - pci_release_regions(pdev); - pci_disable_device(pdev); -} - -/* Power Management */ -/* -* dma_suspend - PCI suspend function -* -* @pci: PCI device structure -* @state: PM message -* -* This function is called by OS when a power event occurs -*/ -static int dma_suspend(struct device *dev) -{ - struct pci_dev *pci = to_pci_dev(dev); - int i; - struct middma_device *device = pci_get_drvdata(pci); - pr_debug("MDMA: dma_suspend called\n"); - - for (i = 0; i < device->max_chan; i++) { - if (device->ch[i].in_use) - return -EAGAIN; - } - dmac1_mask_periphral_intr(device); - device->state = SUSPENDED; - pci_save_state(pci); - pci_disable_device(pci); - pci_set_power_state(pci, PCI_D3hot); - return 0; -} - -/** -* dma_resume - PCI resume function -* -* @pci: PCI device structure -* -* This function is called by OS when a power event occurs -*/ -int dma_resume(struct device *dev) -{ - struct pci_dev *pci = to_pci_dev(dev); - int ret; - struct middma_device *device = pci_get_drvdata(pci); - - pr_debug("MDMA: dma_resume called\n"); - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - ret = pci_enable_device(pci); - if (ret) { - pr_err("MDMA: device can't be enabled for %x\n", pci->device); - return ret; - } - device->state = RUNNING; - iowrite32(REG_BIT0, device->dma_base + DMA_CFG); - return 0; -} - -static int dma_runtime_suspend(struct device *dev) -{ - struct pci_dev *pci_dev = to_pci_dev(dev); - struct middma_device *device = pci_get_drvdata(pci_dev); - - device->state = SUSPENDED; - return 0; -} - -static int dma_runtime_resume(struct device *dev) -{ - struct pci_dev *pci_dev = to_pci_dev(dev); - struct middma_device *device = pci_get_drvdata(pci_dev); - - device->state = RUNNING; - iowrite32(REG_BIT0, device->dma_base + DMA_CFG); - return 0; -} - -static int dma_runtime_idle(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct middma_device *device = pci_get_drvdata(pdev); - int i; - - for (i = 0; i < device->max_chan; i++) { - if (device->ch[i].in_use) - return -EAGAIN; - } - - return 0; -} - -/****************************************************************************** -* PCI stuff -*/ -static struct pci_device_id intel_mid_dma_ids[] = { - { PCI_VDEVICE(INTEL, INTEL_MID_DMAC1_ID), INFO(2, 6, 4095, 0x200020)}, - { PCI_VDEVICE(INTEL, INTEL_MID_DMAC2_ID), INFO(2, 0, 2047, 0)}, - { PCI_VDEVICE(INTEL, INTEL_MID_GP_DMAC2_ID), INFO(2, 0, 2047, 0)}, - { PCI_VDEVICE(INTEL, INTEL_MFLD_DMAC1_ID), INFO(4, 0, 4095, 0x400040)}, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, intel_mid_dma_ids); - -static const struct dev_pm_ops intel_mid_dma_pm = { - .runtime_suspend = dma_runtime_suspend, - .runtime_resume = dma_runtime_resume, - .runtime_idle = dma_runtime_idle, - .suspend = dma_suspend, - .resume = dma_resume, -}; - -static struct pci_driver intel_mid_dma_pci_driver = { - .name = "Intel MID DMA", - .id_table = intel_mid_dma_ids, - .probe = intel_mid_dma_probe, - .remove = intel_mid_dma_remove, -#ifdef CONFIG_PM - .driver = { - .pm = &intel_mid_dma_pm, - }, -#endif -}; - -static int __init intel_mid_dma_init(void) -{ - pr_debug("INFO_MDMA: LNW DMA Driver Version %s\n", - INTEL_MID_DMA_DRIVER_VERSION); - return pci_register_driver(&intel_mid_dma_pci_driver); -} -fs_initcall(intel_mid_dma_init); - -static void __exit intel_mid_dma_exit(void) -{ - pci_unregister_driver(&intel_mid_dma_pci_driver); -} -module_exit(intel_mid_dma_exit); - -MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); -MODULE_DESCRIPTION("Intel (R) MID DMAC Driver"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(INTEL_MID_DMA_DRIVER_VERSION); diff --git a/drivers/dma/intel_mid_dma_regs.h b/drivers/dma/intel_mid_dma_regs.h deleted file mode 100644 index 17b4219..0000000 --- a/drivers/dma/intel_mid_dma_regs.h +++ /dev/null @@ -1,299 +0,0 @@ -/* - * intel_mid_dma_regs.h - Intel MID DMA Drivers - * - * Copyright (C) 2008-10 Intel Corp - * Author: Vinod Koul <vinod.koul@intel.com> - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * - */ -#ifndef __INTEL_MID_DMAC_REGS_H__ -#define __INTEL_MID_DMAC_REGS_H__ - -#include <linux/dmaengine.h> -#include <linux/dmapool.h> -#include <linux/pci_ids.h> - -#define INTEL_MID_DMA_DRIVER_VERSION "1.1.0" - -#define REG_BIT0 0x00000001 -#define REG_BIT8 0x00000100 -#define INT_MASK_WE 0x8 -#define CLEAR_DONE 0xFFFFEFFF -#define UNMASK_INTR_REG(chan_num) \ - ((REG_BIT0 << chan_num) | (REG_BIT8 << chan_num)) -#define MASK_INTR_REG(chan_num) (REG_BIT8 << chan_num) - -#define ENABLE_CHANNEL(chan_num) \ - ((REG_BIT0 << chan_num) | (REG_BIT8 << chan_num)) - -#define DISABLE_CHANNEL(chan_num) \ - (REG_BIT8 << chan_num) - -#define DESCS_PER_CHANNEL 16 -/*DMA Registers*/ -/*registers associated with channel programming*/ -#define DMA_REG_SIZE 0x400 -#define DMA_CH_SIZE 0x58 - -/*CH X REG = (DMA_CH_SIZE)*CH_NO + REG*/ -#define SAR 0x00 /* Source Address Register*/ -#define DAR 0x08 /* Destination Address Register*/ -#define LLP 0x10 /* Linked List Pointer Register*/ -#define CTL_LOW 0x18 /* Control Register*/ -#define CTL_HIGH 0x1C /* Control Register*/ -#define CFG_LOW 0x40 /* Configuration Register Low*/ -#define CFG_HIGH 0x44 /* Configuration Register high*/ - -#define STATUS_TFR 0x2E8 -#define STATUS_BLOCK 0x2F0 -#define STATUS_ERR 0x308 - -#define RAW_TFR 0x2C0 -#define RAW_BLOCK 0x2C8 -#define RAW_ERR 0x2E0 - -#define MASK_TFR 0x310 -#define MASK_BLOCK 0x318 -#define MASK_SRC_TRAN 0x320 -#define MASK_DST_TRAN 0x328 -#define MASK_ERR 0x330 - -#define CLEAR_TFR 0x338 -#define CLEAR_BLOCK 0x340 -#define CLEAR_SRC_TRAN 0x348 -#define CLEAR_DST_TRAN 0x350 -#define CLEAR_ERR 0x358 - -#define INTR_STATUS 0x360 -#define DMA_CFG 0x398 -#define DMA_CHAN_EN 0x3A0 - -/*DMA channel control registers*/ -union intel_mid_dma_ctl_lo { - struct { - u32 int_en:1; /*enable or disable interrupts*/ - /*should be 0*/ - u32 dst_tr_width:3; /*destination transfer width*/ - /*usually 32 bits = 010*/ - u32 src_tr_width:3; /*source transfer width*/ - /*usually 32 bits = 010*/ - u32 dinc:2; /*destination address inc/dec*/ - /*For mem:INC=00, Periphral NoINC=11*/ - u32 sinc:2; /*source address inc or dec, as above*/ - u32 dst_msize:3; /*destination burst transaction length*/ - /*always = 16 ie 011*/ - u32 src_msize:3; /*source burst transaction length*/ - /*always = 16 ie 011*/ - u32 reser1:3; - u32 tt_fc:3; /*transfer type and flow controller*/ - /*M-M = 000 - P-M = 010 - M-P = 001*/ - u32 dms:2; /*destination master select = 0*/ - u32 sms:2; /*source master select = 0*/ - u32 llp_dst_en:1; /*enable/disable destination LLP = 0*/ - u32 llp_src_en:1; /*enable/disable source LLP = 0*/ - u32 reser2:3; - } ctlx; - u32 ctl_lo; -}; - -union intel_mid_dma_ctl_hi { - struct { - u32 block_ts:12; /*block transfer size*/ - u32 done:1; /*Done - updated by DMAC*/ - u32 reser:19; /*configured by DMAC*/ - } ctlx; - u32 ctl_hi; - -}; - -/*DMA channel configuration registers*/ -union intel_mid_dma_cfg_lo { - struct { - u32 reser1:5; - u32 ch_prior:3; /*channel priority = 0*/ - u32 ch_susp:1; /*channel suspend = 0*/ - u32 fifo_empty:1; /*FIFO empty or not R bit = 0*/ - u32 hs_sel_dst:1; /*select HW/SW destn handshaking*/ - /*HW = 0, SW = 1*/ - u32 hs_sel_src:1; /*select HW/SW src handshaking*/ - u32 reser2:6; - u32 dst_hs_pol:1; /*dest HS interface polarity*/ - u32 src_hs_pol:1; /*src HS interface polarity*/ - u32 max_abrst:10; /*max AMBA burst len = 0 (no sw limit*/ - u32 reload_src:1; /*auto reload src addr =1 if src is P*/ - u32 reload_dst:1; /*AR destn addr =1 if dstn is P*/ - } cfgx; - u32 cfg_lo; -}; - -union intel_mid_dma_cfg_hi { - struct { - u32 fcmode:1; /*flow control mode = 1*/ - u32 fifo_mode:1; /*FIFO mode select = 1*/ - u32 protctl:3; /*protection control = 0*/ - u32 rsvd:2; - u32 src_per:4; /*src hw HS interface*/ - u32 dst_per:4; /*dstn hw HS interface*/ - u32 reser2:17; - } cfgx; - u32 cfg_hi; -}; - - -/** - * struct intel_mid_dma_chan - internal mid representation of a DMA channel - * @chan: dma_chan strcture represetation for mid chan - * @ch_regs: MMIO register space pointer to channel register - * @dma_base: MMIO register space DMA engine base pointer - * @ch_id: DMA channel id - * @lock: channel spinlock - * @active_list: current active descriptors - * @queue: current queued up descriptors - * @free_list: current free descriptors - * @slave: dma slave structure - * @descs_allocated: total number of descriptors allocated - * @dma: dma device structure pointer - * @busy: bool representing if ch is busy (active txn) or not - * @in_use: bool representing if ch is in use or not - * @raw_tfr: raw trf interrupt received - * @raw_block: raw block interrupt received - */ -struct intel_mid_dma_chan { - struct dma_chan chan; - void __iomem *ch_regs; - void __iomem *dma_base; - int ch_id; - spinlock_t lock; - struct list_head active_list; - struct list_head queue; - struct list_head free_list; - unsigned int descs_allocated; - struct middma_device *dma; - bool busy; - bool in_use; - u32 raw_tfr; - u32 raw_block; - struct intel_mid_dma_slave *mid_slave; -}; - -static inline struct intel_mid_dma_chan *to_intel_mid_dma_chan( - struct dma_chan *chan) -{ - return container_of(chan, struct intel_mid_dma_chan, chan); -} - -enum intel_mid_dma_state { - RUNNING = 0, - SUSPENDED, -}; -/** - * struct middma_device - internal representation of a DMA device - * @pdev: PCI device - * @dma_base: MMIO register space pointer of DMA - * @dma_pool: for allocating DMA descriptors - * @common: embedded struct dma_device - * @tasklet: dma tasklet for processing interrupts - * @ch: per channel data - * @pci_id: DMA device PCI ID - * @intr_mask: Interrupt mask to be used - * @mask_reg: MMIO register for periphral mask - * @chan_base: Base ch index (read from driver data) - * @max_chan: max number of chs supported (from drv_data) - * @block_size: Block size of DMA transfer supported (from drv_data) - * @pimr_mask: MMIO register addr for periphral interrupt (from drv_data) - * @state: dma PM device state - */ -struct middma_device { - struct pci_dev *pdev; - void __iomem *dma_base; - struct pci_pool *dma_pool; - struct dma_device common; - struct tasklet_struct tasklet; - struct intel_mid_dma_chan ch[MAX_CHAN]; - unsigned int pci_id; - unsigned int intr_mask; - void __iomem *mask_reg; - int chan_base; - int max_chan; - int block_size; - unsigned int pimr_mask; - enum intel_mid_dma_state state; -}; - -static inline struct middma_device *to_middma_device(struct dma_device *common) -{ - return container_of(common, struct middma_device, common); -} - -struct intel_mid_dma_desc { - void __iomem *block; /*ch ptr*/ - struct list_head desc_node; - struct dma_async_tx_descriptor txd; - size_t len; - dma_addr_t sar; - dma_addr_t dar; - u32 cfg_hi; - u32 cfg_lo; - u32 ctl_lo; - u32 ctl_hi; - struct pci_pool *lli_pool; - struct intel_mid_dma_lli *lli; - dma_addr_t lli_phys; - unsigned int lli_length; - unsigned int current_lli; - dma_addr_t next; - enum dma_transfer_direction dirn; - enum dma_status status; - enum dma_slave_buswidth width; /*width of DMA txn*/ - enum intel_mid_dma_mode cfg_mode; /*mode configuration*/ - -}; - -struct intel_mid_dma_lli { - dma_addr_t sar; - dma_addr_t dar; - dma_addr_t llp; - u32 ctl_lo; - u32 ctl_hi; -} __attribute__ ((packed)); - -static inline int test_ch_en(void __iomem *dma, u32 ch_no) -{ - u32 en_reg = ioread32(dma + DMA_CHAN_EN); - return (en_reg >> ch_no) & 0x1; -} - -static inline struct intel_mid_dma_desc *to_intel_mid_dma_desc - (struct dma_async_tx_descriptor *txd) -{ - return container_of(txd, struct intel_mid_dma_desc, txd); -} - -static inline struct intel_mid_dma_slave *to_intel_mid_dma_slave - (struct dma_slave_config *slave) -{ - return container_of(slave, struct intel_mid_dma_slave, dma_slave); -} - - -int dma_resume(struct device *dev); - -#endif /*__INTEL_MID_DMAC_REGS_H__*/ diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 77a6dcf..194ec20 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -230,6 +230,10 @@ static bool is_bwd_noraid(struct pci_dev *pdev) switch (pdev->device) { case PCI_DEVICE_ID_INTEL_IOAT_BWD2: case PCI_DEVICE_ID_INTEL_IOAT_BWD3: + case PCI_DEVICE_ID_INTEL_IOAT_BDXDE0: + case PCI_DEVICE_ID_INTEL_IOAT_BDXDE1: + case PCI_DEVICE_ID_INTEL_IOAT_BDXDE2: + case PCI_DEVICE_ID_INTEL_IOAT_BDXDE3: return true; default: return false; diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 8926f27..eb41004 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -219,6 +219,9 @@ static irqreturn_t mmp_pdma_int_handler(int irq, void *dev_id) while (dint) { i = __ffs(dint); + /* only handle interrupts belonging to pdma driver*/ + if (i >= pdev->dma_channels) + break; dint &= (dint - 1); phy = &pdev->phy[i]; ret = mmp_pdma_chan_handler(irq, phy); @@ -999,6 +1002,9 @@ static int mmp_pdma_probe(struct platform_device *op) struct resource *iores; int i, ret, irq = 0; int dma_channels = 0, irq_num = 0; + const enum dma_slave_buswidth widths = + DMA_SLAVE_BUSWIDTH_1_BYTE | DMA_SLAVE_BUSWIDTH_2_BYTES | + DMA_SLAVE_BUSWIDTH_4_BYTES; pdev = devm_kzalloc(&op->dev, sizeof(*pdev), GFP_KERNEL); if (!pdev) @@ -1066,6 +1072,10 @@ static int mmp_pdma_probe(struct platform_device *op) pdev->device.device_config = mmp_pdma_config; pdev->device.device_terminate_all = mmp_pdma_terminate_all; pdev->device.copy_align = PDMA_ALIGNMENT; + pdev->device.src_addr_widths = widths; + pdev->device.dst_addr_widths = widths; + pdev->device.directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM); + pdev->device.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; if (pdev->dev->coherent_dma_mask) dma_set_mask(pdev->dev, pdev->dev->coherent_dma_mask); diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c index 70c2fa9..b6f4e1f 100644 --- a/drivers/dma/mmp_tdma.c +++ b/drivers/dma/mmp_tdma.c @@ -110,7 +110,7 @@ struct mmp_tdma_chan { struct tasklet_struct tasklet; struct mmp_tdma_desc *desc_arr; - phys_addr_t desc_arr_phys; + dma_addr_t desc_arr_phys; int desc_num; enum dma_transfer_direction dir; dma_addr_t dev_addr; @@ -166,9 +166,12 @@ static void mmp_tdma_enable_chan(struct mmp_tdma_chan *tdmac) static int mmp_tdma_disable_chan(struct dma_chan *chan) { struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); + u32 tdcr; - writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN, - tdmac->reg_base + TDCR); + tdcr = readl(tdmac->reg_base + TDCR); + tdcr |= TDCR_ABR; + tdcr &= ~TDCR_CHANEN; + writel(tdcr, tdmac->reg_base + TDCR); tdmac->status = DMA_COMPLETE; @@ -296,12 +299,27 @@ static int mmp_tdma_clear_chan_irq(struct mmp_tdma_chan *tdmac) return -EAGAIN; } +static size_t mmp_tdma_get_pos(struct mmp_tdma_chan *tdmac) +{ + size_t reg; + + if (tdmac->idx == 0) { + reg = __raw_readl(tdmac->reg_base + TDSAR); + reg -= tdmac->desc_arr[0].src_addr; + } else if (tdmac->idx == 1) { + reg = __raw_readl(tdmac->reg_base + TDDAR); + reg -= tdmac->desc_arr[0].dst_addr; + } else + return -EINVAL; + + return reg; +} + static irqreturn_t mmp_tdma_chan_handler(int irq, void *dev_id) { struct mmp_tdma_chan *tdmac = dev_id; if (mmp_tdma_clear_chan_irq(tdmac) == 0) { - tdmac->pos = (tdmac->pos + tdmac->period_len) % tdmac->buf_len; tasklet_schedule(&tdmac->tasklet); return IRQ_HANDLED; } else @@ -343,7 +361,7 @@ static void mmp_tdma_free_descriptor(struct mmp_tdma_chan *tdmac) int size = tdmac->desc_num * sizeof(struct mmp_tdma_desc); gpool = tdmac->pool; - if (tdmac->desc_arr) + if (gpool && tdmac->desc_arr) gen_pool_free(gpool, (unsigned long)tdmac->desc_arr, size); tdmac->desc_arr = NULL; @@ -499,6 +517,7 @@ static enum dma_status mmp_tdma_tx_status(struct dma_chan *chan, { struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); + tdmac->pos = mmp_tdma_get_pos(tdmac); dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie, tdmac->buf_len - tdmac->pos); @@ -610,7 +629,7 @@ static int mmp_tdma_probe(struct platform_device *pdev) int i, ret; int irq = 0, irq_num = 0; int chan_num = TDMA_CHANNEL_NUM; - struct gen_pool *pool; + struct gen_pool *pool = NULL; of_id = of_match_device(mmp_tdma_dt_ids, &pdev->dev); if (of_id) diff --git a/drivers/dma/moxart-dma.c b/drivers/dma/moxart-dma.c index 15cab7d..b463410 100644 --- a/drivers/dma/moxart-dma.c +++ b/drivers/dma/moxart-dma.c @@ -193,8 +193,10 @@ static int moxart_terminate_all(struct dma_chan *chan) spin_lock_irqsave(&ch->vc.lock, flags); - if (ch->desc) + if (ch->desc) { + moxart_dma_desc_free(&ch->desc->vd); ch->desc = NULL; + } ctrl = readl(ch->base + REG_OFF_CTRL); ctrl &= ~(APB_DMA_ENABLE | APB_DMA_FIN_INT_EN | APB_DMA_ERR_INT_EN); diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c index 7dd6dd1..167dbaf 100644 --- a/drivers/dma/omap-dma.c +++ b/drivers/dma/omap-dma.c @@ -981,6 +981,7 @@ static int omap_dma_terminate_all(struct dma_chan *chan) * c->desc is NULL and exit.) */ if (c->desc) { + omap_dma_desc_free(&c->desc->vd); c->desc = NULL; /* Avoid stopping the dma twice */ if (!c->paused) diff --git a/drivers/dma/qcom_bam_dma.c b/drivers/dma/qcom_bam_dma.c index d7a33b3..9c914d6 100644 --- a/drivers/dma/qcom_bam_dma.c +++ b/drivers/dma/qcom_bam_dma.c @@ -162,9 +162,9 @@ static const struct reg_offset_data bam_v1_4_reg_info[] = { [BAM_P_IRQ_STTS] = { 0x1010, 0x1000, 0x00, 0x00 }, [BAM_P_IRQ_CLR] = { 0x1014, 0x1000, 0x00, 0x00 }, [BAM_P_IRQ_EN] = { 0x1018, 0x1000, 0x00, 0x00 }, - [BAM_P_EVNT_DEST_ADDR] = { 0x102C, 0x00, 0x1000, 0x00 }, - [BAM_P_EVNT_REG] = { 0x1018, 0x00, 0x1000, 0x00 }, - [BAM_P_SW_OFSTS] = { 0x1000, 0x00, 0x1000, 0x00 }, + [BAM_P_EVNT_DEST_ADDR] = { 0x182C, 0x00, 0x1000, 0x00 }, + [BAM_P_EVNT_REG] = { 0x1818, 0x00, 0x1000, 0x00 }, + [BAM_P_SW_OFSTS] = { 0x1800, 0x00, 0x1000, 0x00 }, [BAM_P_DATA_FIFO_ADDR] = { 0x1824, 0x00, 0x1000, 0x00 }, [BAM_P_DESC_FIFO_ADDR] = { 0x181C, 0x00, 0x1000, 0x00 }, [BAM_P_EVNT_GEN_TRSHLD] = { 0x1828, 0x00, 0x1000, 0x00 }, @@ -1143,6 +1143,10 @@ static int bam_dma_probe(struct platform_device *pdev) dma_cap_set(DMA_SLAVE, bdev->common.cap_mask); /* initialize dmaengine apis */ + bdev->common.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + bdev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; + bdev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; + bdev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES; bdev->common.device_alloc_chan_resources = bam_alloc_chan; bdev->common.device_free_chan_resources = bam_free_chan; bdev->common.device_prep_slave_sg = bam_prep_slave_sg; diff --git a/drivers/dma/sh/shdmac.c b/drivers/dma/sh/shdmac.c index b2431aa..9f1d4c7 100644 --- a/drivers/dma/sh/shdmac.c +++ b/drivers/dma/sh/shdmac.c @@ -582,15 +582,12 @@ static void sh_dmae_chan_remove(struct sh_dmae_device *shdev) } } -static void sh_dmae_shutdown(struct platform_device *pdev) -{ - struct sh_dmae_device *shdev = platform_get_drvdata(pdev); - sh_dmae_ctl_stop(shdev); -} - #ifdef CONFIG_PM static int sh_dmae_runtime_suspend(struct device *dev) { + struct sh_dmae_device *shdev = dev_get_drvdata(dev); + + sh_dmae_ctl_stop(shdev); return 0; } @@ -605,6 +602,9 @@ static int sh_dmae_runtime_resume(struct device *dev) #ifdef CONFIG_PM_SLEEP static int sh_dmae_suspend(struct device *dev) { + struct sh_dmae_device *shdev = dev_get_drvdata(dev); + + sh_dmae_ctl_stop(shdev); return 0; } @@ -929,13 +929,12 @@ static int sh_dmae_remove(struct platform_device *pdev) } static struct platform_driver sh_dmae_driver = { - .driver = { + .driver = { .pm = &sh_dmae_pm, .name = SH_DMAE_DRV_NAME, .of_match_table = sh_dmae_of_match, }, .remove = sh_dmae_remove, - .shutdown = sh_dmae_shutdown, }; static int __init sh_dmae_init(void) diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 5907c17..92772ff 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -20,8 +20,7 @@ static struct msr __percpu *msrs; */ static atomic_t drv_instances = ATOMIC_INIT(0); -/* Per-node driver instances */ -static struct mem_ctl_info **mcis; +/* Per-node stuff */ static struct ecc_settings **ecc_stngs; /* @@ -903,9 +902,17 @@ static int k8_early_channel_count(struct amd64_pvt *pvt) /* On F10h and later ErrAddr is MC4_ADDR[47:1] */ static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m) { - u64 addr; + u16 mce_nid = amd_get_nb_id(m->extcpu); + struct mem_ctl_info *mci; u8 start_bit = 1; u8 end_bit = 47; + u64 addr; + + mci = edac_mc_find(mce_nid); + if (!mci) + return 0; + + pvt = mci->pvt_info; if (pvt->fam == 0xf) { start_bit = 3; @@ -918,17 +925,13 @@ static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m) * Erratum 637 workaround */ if (pvt->fam == 0x15) { - struct amd64_pvt *pvt; u64 cc6_base, tmp_addr; u32 tmp; - u16 mce_nid; u8 intlv_en; if ((addr & GENMASK_ULL(47, 24)) >> 24 != 0x00fdf7) return addr; - mce_nid = amd_get_nb_id(m->extcpu); - pvt = mcis[mce_nid]->pvt_info; amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp); intlv_en = tmp >> 21 & 0x7; @@ -1511,7 +1514,7 @@ static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct) int cs_found = -EINVAL; int csrow; - mci = mcis[nid]; + mci = edac_mc_find(nid); if (!mci) return cs_found; @@ -2663,34 +2666,6 @@ static bool ecc_enabled(struct pci_dev *F3, u16 nid) return true; } -static int set_mc_sysfs_attrs(struct mem_ctl_info *mci) -{ - struct amd64_pvt *pvt = mci->pvt_info; - int rc; - - rc = amd64_create_sysfs_dbg_files(mci); - if (rc < 0) - return rc; - - if (pvt->fam >= 0x10) { - rc = amd64_create_sysfs_inject_files(mci); - if (rc < 0) - return rc; - } - - return 0; -} - -static void del_mc_sysfs_attrs(struct mem_ctl_info *mci) -{ - struct amd64_pvt *pvt = mci->pvt_info; - - amd64_remove_sysfs_dbg_files(mci); - - if (pvt->fam >= 0x10) - amd64_remove_sysfs_inject_files(mci); -} - static void setup_mci_misc_attrs(struct mem_ctl_info *mci, struct amd64_family_type *fam) { @@ -2778,6 +2753,16 @@ static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt) return fam_type; } +static const struct attribute_group *amd64_edac_attr_groups[] = { +#ifdef CONFIG_EDAC_DEBUG + &amd64_edac_dbg_group, +#endif +#ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION + &amd64_edac_inj_group, +#endif + NULL +}; + static int init_one_instance(struct pci_dev *F2) { struct amd64_pvt *pvt = NULL; @@ -2844,14 +2829,10 @@ static int init_one_instance(struct pci_dev *F2) mci->edac_cap = EDAC_FLAG_NONE; ret = -ENODEV; - if (edac_mc_add_mc(mci)) { + if (edac_mc_add_mc_with_groups(mci, amd64_edac_attr_groups)) { edac_dbg(1, "failed edac_mc_add_mc()\n"); goto err_add_mc; } - if (set_mc_sysfs_attrs(mci)) { - edac_dbg(1, "failed edac_mc_add_mc()\n"); - goto err_add_sysfs; - } /* register stuff with EDAC MCE */ if (report_gart_errors) @@ -2859,14 +2840,10 @@ static int init_one_instance(struct pci_dev *F2) amd_register_ecc_decoder(decode_bus_error); - mcis[nid] = mci; - atomic_inc(&drv_instances); return 0; -err_add_sysfs: - edac_mc_del_mc(mci->pdev); err_add_mc: edac_mc_free(mci); @@ -2940,7 +2917,6 @@ static void remove_one_instance(struct pci_dev *pdev) mci = find_mci_by_dev(&pdev->dev); WARN_ON(!mci); - del_mc_sysfs_attrs(mci); /* Remove from EDAC CORE tracking list */ mci = edac_mc_del_mc(&pdev->dev); if (!mci) @@ -2961,7 +2937,6 @@ static void remove_one_instance(struct pci_dev *pdev) /* Free the EDAC CORE resources */ mci->pvt_info = NULL; - mcis[nid] = NULL; kfree(pvt); edac_mc_free(mci); @@ -2999,7 +2974,7 @@ static void setup_pci_device(void) if (pci_ctl) return; - mci = mcis[0]; + mci = edac_mc_find(0); if (!mci) return; @@ -3023,9 +2998,8 @@ static int __init amd64_edac_init(void) goto err_ret; err = -ENOMEM; - mcis = kzalloc(amd_nb_num() * sizeof(mcis[0]), GFP_KERNEL); ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL); - if (!(mcis && ecc_stngs)) + if (!ecc_stngs) goto err_free; msrs = msrs_alloc(); @@ -3056,9 +3030,6 @@ err_pci: msrs = NULL; err_free: - kfree(mcis); - mcis = NULL; - kfree(ecc_stngs); ecc_stngs = NULL; @@ -3076,9 +3047,6 @@ static void __exit amd64_edac_exit(void) kfree(ecc_stngs); ecc_stngs = NULL; - kfree(mcis); - mcis = NULL; - msrs_free(msrs); msrs = NULL; } diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h index d8468c6..4bdec75 100644 --- a/drivers/edac/amd64_edac.h +++ b/drivers/edac/amd64_edac.h @@ -453,31 +453,11 @@ struct ecc_settings { }; #ifdef CONFIG_EDAC_DEBUG -int amd64_create_sysfs_dbg_files(struct mem_ctl_info *mci); -void amd64_remove_sysfs_dbg_files(struct mem_ctl_info *mci); - -#else -static inline int amd64_create_sysfs_dbg_files(struct mem_ctl_info *mci) -{ - return 0; -} -static void inline amd64_remove_sysfs_dbg_files(struct mem_ctl_info *mci) -{ -} +extern const struct attribute_group amd64_edac_dbg_group; #endif #ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION -int amd64_create_sysfs_inject_files(struct mem_ctl_info *mci); -void amd64_remove_sysfs_inject_files(struct mem_ctl_info *mci); - -#else -static inline int amd64_create_sysfs_inject_files(struct mem_ctl_info *mci) -{ - return 0; -} -static inline void amd64_remove_sysfs_inject_files(struct mem_ctl_info *mci) -{ -} +extern const struct attribute_group amd64_edac_inj_group; #endif /* diff --git a/drivers/edac/amd64_edac_dbg.c b/drivers/edac/amd64_edac_dbg.c index 2c1bbf7..4709c60 100644 --- a/drivers/edac/amd64_edac_dbg.c +++ b/drivers/edac/amd64_edac_dbg.c @@ -40,34 +40,15 @@ static DEVICE_ATTR(topmem, S_IRUGO, amd64_top_mem_show, NULL); static DEVICE_ATTR(topmem2, S_IRUGO, amd64_top_mem2_show, NULL); static DEVICE_ATTR(dram_hole, S_IRUGO, amd64_hole_show, NULL); -int amd64_create_sysfs_dbg_files(struct mem_ctl_info *mci) -{ - int rc; - - rc = device_create_file(&mci->dev, &dev_attr_dhar); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_dbam); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_topmem); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_topmem2); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_dram_hole); - if (rc < 0) - return rc; - - return 0; -} - -void amd64_remove_sysfs_dbg_files(struct mem_ctl_info *mci) -{ - device_remove_file(&mci->dev, &dev_attr_dhar); - device_remove_file(&mci->dev, &dev_attr_dbam); - device_remove_file(&mci->dev, &dev_attr_topmem); - device_remove_file(&mci->dev, &dev_attr_topmem2); - device_remove_file(&mci->dev, &dev_attr_dram_hole); -} +static struct attribute *amd64_edac_dbg_attrs[] = { + &dev_attr_dhar.attr, + &dev_attr_dbam.attr, + &dev_attr_topmem.attr, + &dev_attr_topmem2.attr, + &dev_attr_dram_hole.attr, + NULL +}; + +const struct attribute_group amd64_edac_dbg_group = { + .attrs = amd64_edac_dbg_attrs, +}; diff --git a/drivers/edac/amd64_edac_inj.c b/drivers/edac/amd64_edac_inj.c index 0d66ae6..e14977f 100644 --- a/drivers/edac/amd64_edac_inj.c +++ b/drivers/edac/amd64_edac_inj.c @@ -207,35 +207,28 @@ static DEVICE_ATTR(inject_write, S_IWUSR, static DEVICE_ATTR(inject_read, S_IWUSR, NULL, amd64_inject_read_store); - -int amd64_create_sysfs_inject_files(struct mem_ctl_info *mci) +static struct attribute *amd64_edac_inj_attrs[] = { + &dev_attr_inject_section.attr, + &dev_attr_inject_word.attr, + &dev_attr_inject_ecc_vector.attr, + &dev_attr_inject_write.attr, + &dev_attr_inject_read.attr, + NULL +}; + +static umode_t amd64_edac_inj_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) { - int rc; - - rc = device_create_file(&mci->dev, &dev_attr_inject_section); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_inject_word); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_inject_ecc_vector); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_inject_write); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_inject_read); - if (rc < 0) - return rc; - - return 0; -} + struct device *dev = kobj_to_dev(kobj); + struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); + struct amd64_pvt *pvt = mci->pvt_info; -void amd64_remove_sysfs_inject_files(struct mem_ctl_info *mci) -{ - device_remove_file(&mci->dev, &dev_attr_inject_section); - device_remove_file(&mci->dev, &dev_attr_inject_word); - device_remove_file(&mci->dev, &dev_attr_inject_ecc_vector); - device_remove_file(&mci->dev, &dev_attr_inject_write); - device_remove_file(&mci->dev, &dev_attr_inject_read); + if (pvt->fam < 0x10) + return 0; + return attr->mode; } + +const struct attribute_group amd64_edac_inj_group = { + .attrs = amd64_edac_inj_attrs, + .is_visible = amd64_edac_inj_is_visible, +}; diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index 6c9f381..ad42587 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h @@ -446,7 +446,9 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, unsigned n_layers, struct edac_mc_layer *layers, unsigned sz_pvt); -extern int edac_mc_add_mc(struct mem_ctl_info *mci); +extern int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci, + const struct attribute_group **groups); +#define edac_mc_add_mc(mci) edac_mc_add_mc_with_groups(mci, NULL) extern void edac_mc_free(struct mem_ctl_info *mci); extern struct mem_ctl_info *edac_mc_find(int idx); extern struct mem_ctl_info *find_mci_by_dev(struct device *dev); diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 1747906..af3be19 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -710,9 +710,10 @@ struct mem_ctl_info *edac_mc_find(int idx) EXPORT_SYMBOL(edac_mc_find); /** - * edac_mc_add_mc: Insert the 'mci' structure into the mci global list and - * create sysfs entries associated with mci structure + * edac_mc_add_mc_with_groups: Insert the 'mci' structure into the mci + * global list and create sysfs entries associated with mci structure * @mci: pointer to the mci structure to be added to the list + * @groups: optional attribute groups for the driver-specific sysfs entries * * Return: * 0 Success @@ -720,7 +721,8 @@ EXPORT_SYMBOL(edac_mc_find); */ /* FIXME - should a warning be printed if no error detection? correction? */ -int edac_mc_add_mc(struct mem_ctl_info *mci) +int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci, + const struct attribute_group **groups) { int ret = -EINVAL; edac_dbg(0, "\n"); @@ -771,7 +773,7 @@ int edac_mc_add_mc(struct mem_ctl_info *mci) mci->bus = &mc_bus[mci->mc_idx]; - if (edac_create_sysfs_mci_device(mci)) { + if (edac_create_sysfs_mci_device(mci, groups)) { edac_mc_printk(mci, KERN_WARNING, "failed to create sysfs device\n"); goto fail1; @@ -805,7 +807,7 @@ fail0: mutex_unlock(&mem_ctls_mutex); return ret; } -EXPORT_SYMBOL_GPL(edac_mc_add_mc); +EXPORT_SYMBOL_GPL(edac_mc_add_mc_with_groups); /** * edac_mc_del_mc: Remove sysfs entries for specified mci structure and diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index c84eecb..112d63a 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -323,13 +323,14 @@ DEVICE_CHANNEL(ch5_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 5); /* Total possible dynamic DIMM Label attribute file table */ -static struct device_attribute *dynamic_csrow_dimm_attr[] = { - &dev_attr_legacy_ch0_dimm_label.attr, - &dev_attr_legacy_ch1_dimm_label.attr, - &dev_attr_legacy_ch2_dimm_label.attr, - &dev_attr_legacy_ch3_dimm_label.attr, - &dev_attr_legacy_ch4_dimm_label.attr, - &dev_attr_legacy_ch5_dimm_label.attr +static struct attribute *dynamic_csrow_dimm_attr[] = { + &dev_attr_legacy_ch0_dimm_label.attr.attr, + &dev_attr_legacy_ch1_dimm_label.attr.attr, + &dev_attr_legacy_ch2_dimm_label.attr.attr, + &dev_attr_legacy_ch3_dimm_label.attr.attr, + &dev_attr_legacy_ch4_dimm_label.attr.attr, + &dev_attr_legacy_ch5_dimm_label.attr.attr, + NULL }; /* possible dynamic channel ce_count attribute files */ @@ -347,13 +348,45 @@ DEVICE_CHANNEL(ch5_ce_count, S_IRUGO, channel_ce_count_show, NULL, 5); /* Total possible dynamic ce_count attribute file table */ -static struct device_attribute *dynamic_csrow_ce_count_attr[] = { - &dev_attr_legacy_ch0_ce_count.attr, - &dev_attr_legacy_ch1_ce_count.attr, - &dev_attr_legacy_ch2_ce_count.attr, - &dev_attr_legacy_ch3_ce_count.attr, - &dev_attr_legacy_ch4_ce_count.attr, - &dev_attr_legacy_ch5_ce_count.attr +static struct attribute *dynamic_csrow_ce_count_attr[] = { + &dev_attr_legacy_ch0_ce_count.attr.attr, + &dev_attr_legacy_ch1_ce_count.attr.attr, + &dev_attr_legacy_ch2_ce_count.attr.attr, + &dev_attr_legacy_ch3_ce_count.attr.attr, + &dev_attr_legacy_ch4_ce_count.attr.attr, + &dev_attr_legacy_ch5_ce_count.attr.attr, + NULL +}; + +static umode_t csrow_dev_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct csrow_info *csrow = container_of(dev, struct csrow_info, dev); + + if (idx >= csrow->nr_channels) + return 0; + /* Only expose populated DIMMs */ + if (!csrow->channels[idx]->dimm->nr_pages) + return 0; + return attr->mode; +} + + +static const struct attribute_group csrow_dev_dimm_group = { + .attrs = dynamic_csrow_dimm_attr, + .is_visible = csrow_dev_is_visible, +}; + +static const struct attribute_group csrow_dev_ce_count_group = { + .attrs = dynamic_csrow_ce_count_attr, + .is_visible = csrow_dev_is_visible, +}; + +static const struct attribute_group *csrow_dev_groups[] = { + &csrow_dev_dimm_group, + &csrow_dev_ce_count_group, + NULL }; static inline int nr_pages_per_csrow(struct csrow_info *csrow) @@ -370,13 +403,12 @@ static inline int nr_pages_per_csrow(struct csrow_info *csrow) static int edac_create_csrow_object(struct mem_ctl_info *mci, struct csrow_info *csrow, int index) { - int err, chan; - if (csrow->nr_channels > EDAC_NR_CHANNELS) return -ENODEV; csrow->dev.type = &csrow_attr_type; csrow->dev.bus = mci->bus; + csrow->dev.groups = csrow_dev_groups; device_initialize(&csrow->dev); csrow->dev.parent = &mci->dev; csrow->mci = mci; @@ -386,45 +418,13 @@ static int edac_create_csrow_object(struct mem_ctl_info *mci, edac_dbg(0, "creating (virtual) csrow node %s\n", dev_name(&csrow->dev)); - err = device_add(&csrow->dev); - if (err < 0) - return err; - - for (chan = 0; chan < csrow->nr_channels; chan++) { - /* Only expose populated DIMMs */ - if (!csrow->channels[chan]->dimm->nr_pages) - continue; - err = device_create_file(&csrow->dev, - dynamic_csrow_dimm_attr[chan]); - if (err < 0) - goto error; - err = device_create_file(&csrow->dev, - dynamic_csrow_ce_count_attr[chan]); - if (err < 0) { - device_remove_file(&csrow->dev, - dynamic_csrow_dimm_attr[chan]); - goto error; - } - } - - return 0; - -error: - for (--chan; chan >= 0; chan--) { - device_remove_file(&csrow->dev, - dynamic_csrow_dimm_attr[chan]); - device_remove_file(&csrow->dev, - dynamic_csrow_ce_count_attr[chan]); - } - put_device(&csrow->dev); - - return err; + return device_add(&csrow->dev); } /* Create a CSROW object under specifed edac_mc_device */ static int edac_create_csrow_objects(struct mem_ctl_info *mci) { - int err, i, chan; + int err, i; struct csrow_info *csrow; for (i = 0; i < mci->nr_csrows; i++) { @@ -446,14 +446,6 @@ error: csrow = mci->csrows[i]; if (!nr_pages_per_csrow(csrow)) continue; - for (chan = csrow->nr_channels - 1; chan >= 0; chan--) { - if (!csrow->channels[chan]->dimm->nr_pages) - continue; - device_remove_file(&csrow->dev, - dynamic_csrow_dimm_attr[chan]); - device_remove_file(&csrow->dev, - dynamic_csrow_ce_count_attr[chan]); - } put_device(&mci->csrows[i]->dev); } @@ -462,23 +454,13 @@ error: static void edac_delete_csrow_objects(struct mem_ctl_info *mci) { - int i, chan; + int i; struct csrow_info *csrow; for (i = mci->nr_csrows - 1; i >= 0; i--) { csrow = mci->csrows[i]; if (!nr_pages_per_csrow(csrow)) continue; - for (chan = csrow->nr_channels - 1; chan >= 0; chan--) { - if (!csrow->channels[chan]->dimm->nr_pages) - continue; - edac_dbg(1, "Removing csrow %d channel %d sysfs nodes\n", - i, chan); - device_remove_file(&csrow->dev, - dynamic_csrow_dimm_attr[chan]); - device_remove_file(&csrow->dev, - dynamic_csrow_ce_count_attr[chan]); - } device_unregister(&mci->csrows[i]->dev); } } @@ -863,7 +845,8 @@ static DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); static DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL); /* memory scrubber attribute file */ -static DEVICE_ATTR(sdram_scrub_rate, 0, NULL, NULL); +DEVICE_ATTR(sdram_scrub_rate, 0, mci_sdram_scrub_rate_show, + mci_sdram_scrub_rate_store); /* umode set later in is_visible */ static struct attribute *mci_attrs[] = { &dev_attr_reset_counters.attr, @@ -875,11 +858,29 @@ static struct attribute *mci_attrs[] = { &dev_attr_ue_count.attr, &dev_attr_ce_count.attr, &dev_attr_max_location.attr, + &dev_attr_sdram_scrub_rate.attr, NULL }; +static umode_t mci_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct mem_ctl_info *mci = to_mci(dev); + umode_t mode = 0; + + if (attr != &dev_attr_sdram_scrub_rate.attr) + return attr->mode; + if (mci->get_sdram_scrub_rate) + mode |= S_IRUGO; + if (mci->set_sdram_scrub_rate) + mode |= S_IWUSR; + return mode; +} + static struct attribute_group mci_attr_grp = { .attrs = mci_attrs, + .is_visible = mci_attr_is_visible, }; static const struct attribute_group *mci_attr_groups[] = { @@ -913,7 +914,7 @@ int __init edac_debugfs_init(void) return 0; } -void __exit edac_debugfs_exit(void) +void edac_debugfs_exit(void) { debugfs_remove(edac_debugfs); } @@ -973,7 +974,8 @@ nomem: * 0 Success * !0 Failure */ -int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) +int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, + const struct attribute_group **groups) { int i, err; @@ -997,6 +999,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) mci->dev.parent = mci_pdev; mci->dev.bus = mci->bus; + mci->dev.groups = groups; dev_set_name(&mci->dev, "mc%d", mci->mc_idx); dev_set_drvdata(&mci->dev, mci); pm_runtime_forbid(&mci->dev); @@ -1008,23 +1011,6 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) goto fail_unregister_bus; } - if (mci->set_sdram_scrub_rate || mci->get_sdram_scrub_rate) { - if (mci->get_sdram_scrub_rate) { - dev_attr_sdram_scrub_rate.attr.mode |= S_IRUGO; - dev_attr_sdram_scrub_rate.show = &mci_sdram_scrub_rate_show; - } - - if (mci->set_sdram_scrub_rate) { - dev_attr_sdram_scrub_rate.attr.mode |= S_IWUSR; - dev_attr_sdram_scrub_rate.store = &mci_sdram_scrub_rate_store; - } - - err = device_create_file(&mci->dev, &dev_attr_sdram_scrub_rate); - if (err) { - edac_dbg(1, "failure: create sdram_scrub_rate\n"); - goto fail_unregister_dev; - } - } /* * Create the dimm/rank devices */ @@ -1071,7 +1057,6 @@ fail_unregister_dimm: device_unregister(&dimm->dev); } -fail_unregister_dev: device_unregister(&mci->dev); fail_unregister_bus: bus_unregister(mci->bus); @@ -1170,7 +1155,7 @@ int __init edac_mc_sysfs_init(void) return err; } -void __exit edac_mc_sysfs_exit(void) +void edac_mc_sysfs_exit(void) { device_unregister(mci_pdev); edac_put_sysfs_subsys(); diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c index e6d1691..9cb082a 100644 --- a/drivers/edac/edac_module.c +++ b/drivers/edac/edac_module.c @@ -112,20 +112,23 @@ static int __init edac_init(void) err = edac_mc_sysfs_init(); if (err) - goto error; + goto err_sysfs; edac_debugfs_init(); - /* Setup/Initialize the workq for this core */ err = edac_workqueue_setup(); if (err) { - edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n"); - goto error; + edac_printk(KERN_ERR, EDAC_MC, "Failure initializing workqueue\n"); + goto err_wq; } return 0; -error: +err_wq: + edac_debugfs_exit(); + edac_mc_sysfs_exit(); + +err_sysfs: return err; } diff --git a/drivers/edac/edac_module.h b/drivers/edac/edac_module.h index f2118bf..26ecc52 100644 --- a/drivers/edac/edac_module.h +++ b/drivers/edac/edac_module.h @@ -22,7 +22,8 @@ /* on edac_mc_sysfs.c */ int edac_mc_sysfs_init(void); void edac_mc_sysfs_exit(void); -extern int edac_create_sysfs_mci_device(struct mem_ctl_info *mci); +extern int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, + const struct attribute_group **groups); extern void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci); void edac_unregister_sysfs(struct mem_ctl_info *mci); extern int edac_get_log_ue(void); diff --git a/drivers/edac/highbank_mc_edac.c b/drivers/edac/highbank_mc_edac.c index f784de1..11260cc 100644 --- a/drivers/edac/highbank_mc_edac.c +++ b/drivers/edac/highbank_mc_edac.c @@ -124,6 +124,13 @@ static ssize_t highbank_mc_inject_ctrl(struct device *dev, static DEVICE_ATTR(inject_ctrl, S_IWUSR, NULL, highbank_mc_inject_ctrl); +static struct attribute *highbank_dev_attrs[] = { + &dev_attr_inject_ctrl.attr, + NULL +}; + +ATTRIBUTE_GROUPS(highbank_dev); + struct hb_mc_settings { int err_offset; int int_offset; @@ -139,7 +146,7 @@ static struct hb_mc_settings mw_settings = { .int_offset = MW_DDR_ECC_INT_BASE, }; -static struct of_device_id hb_ddr_ctrl_of_match[] = { +static const struct of_device_id hb_ddr_ctrl_of_match[] = { { .compatible = "calxeda,hb-ddr-ctrl", .data = &hb_settings }, { .compatible = "calxeda,ecx-2000-ddr-ctrl", .data = &mw_settings }, {}, @@ -231,7 +238,7 @@ static int highbank_mc_probe(struct platform_device *pdev) dimm->mtype = MEM_DDR3; dimm->edac_mode = EDAC_SECDED; - res = edac_mc_add_mc(mci); + res = edac_mc_add_mc_with_groups(mci, highbank_dev_groups); if (res < 0) goto err; @@ -243,8 +250,6 @@ static int highbank_mc_probe(struct platform_device *pdev) goto err2; } - device_create_file(&mci->dev, &dev_attr_inject_ctrl); - devres_close_group(&pdev->dev, NULL); return 0; err2: @@ -259,7 +264,6 @@ static int highbank_mc_remove(struct platform_device *pdev) { struct mem_ctl_info *mci = platform_get_drvdata(pdev); - device_remove_file(&mci->dev, &dev_attr_inject_ctrl); edac_mc_del_mc(&pdev->dev); edac_mc_free(mci); return 0; diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 9cd0b30..01087a3 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1157,27 +1157,24 @@ static DEVICE_ATTR(inject_eccmask, S_IRUGO | S_IWUSR, static DEVICE_ATTR(inject_enable, S_IRUGO | S_IWUSR, i7core_inject_enable_show, i7core_inject_enable_store); +static struct attribute *i7core_dev_attrs[] = { + &dev_attr_inject_section.attr, + &dev_attr_inject_type.attr, + &dev_attr_inject_eccmask.attr, + &dev_attr_inject_enable.attr, + NULL +}; + +ATTRIBUTE_GROUPS(i7core_dev); + static int i7core_create_sysfs_devices(struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; int rc; - rc = device_create_file(&mci->dev, &dev_attr_inject_section); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_inject_type); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_inject_eccmask); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_inject_enable); - if (rc < 0) - return rc; - pvt->addrmatch_dev = kzalloc(sizeof(*pvt->addrmatch_dev), GFP_KERNEL); if (!pvt->addrmatch_dev) - return rc; + return -ENOMEM; pvt->addrmatch_dev->type = &addrmatch_type; pvt->addrmatch_dev->bus = mci->dev.bus; @@ -1198,7 +1195,7 @@ static int i7core_create_sysfs_devices(struct mem_ctl_info *mci) if (!pvt->chancounts_dev) { put_device(pvt->addrmatch_dev); device_del(pvt->addrmatch_dev); - return rc; + return -ENOMEM; } pvt->chancounts_dev->type = &all_channel_counts_type; @@ -1223,11 +1220,6 @@ static void i7core_delete_sysfs_devices(struct mem_ctl_info *mci) edac_dbg(1, "\n"); - device_remove_file(&mci->dev, &dev_attr_inject_section); - device_remove_file(&mci->dev, &dev_attr_inject_type); - device_remove_file(&mci->dev, &dev_attr_inject_eccmask); - device_remove_file(&mci->dev, &dev_attr_inject_enable); - if (!pvt->is_registered) { put_device(pvt->chancounts_dev); device_del(pvt->chancounts_dev); @@ -2259,7 +2251,7 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev) enable_sdram_scrub_setting(mci); /* add this new MC control structure to EDAC's list of MCs */ - if (unlikely(edac_mc_add_mc(mci))) { + if (unlikely(edac_mc_add_mc_with_groups(mci, i7core_dev_groups))) { edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); /* FIXME: perhaps some code should go here that disables error * reporting if we just enabled it diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c index b4705d9..4d411036 100644 --- a/drivers/edac/i82443bxgx_edac.c +++ b/drivers/edac/i82443bxgx_edac.c @@ -350,8 +350,6 @@ fail: return -ENODEV; } -EXPORT_SYMBOL_GPL(i82443bxgx_edacmc_probe1); - /* returns count (>= 0), or negative on error */ static int i82443bxgx_edacmc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -384,8 +382,6 @@ static void i82443bxgx_edacmc_remove_one(struct pci_dev *pdev) edac_mc_free(mci); } -EXPORT_SYMBOL_GPL(i82443bxgx_edacmc_remove_one); - static const struct pci_device_id i82443bxgx_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_0)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_2)}, @@ -445,9 +441,7 @@ fail1: pci_unregister_driver(&i82443bxgx_edacmc_driver); fail0: - if (mci_pdev != NULL) - pci_dev_put(mci_pdev); - + pci_dev_put(mci_pdev); return pci_rc; } diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c index 4382343..ee1078c 100644 --- a/drivers/edac/i82860_edac.c +++ b/drivers/edac/i82860_edac.c @@ -343,20 +343,15 @@ fail1: pci_unregister_driver(&i82860_driver); fail0: - if (mci_pdev != NULL) - pci_dev_put(mci_pdev); - + pci_dev_put(mci_pdev); return pci_rc; } static void __exit i82860_exit(void) { edac_dbg(3, "\n"); - pci_unregister_driver(&i82860_driver); - - if (mci_pdev != NULL) - pci_dev_put(mci_pdev); + pci_dev_put(mci_pdev); } module_init(i82860_init); diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index 64b6832..c26a513 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c @@ -576,9 +576,7 @@ fail1: pci_unregister_driver(&i82875p_driver); fail0: - if (mci_pdev != NULL) - pci_dev_put(mci_pdev); - + pci_dev_put(mci_pdev); return pci_rc; } diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c index 10b1052..35ab66c 100644 --- a/drivers/edac/i82975x_edac.c +++ b/drivers/edac/i82975x_edac.c @@ -685,9 +685,7 @@ fail1: pci_unregister_driver(&i82975x_driver); fail0: - if (mci_pdev != NULL) - pci_dev_put(mci_pdev); - + pci_dev_put(mci_pdev); return pci_rc; } diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index 1fa76a5..68bf234 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c @@ -134,29 +134,14 @@ DEVICE_ATTR(inject_data_lo, S_IRUGO | S_IWUSR, DEVICE_ATTR(inject_ctrl, S_IRUGO | S_IWUSR, mpc85xx_mc_inject_ctrl_show, mpc85xx_mc_inject_ctrl_store); -static int mpc85xx_create_sysfs_attributes(struct mem_ctl_info *mci) -{ - int rc; - - rc = device_create_file(&mci->dev, &dev_attr_inject_data_hi); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_inject_data_lo); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_inject_ctrl); - if (rc < 0) - return rc; +static struct attribute *mpc85xx_dev_attrs[] = { + &dev_attr_inject_data_hi.attr, + &dev_attr_inject_data_lo.attr, + &dev_attr_inject_ctrl.attr, + NULL +}; - return 0; -} - -static void mpc85xx_remove_sysfs_attributes(struct mem_ctl_info *mci) -{ - device_remove_file(&mci->dev, &dev_attr_inject_data_hi); - device_remove_file(&mci->dev, &dev_attr_inject_data_lo); - device_remove_file(&mci->dev, &dev_attr_inject_ctrl); -} +ATTRIBUTE_GROUPS(mpc85xx_dev); /**************************** PCI Err device ***************************/ #ifdef CONFIG_PCI @@ -685,7 +670,7 @@ static int mpc85xx_l2_err_remove(struct platform_device *op) return 0; } -static struct of_device_id mpc85xx_l2_err_of_match[] = { +static const struct of_device_id mpc85xx_l2_err_of_match[] = { /* deprecate the fsl,85.. forms in the future, 2.6.30? */ { .compatible = "fsl,8540-l2-cache-controller", }, { .compatible = "fsl,8541-l2-cache-controller", }, @@ -1106,13 +1091,7 @@ static int mpc85xx_mc_err_probe(struct platform_device *op) /* clear all error bits */ out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, ~0); - if (edac_mc_add_mc(mci)) { - edac_dbg(3, "failed edac_mc_add_mc()\n"); - goto err; - } - - if (mpc85xx_create_sysfs_attributes(mci)) { - edac_mc_del_mc(mci->pdev); + if (edac_mc_add_mc_with_groups(mci, mpc85xx_dev_groups)) { edac_dbg(3, "failed edac_mc_add_mc()\n"); goto err; } @@ -1176,13 +1155,12 @@ static int mpc85xx_mc_err_remove(struct platform_device *op) orig_ddr_err_disable); out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_SBE, orig_ddr_err_sbe); - mpc85xx_remove_sysfs_attributes(mci); edac_mc_del_mc(&op->dev); edac_mc_free(mci); return 0; } -static struct of_device_id mpc85xx_mc_err_of_match[] = { +static const struct of_device_id mpc85xx_mc_err_of_match[] = { /* deprecate the fsl,85.. forms in the future, 2.6.30? */ { .compatible = "fsl,8540-memory-controller", }, { .compatible = "fsl,8541-memory-controller", }, diff --git a/drivers/edac/octeon_edac-lmc.c b/drivers/edac/octeon_edac-lmc.c index 4bd10f9..bb19e07 100644 --- a/drivers/edac/octeon_edac-lmc.c +++ b/drivers/edac/octeon_edac-lmc.c @@ -209,35 +209,18 @@ static DEVICE_ATTR(row, S_IRUGO | S_IWUSR, static DEVICE_ATTR(col, S_IRUGO | S_IWUSR, octeon_mc_inject_col_show, octeon_mc_inject_col_store); +static struct attribute *octeon_dev_attrs[] = { + &dev_attr_inject.attr, + &dev_attr_error_type.attr, + &dev_attr_dimm.attr, + &dev_attr_rank.attr, + &dev_attr_bank.attr, + &dev_attr_row.attr, + &dev_attr_col.attr, + NULL +}; -static int octeon_set_mc_sysfs_attributes(struct mem_ctl_info *mci) -{ - int rc; - - rc = device_create_file(&mci->dev, &dev_attr_inject); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_error_type); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_dimm); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_rank); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_bank); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_row); - if (rc < 0) - return rc; - rc = device_create_file(&mci->dev, &dev_attr_col); - if (rc < 0) - return rc; - - return 0; -} +ATTRIBUTE_GROUPS(octeon_dev); static int octeon_lmc_edac_probe(struct platform_device *pdev) { @@ -271,18 +254,12 @@ static int octeon_lmc_edac_probe(struct platform_device *pdev) mci->ctl_name = "octeon-lmc-err"; mci->edac_check = octeon_lmc_edac_poll; - if (edac_mc_add_mc(mci)) { + if (edac_mc_add_mc_with_groups(mci, octeon_dev_groups)) { dev_err(&pdev->dev, "edac_mc_add_mc() failed\n"); edac_mc_free(mci); return -ENXIO; } - if (octeon_set_mc_sysfs_attributes(mci)) { - dev_err(&pdev->dev, "octeon_set_mc_sysfs_attributes() failed\n"); - return -ENXIO; - } - - cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc)); cfg0.s.intr_ded_ena = 0; /* We poll */ cfg0.s.intr_sec_ena = 0; @@ -309,18 +286,12 @@ static int octeon_lmc_edac_probe(struct platform_device *pdev) mci->ctl_name = "co_lmc_err"; mci->edac_check = octeon_lmc_edac_poll_o2; - if (edac_mc_add_mc(mci)) { + if (edac_mc_add_mc_with_groups(mci, octeon_dev_groups)) { dev_err(&pdev->dev, "edac_mc_add_mc() failed\n"); edac_mc_free(mci); return -ENXIO; } - if (octeon_set_mc_sysfs_attributes(mci)) { - dev_err(&pdev->dev, "octeon_set_mc_sysfs_attributes() failed\n"); - return -ENXIO; - } - - en.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc)); en.s.intr_ded_ena = 0; /* We poll */ en.s.intr_sec_ena = 0; diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c index 1b64fd0..3515b381 100644 --- a/drivers/edac/ppc4xx_edac.c +++ b/drivers/edac/ppc4xx_edac.c @@ -193,7 +193,7 @@ static int ppc4xx_edac_remove(struct platform_device *device); * Device tree node type and compatible tuples this driver can match * on. */ -static struct of_device_id ppc4xx_edac_match[] = { +static const struct of_device_id ppc4xx_edac_match[] = { { .compatible = "ibm,sdram-4xx-ddr2" }, diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c index 1c96915..fc153ae 100644 --- a/drivers/edac/synopsys_edac.c +++ b/drivers/edac/synopsys_edac.c @@ -512,7 +512,7 @@ static int synps_edac_mc_remove(struct platform_device *pdev) return 0; } -static struct of_device_id synps_edac_match[] = { +static const struct of_device_id synps_edac_match[] = { { .compatible = "xlnx,zynq-ddrc-a05", }, { /* end of table */ } }; diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index c5f7b4e..6e45a43 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -17,7 +17,9 @@ */ static const char dmi_empty_string[] = " "; -static u16 __initdata dmi_ver; +static u32 dmi_ver __initdata; +static u32 dmi_len; +static u16 dmi_num; /* * Catch too early calls to dmi_check_system(): */ @@ -78,7 +80,7 @@ static const char * __init dmi_string(const struct dmi_header *dm, u8 s) * We have to be cautious here. We have seen BIOSes with DMI pointers * pointing to completely the wrong place for example */ -static void dmi_table(u8 *buf, int len, int num, +static void dmi_table(u8 *buf, void (*decode)(const struct dmi_header *, void *), void *private_data) { @@ -86,36 +88,38 @@ static void dmi_table(u8 *buf, int len, int num, int i = 0; /* - * Stop when we see all the items the table claimed to have - * OR we run off the end of the table (also happens) + * Stop when we have seen all the items the table claimed to have + * (SMBIOS < 3.0 only) OR we reach an end-of-table marker OR we run + * off the end of the table (should never happen but sometimes does + * on bogus implementations.) */ - while ((i < num) && (data - buf + sizeof(struct dmi_header)) <= len) { + while ((!dmi_num || i < dmi_num) && + (data - buf + sizeof(struct dmi_header)) <= dmi_len) { const struct dmi_header *dm = (const struct dmi_header *)data; /* - * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0] - */ - if (dm->type == DMI_ENTRY_END_OF_TABLE) - break; - - /* * We want to know the total length (formatted area and * strings) before decoding to make sure we won't run off the * table in dmi_decode or dmi_string */ data += dm->length; - while ((data - buf < len - 1) && (data[0] || data[1])) + while ((data - buf < dmi_len - 1) && (data[0] || data[1])) data++; - if (data - buf < len - 1) + if (data - buf < dmi_len - 1) decode(dm, private_data); + + /* + * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0] + */ + if (dm->type == DMI_ENTRY_END_OF_TABLE) + break; + data += 2; i++; } } static phys_addr_t dmi_base; -static u16 dmi_len; -static u16 dmi_num; static int __init dmi_walk_early(void (*decode)(const struct dmi_header *, void *)) @@ -126,7 +130,7 @@ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *, if (buf == NULL) return -1; - dmi_table(buf, dmi_len, dmi_num, decode, NULL); + dmi_table(buf, decode, NULL); add_device_randomness(buf, dmi_len); @@ -197,7 +201,7 @@ static void __init dmi_save_uuid(const struct dmi_header *dm, int slot, * the UUID are supposed to be little-endian encoded. The specification * says that this is the defacto standard. */ - if (dmi_ver >= 0x0206) + if (dmi_ver >= 0x020600) sprintf(s, "%pUL", d); else sprintf(s, "%pUB", d); @@ -469,7 +473,7 @@ static void __init dmi_format_ids(char *buf, size_t len) */ static int __init dmi_present(const u8 *buf) { - int smbios_ver; + u32 smbios_ver; if (memcmp(buf, "_SM_", 4) == 0 && buf[5] < 32 && dmi_checksum(buf, buf[5])) { @@ -502,14 +506,16 @@ static int __init dmi_present(const u8 *buf) if (dmi_walk_early(dmi_decode) == 0) { if (smbios_ver) { dmi_ver = smbios_ver; - pr_info("SMBIOS %d.%d present.\n", - dmi_ver >> 8, dmi_ver & 0xFF); + pr_info("SMBIOS %d.%d%s present.\n", + dmi_ver >> 8, dmi_ver & 0xFF, + (dmi_ver < 0x0300) ? "" : ".x"); } else { dmi_ver = (buf[14] & 0xF0) << 4 | (buf[14] & 0x0F); pr_info("Legacy DMI %d.%d present.\n", dmi_ver >> 8, dmi_ver & 0xFF); } + dmi_ver <<= 8; dmi_format_ids(dmi_ids_string, sizeof(dmi_ids_string)); printk(KERN_DEBUG "DMI: %s\n", dmi_ids_string); return 0; @@ -527,25 +533,16 @@ static int __init dmi_smbios3_present(const u8 *buf) { if (memcmp(buf, "_SM3_", 5) == 0 && buf[6] < 32 && dmi_checksum(buf, buf[6])) { - dmi_ver = get_unaligned_be16(buf + 7); + dmi_ver = get_unaligned_be32(buf + 6); + dmi_ver &= 0xFFFFFF; + dmi_num = 0; /* No longer specified */ dmi_len = get_unaligned_le32(buf + 12); dmi_base = get_unaligned_le64(buf + 16); - /* - * The 64-bit SMBIOS 3.0 entry point no longer has a field - * containing the number of structures present in the table. - * Instead, it defines the table size as a maximum size, and - * relies on the end-of-table structure type (#127) to be used - * to signal the end of the table. - * So let's define dmi_num as an upper bound as well: each - * structure has a 4 byte header, so dmi_len / 4 is an upper - * bound for the number of structures in the table. - */ - dmi_num = dmi_len / 4; - if (dmi_walk_early(dmi_decode) == 0) { - pr_info("SMBIOS %d.%d present.\n", - dmi_ver >> 8, dmi_ver & 0xFF); + pr_info("SMBIOS %d.%d.%d present.\n", + dmi_ver >> 16, (dmi_ver >> 8) & 0xFF, + dmi_ver & 0xFF); dmi_format_ids(dmi_ids_string, sizeof(dmi_ids_string)); pr_debug("DMI: %s\n", dmi_ids_string); return 0; @@ -900,7 +897,7 @@ int dmi_walk(void (*decode)(const struct dmi_header *, void *), if (buf == NULL) return -1; - dmi_table(buf, dmi_len, dmi_num, decode, private_data); + dmi_table(buf, decode, private_data); dmi_unmap(buf); return 0; diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index dcae482..e29560e 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -175,7 +175,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, unsigned long initrd_addr; u64 initrd_size = 0; unsigned long fdt_addr = 0; /* Original DTB */ - u64 fdt_size = 0; /* We don't get size from configuration table */ + unsigned long fdt_size = 0; char *cmdline_ptr = NULL; int cmdline_size = 0; unsigned long new_fdt_addr; @@ -239,8 +239,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, } else { status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=", - ~0UL, (unsigned long *)&fdt_addr, - (unsigned long *)&fdt_size); + ~0UL, &fdt_addr, &fdt_size); if (status != EFI_SUCCESS) { pr_efi_err(sys_table, "Failed to load device tree!\n"); @@ -252,7 +251,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, pr_efi(sys_table, "Using DTB from command line\n"); } else { /* Look for a device tree configuration table entry. */ - fdt_addr = (uintptr_t)get_fdt(sys_table); + fdt_addr = (uintptr_t)get_fdt(sys_table, &fdt_size); if (fdt_addr) pr_efi(sys_table, "Using DTB from configuration table\n"); } diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 2fe1950..f07d4a6 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -179,12 +179,12 @@ again: start = desc->phys_addr; end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT); - if ((start + size) > end || (start + size) > max) - continue; - - if (end - size > max) + if (end > max) end = max; + if ((start + size) > end) + continue; + if (round_down(end - size, align) < start) continue; diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 47437b1..e334a01 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -41,7 +41,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, unsigned long fdt_addr, unsigned long fdt_size); -void *get_fdt(efi_system_table_t *sys_table); +void *get_fdt(efi_system_table_t *sys_table, unsigned long *fdt_size); void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, unsigned long desc_size, efi_memory_desc_t *runtime_map, diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index 91da56c..ef5d764 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -323,7 +323,7 @@ fail: return EFI_LOAD_ERROR; } -void *get_fdt(efi_system_table_t *sys_table) +void *get_fdt(efi_system_table_t *sys_table, unsigned long *fdt_size) { efi_guid_t fdt_guid = DEVICE_TREE_GUID; efi_config_table_t *tables; @@ -336,6 +336,11 @@ void *get_fdt(efi_system_table_t *sys_table) for (i = 0; i < sys_table->nr_tables; i++) if (efi_guidcmp(tables[i].guid, fdt_guid) == 0) { fdt = (void *) tables[i].table; + if (fdt_check_header(fdt) != 0) { + pr_efi_err(sys_table, "Invalid header detected on UEFI supplied FDT, ignoring ...\n"); + return NULL; + } + *fdt_size = fdt_totalsize(fdt); break; } diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index a6952ba3..a65b751 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -334,7 +334,7 @@ static struct irq_domain_ops mpc8xxx_gpio_irq_ops = { .xlate = irq_domain_xlate_twocell, }; -static struct of_device_id mpc8xxx_gpio_ids[] __initdata = { +static struct of_device_id mpc8xxx_gpio_ids[] = { { .compatible = "fsl,mpc8349-gpio", }, { .compatible = "fsl,mpc8572-gpio", }, { .compatible = "fsl,mpc8610-gpio", }, diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c index 257e298..045a952 100644 --- a/drivers/gpio/gpio-syscon.c +++ b/drivers/gpio/gpio-syscon.c @@ -219,7 +219,7 @@ static int syscon_gpio_probe(struct platform_device *pdev) ret = of_property_read_u32_index(np, "gpio,syscon-dev", 2, &priv->dir_reg_offset); if (ret) - dev_err(dev, "can't read the dir register offset!\n"); + dev_dbg(dev, "can't read the dir register offset!\n"); priv->dir_reg_offset <<= 3; } diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c index 472fb5b..9cdbc0c 100644 --- a/drivers/gpio/gpio-tps65912.c +++ b/drivers/gpio/gpio-tps65912.c @@ -26,9 +26,12 @@ struct tps65912_gpio_data { struct gpio_chip gpio_chip; }; +#define to_tgd(gc) container_of(gc, struct tps65912_gpio_data, gpio_chip) + static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset) { - struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio); + struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc); + struct tps65912 *tps65912 = tps65912_gpio->tps65912; int val; val = tps65912_reg_read(tps65912, TPS65912_GPIO1 + offset); @@ -42,7 +45,8 @@ static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset) static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset, int value) { - struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio); + struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc); + struct tps65912 *tps65912 = tps65912_gpio->tps65912; if (value) tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset, @@ -55,7 +59,8 @@ static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset, static int tps65912_gpio_output(struct gpio_chip *gc, unsigned offset, int value) { - struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio); + struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc); + struct tps65912 *tps65912 = tps65912_gpio->tps65912; /* Set the initial value */ tps65912_gpio_set(gc, offset, value); @@ -66,7 +71,8 @@ static int tps65912_gpio_output(struct gpio_chip *gc, unsigned offset, static int tps65912_gpio_input(struct gpio_chip *gc, unsigned offset) { - struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio); + struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc); + struct tps65912 *tps65912 = tps65912_gpio->tps65912; return tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset, GPIO_CFG_MASK); diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index c0929d9..df990f2 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -201,6 +201,10 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares, if (!handler) return AE_BAD_PARAMETER; + pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin); + if (pin < 0) + return AE_BAD_PARAMETER; + desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event"); if (IS_ERR(desc)) { dev_err(chip->dev, "Failed to request GPIO\n"); @@ -551,6 +555,12 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, struct gpio_desc *desc; bool found; + pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin); + if (pin < 0) { + status = AE_BAD_PARAMETER; + goto out; + } + mutex_lock(&achip->conn_lock); found = false; diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 8cad8e4..4650bf8 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -46,12 +46,13 @@ static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data) ret = gc->of_xlate(gc, &gg_data->gpiospec, gg_data->flags); if (ret < 0) { - /* We've found the gpio chip, but the translation failed. - * Return true to stop looking and return the translation - * error via out_gpio + /* We've found a gpio chip, but the translation failed. + * Store translation error in out_gpio. + * Return false to keep looking, as more than one gpio chip + * could be registered per of-node. */ gg_data->out_gpio = ERR_PTR(ret); - return true; + return false; } gg_data->out_gpio = gpiochip_get_desc(gc, ret); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index b3589d0..d8135ad 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -62,12 +62,18 @@ enum KFD_MQD_TYPE get_mqd_type_from_queue_type(enum kfd_queue_type type) return KFD_MQD_TYPE_CP; } -static inline unsigned int get_first_pipe(struct device_queue_manager *dqm) +unsigned int get_first_pipe(struct device_queue_manager *dqm) { - BUG_ON(!dqm); + BUG_ON(!dqm || !dqm->dev); return dqm->dev->shared_resources.first_compute_pipe; } +unsigned int get_pipes_num(struct device_queue_manager *dqm) +{ + BUG_ON(!dqm || !dqm->dev); + return dqm->dev->shared_resources.compute_pipe_count; +} + static inline unsigned int get_pipes_num_cpsch(void) { return PIPE_PER_ME_CP_SCHEDULING; @@ -639,6 +645,7 @@ static int create_sdma_queue_nocpsch(struct device_queue_manager *dqm, pr_debug(" sdma queue id: %d\n", q->properties.sdma_queue_id); pr_debug(" sdma engine id: %d\n", q->properties.sdma_engine_id); + init_sdma_vm(dqm, q, qpd); retval = mqd->init_mqd(mqd, &q->mqd, &q->mqd_mem_obj, &q->gart_mqd_addr, &q->properties); if (retval != 0) { @@ -646,7 +653,14 @@ static int create_sdma_queue_nocpsch(struct device_queue_manager *dqm, return retval; } - init_sdma_vm(dqm, q, qpd); + retval = mqd->load_mqd(mqd, q->mqd, 0, + 0, NULL); + if (retval != 0) { + deallocate_sdma_queue(dqm, q->sdma_id); + mqd->uninit_mqd(mqd, q->mqd, q->mqd_mem_obj); + return retval; + } + return 0; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h index d64f86c..488f51d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h @@ -163,6 +163,8 @@ void program_sh_mem_settings(struct device_queue_manager *dqm, struct qcm_process_device *qpd); int init_pipelines(struct device_queue_manager *dqm, unsigned int pipes_num, unsigned int first_pipe); +unsigned int get_first_pipe(struct device_queue_manager *dqm); +unsigned int get_pipes_num(struct device_queue_manager *dqm); extern inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd) { @@ -175,10 +177,4 @@ get_sh_mem_bases_nybble_64(struct kfd_process_device *pdd) return (pdd->lds_base >> 60) & 0x0E; } -extern inline unsigned int get_pipes_num(struct device_queue_manager *dqm) -{ - BUG_ON(!dqm || !dqm->dev); - return dqm->dev->shared_resources.compute_pipe_count; -} - #endif /* KFD_DEVICE_QUEUE_MANAGER_H_ */ diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager_cik.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager_cik.c index 6b07246..5469efe 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager_cik.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager_cik.c @@ -131,5 +131,5 @@ static int register_process_cik(struct device_queue_manager *dqm, static int initialize_cpsch_cik(struct device_queue_manager *dqm) { - return init_pipelines(dqm, get_pipes_num(dqm), 0); + return init_pipelines(dqm, get_pipes_num(dqm), get_first_pipe(dqm)); } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c index e415a2a..c7d298e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c @@ -44,7 +44,7 @@ static bool initialize(struct kernel_queue *kq, struct kfd_dev *dev, BUG_ON(!kq || !dev); BUG_ON(type != KFD_QUEUE_TYPE_DIQ && type != KFD_QUEUE_TYPE_HIQ); - pr_debug("kfd: In func %s initializing queue type %d size %d\n", + pr_debug("amdkfd: In func %s initializing queue type %d size %d\n", __func__, KFD_QUEUE_TYPE_HIQ, queue_size); nop.opcode = IT_NOP; @@ -69,12 +69,16 @@ static bool initialize(struct kernel_queue *kq, struct kfd_dev *dev, prop.doorbell_ptr = kfd_get_kernel_doorbell(dev, &prop.doorbell_off); - if (prop.doorbell_ptr == NULL) + if (prop.doorbell_ptr == NULL) { + pr_err("amdkfd: error init doorbell"); goto err_get_kernel_doorbell; + } retval = kfd_gtt_sa_allocate(dev, queue_size, &kq->pq); - if (retval != 0) + if (retval != 0) { + pr_err("amdkfd: error init pq queues size (%d)\n", queue_size); goto err_pq_allocate_vidmem; + } kq->pq_kernel_addr = kq->pq->cpu_ptr; kq->pq_gpu_addr = kq->pq->gpu_addr; @@ -165,10 +169,8 @@ err_rptr_allocate_vidmem: err_eop_allocate_vidmem: kfd_gtt_sa_free(dev, kq->pq); err_pq_allocate_vidmem: - pr_err("kfd: error init pq\n"); kfd_release_kernel_doorbell(dev, prop.doorbell_ptr); err_get_kernel_doorbell: - pr_err("kfd: error init doorbell"); return false; } @@ -187,6 +189,8 @@ static void uninitialize(struct kernel_queue *kq) else if (kq->queue->properties.type == KFD_QUEUE_TYPE_DIQ) kfd_gtt_sa_free(kq->dev, kq->fence_mem_obj); + kq->mqd->uninit_mqd(kq->mqd, kq->queue->mqd, kq->queue->mqd_mem_obj); + kfd_gtt_sa_free(kq->dev, kq->rptr_mem); kfd_gtt_sa_free(kq->dev, kq->wptr_mem); kq->ops_asic_specific.uninitialize(kq); @@ -211,7 +215,7 @@ static int acquire_packet_buffer(struct kernel_queue *kq, queue_address = (unsigned int *)kq->pq_kernel_addr; queue_size_dwords = kq->queue->properties.queue_size / sizeof(uint32_t); - pr_debug("kfd: In func %s\nrptr: %d\nwptr: %d\nqueue_address 0x%p\n", + pr_debug("amdkfd: In func %s\nrptr: %d\nwptr: %d\nqueue_address 0x%p\n", __func__, rptr, wptr, queue_address); available_size = (rptr - 1 - wptr + queue_size_dwords) % @@ -296,7 +300,7 @@ struct kernel_queue *kernel_queue_init(struct kfd_dev *dev, } if (kq->ops.initialize(kq, dev, type, KFD_KERNEL_QUEUE_SIZE) == false) { - pr_err("kfd: failed to init kernel queue\n"); + pr_err("amdkfd: failed to init kernel queue\n"); kfree(kq); return NULL; } @@ -319,7 +323,7 @@ static __attribute__((unused)) void test_kq(struct kfd_dev *dev) BUG_ON(!dev); - pr_err("kfd: starting kernel queue test\n"); + pr_err("amdkfd: starting kernel queue test\n"); kq = kernel_queue_init(dev, KFD_QUEUE_TYPE_HIQ); BUG_ON(!kq); @@ -330,7 +334,7 @@ static __attribute__((unused)) void test_kq(struct kfd_dev *dev) buffer[i] = kq->nop_packet; kq->ops.submit_packet(kq); - pr_err("kfd: ending kernel queue test\n"); + pr_err("amdkfd: ending kernel queue test\n"); } diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index 0409b90..b3e3068 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -153,7 +153,7 @@ static int atmel_hlcdc_crtc_mode_set(struct drm_crtc *c, (adj->crtc_hdisplay - 1) | ((adj->crtc_vdisplay - 1) << 16)); - cfg = ATMEL_HLCDC_CLKPOL; + cfg = 0; prate = clk_get_rate(crtc->dc->hlcdc->sys_clk); mode_rate = mode->crtc_clock * 1000; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 7320a6c..c1cb174 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -311,8 +311,6 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev) pm_runtime_enable(dev->dev); - pm_runtime_put_sync(dev->dev); - ret = atmel_hlcdc_dc_modeset_init(dev); if (ret < 0) { dev_err(dev->dev, "failed to initialize mode setting\n"); diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c index 063d2a7..e79bd9b 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c @@ -311,7 +311,8 @@ int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer) /* Disable the layer */ regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR, - ATMEL_HLCDC_LAYER_RST); + ATMEL_HLCDC_LAYER_RST | ATMEL_HLCDC_LAYER_A2Q | + ATMEL_HLCDC_LAYER_UPDATE); /* Clear all pending interrupts */ regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 6b00173..b6f076b2 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -43,9 +43,10 @@ #include "drm_crtc_internal.h" #include "drm_internal.h" -static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, - struct drm_mode_fb_cmd2 *r, - struct drm_file *file_priv); +static struct drm_framebuffer * +internal_framebuffer_create(struct drm_device *dev, + struct drm_mode_fb_cmd2 *r, + struct drm_file *file_priv); /* Avoid boilerplate. I'm tired of typing. */ #define DRM_ENUM_NAME_FN(fnname, list) \ @@ -524,17 +525,6 @@ void drm_framebuffer_reference(struct drm_framebuffer *fb) } EXPORT_SYMBOL(drm_framebuffer_reference); -static void drm_framebuffer_free_bug(struct kref *kref) -{ - BUG(); -} - -static void __drm_framebuffer_unreference(struct drm_framebuffer *fb) -{ - DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount)); - kref_put(&fb->refcount, drm_framebuffer_free_bug); -} - /** * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr * @fb: fb to unregister @@ -1319,7 +1309,7 @@ void drm_plane_force_disable(struct drm_plane *plane) return; } /* disconnect the plane from the fb and crtc: */ - __drm_framebuffer_unreference(plane->old_fb); + drm_framebuffer_unreference(plane->old_fb); plane->old_fb = NULL; plane->fb = NULL; plane->crtc = NULL; @@ -2127,12 +2117,11 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); mutex_lock(&dev->mode_config.mutex); - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); connector = drm_connector_find(dev, out_resp->connector_id); if (!connector) { ret = -ENOENT; - goto out; + goto out_unlock; } for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) @@ -2157,6 +2146,8 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out_resp->mm_height = connector->display_info.height_mm; out_resp->subpixel = connector->display_info.subpixel_order; out_resp->connection = connector->status; + + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); encoder = drm_connector_get_encoder(connector); if (encoder) out_resp->encoder_id = encoder->base.id; @@ -2210,6 +2201,8 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out: drm_modeset_unlock(&dev->mode_config.connection_mutex); + +out_unlock: mutex_unlock(&dev->mode_config.mutex); return ret; @@ -2907,13 +2900,11 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc, */ if (req->flags & DRM_MODE_CURSOR_BO) { if (req->handle) { - fb = add_framebuffer_internal(dev, &fbreq, file_priv); + fb = internal_framebuffer_create(dev, &fbreq, file_priv); if (IS_ERR(fb)) { DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n"); return PTR_ERR(fb); } - - drm_framebuffer_reference(fb); } else { fb = NULL; } @@ -3266,9 +3257,10 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) return 0; } -static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, - struct drm_mode_fb_cmd2 *r, - struct drm_file *file_priv) +static struct drm_framebuffer * +internal_framebuffer_create(struct drm_device *dev, + struct drm_mode_fb_cmd2 *r, + struct drm_file *file_priv) { struct drm_mode_config *config = &dev->mode_config; struct drm_framebuffer *fb; @@ -3300,12 +3292,6 @@ static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, return fb; } - mutex_lock(&file_priv->fbs_lock); - r->fb_id = fb->base.id; - list_add(&fb->filp_head, &file_priv->fbs); - DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); - mutex_unlock(&file_priv->fbs_lock); - return fb; } @@ -3327,15 +3313,24 @@ static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, int drm_mode_addfb2(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct drm_mode_fb_cmd2 *r = data; struct drm_framebuffer *fb; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - fb = add_framebuffer_internal(dev, data, file_priv); + fb = internal_framebuffer_create(dev, r, file_priv); if (IS_ERR(fb)) return PTR_ERR(fb); + /* Transfer ownership to the filp for reaping on close */ + + DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); + mutex_lock(&file_priv->fbs_lock); + r->fb_id = fb->base.id; + list_add(&fb->filp_head, &file_priv->fbs); + mutex_unlock(&file_priv->fbs_lock); + return 0; } diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 9a5b687..379ab45 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -733,10 +733,14 @@ static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_sideband_msg_tx *txmsg) { bool ret; - mutex_lock(&mgr->qlock); + + /* + * All updates to txmsg->state are protected by mgr->qlock, and the two + * cases we check here are terminal states. For those the barriers + * provided by the wake_up/wait_event pair are enough. + */ ret = (txmsg->state == DRM_DP_SIDEBAND_TX_RX || txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT); - mutex_unlock(&mgr->qlock); return ret; } @@ -1363,12 +1367,13 @@ static int process_single_tx_qlock(struct drm_dp_mst_topology_mgr *mgr, return 0; } -/* must be called holding qlock */ static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr) { struct drm_dp_sideband_msg_tx *txmsg; int ret; + WARN_ON(!mutex_is_locked(&mgr->qlock)); + /* construct a chunk from the first msg in the tx_msg queue */ if (list_empty(&mgr->tx_msg_downq)) { mgr->tx_down_in_progress = false; diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index 732cb6f..4c0aa97 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -287,6 +287,7 @@ int drm_load_edid_firmware(struct drm_connector *connector) drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); + drm_edid_to_eld(connector, edid); kfree(edid); return ret; diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 04a209e..1134526 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -91,29 +91,29 @@ */ static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, - unsigned long size, + u64 size, unsigned alignment, unsigned long color, enum drm_mm_search_flags flags); static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, - unsigned long size, + u64 size, unsigned alignment, unsigned long color, - unsigned long start, - unsigned long end, + u64 start, + u64 end, enum drm_mm_search_flags flags); static void drm_mm_insert_helper(struct drm_mm_node *hole_node, struct drm_mm_node *node, - unsigned long size, unsigned alignment, + u64 size, unsigned alignment, unsigned long color, enum drm_mm_allocator_flags flags) { struct drm_mm *mm = hole_node->mm; - unsigned long hole_start = drm_mm_hole_node_start(hole_node); - unsigned long hole_end = drm_mm_hole_node_end(hole_node); - unsigned long adj_start = hole_start; - unsigned long adj_end = hole_end; + u64 hole_start = drm_mm_hole_node_start(hole_node); + u64 hole_end = drm_mm_hole_node_end(hole_node); + u64 adj_start = hole_start; + u64 adj_end = hole_end; BUG_ON(node->allocated); @@ -124,12 +124,15 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, adj_start = adj_end - size; if (alignment) { - unsigned tmp = adj_start % alignment; - if (tmp) { + u64 tmp = adj_start; + unsigned rem; + + rem = do_div(tmp, alignment); + if (rem) { if (flags & DRM_MM_CREATE_TOP) - adj_start -= tmp; + adj_start -= rem; else - adj_start += alignment - tmp; + adj_start += alignment - rem; } } @@ -176,9 +179,9 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) { struct drm_mm_node *hole; - unsigned long end = node->start + node->size; - unsigned long hole_start; - unsigned long hole_end; + u64 end = node->start + node->size; + u64 hole_start; + u64 hole_end; BUG_ON(node == NULL); @@ -227,7 +230,7 @@ EXPORT_SYMBOL(drm_mm_reserve_node); * 0 on success, -ENOSPC if there's no suitable hole. */ int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, - unsigned long size, unsigned alignment, + u64 size, unsigned alignment, unsigned long color, enum drm_mm_search_flags sflags, enum drm_mm_allocator_flags aflags) @@ -246,16 +249,16 @@ EXPORT_SYMBOL(drm_mm_insert_node_generic); static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, struct drm_mm_node *node, - unsigned long size, unsigned alignment, + u64 size, unsigned alignment, unsigned long color, - unsigned long start, unsigned long end, + u64 start, u64 end, enum drm_mm_allocator_flags flags) { struct drm_mm *mm = hole_node->mm; - unsigned long hole_start = drm_mm_hole_node_start(hole_node); - unsigned long hole_end = drm_mm_hole_node_end(hole_node); - unsigned long adj_start = hole_start; - unsigned long adj_end = hole_end; + u64 hole_start = drm_mm_hole_node_start(hole_node); + u64 hole_end = drm_mm_hole_node_end(hole_node); + u64 adj_start = hole_start; + u64 adj_end = hole_end; BUG_ON(!hole_node->hole_follows || node->allocated); @@ -271,12 +274,15 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, mm->color_adjust(hole_node, color, &adj_start, &adj_end); if (alignment) { - unsigned tmp = adj_start % alignment; - if (tmp) { + u64 tmp = adj_start; + unsigned rem; + + rem = do_div(tmp, alignment); + if (rem) { if (flags & DRM_MM_CREATE_TOP) - adj_start -= tmp; + adj_start -= rem; else - adj_start += alignment - tmp; + adj_start += alignment - rem; } } @@ -324,9 +330,9 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, * 0 on success, -ENOSPC if there's no suitable hole. */ int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node, - unsigned long size, unsigned alignment, + u64 size, unsigned alignment, unsigned long color, - unsigned long start, unsigned long end, + u64 start, u64 end, enum drm_mm_search_flags sflags, enum drm_mm_allocator_flags aflags) { @@ -387,32 +393,34 @@ void drm_mm_remove_node(struct drm_mm_node *node) } EXPORT_SYMBOL(drm_mm_remove_node); -static int check_free_hole(unsigned long start, unsigned long end, - unsigned long size, unsigned alignment) +static int check_free_hole(u64 start, u64 end, u64 size, unsigned alignment) { if (end - start < size) return 0; if (alignment) { - unsigned tmp = start % alignment; - if (tmp) - start += alignment - tmp; + u64 tmp = start; + unsigned rem; + + rem = do_div(tmp, alignment); + if (rem) + start += alignment - rem; } return end >= start + size; } static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, - unsigned long size, + u64 size, unsigned alignment, unsigned long color, enum drm_mm_search_flags flags) { struct drm_mm_node *entry; struct drm_mm_node *best; - unsigned long adj_start; - unsigned long adj_end; - unsigned long best_size; + u64 adj_start; + u64 adj_end; + u64 best_size; BUG_ON(mm->scanned_blocks); @@ -421,7 +429,7 @@ static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, __drm_mm_for_each_hole(entry, mm, adj_start, adj_end, flags & DRM_MM_SEARCH_BELOW) { - unsigned long hole_size = adj_end - adj_start; + u64 hole_size = adj_end - adj_start; if (mm->color_adjust) { mm->color_adjust(entry, color, &adj_start, &adj_end); @@ -445,18 +453,18 @@ static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, } static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, - unsigned long size, + u64 size, unsigned alignment, unsigned long color, - unsigned long start, - unsigned long end, + u64 start, + u64 end, enum drm_mm_search_flags flags) { struct drm_mm_node *entry; struct drm_mm_node *best; - unsigned long adj_start; - unsigned long adj_end; - unsigned long best_size; + u64 adj_start; + u64 adj_end; + u64 best_size; BUG_ON(mm->scanned_blocks); @@ -465,7 +473,7 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ __drm_mm_for_each_hole(entry, mm, adj_start, adj_end, flags & DRM_MM_SEARCH_BELOW) { - unsigned long hole_size = adj_end - adj_start; + u64 hole_size = adj_end - adj_start; if (adj_start < start) adj_start = start; @@ -561,7 +569,7 @@ EXPORT_SYMBOL(drm_mm_replace_node); * adding/removing nodes to/from the scan list are allowed. */ void drm_mm_init_scan(struct drm_mm *mm, - unsigned long size, + u64 size, unsigned alignment, unsigned long color) { @@ -594,11 +602,11 @@ EXPORT_SYMBOL(drm_mm_init_scan); * adding/removing nodes to/from the scan list are allowed. */ void drm_mm_init_scan_with_range(struct drm_mm *mm, - unsigned long size, + u64 size, unsigned alignment, unsigned long color, - unsigned long start, - unsigned long end) + u64 start, + u64 end) { mm->scan_color = color; mm->scan_alignment = alignment; @@ -627,8 +635,8 @@ bool drm_mm_scan_add_block(struct drm_mm_node *node) { struct drm_mm *mm = node->mm; struct drm_mm_node *prev_node; - unsigned long hole_start, hole_end; - unsigned long adj_start, adj_end; + u64 hole_start, hole_end; + u64 adj_start, adj_end; mm->scanned_blocks++; @@ -731,7 +739,7 @@ EXPORT_SYMBOL(drm_mm_clean); * * Note that @mm must be cleared to 0 before calling this function. */ -void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size) +void drm_mm_init(struct drm_mm * mm, u64 start, u64 size) { INIT_LIST_HEAD(&mm->hole_stack); mm->scanned_blocks = 0; @@ -766,18 +774,17 @@ void drm_mm_takedown(struct drm_mm * mm) } EXPORT_SYMBOL(drm_mm_takedown); -static unsigned long drm_mm_debug_hole(struct drm_mm_node *entry, - const char *prefix) +static u64 drm_mm_debug_hole(struct drm_mm_node *entry, + const char *prefix) { - unsigned long hole_start, hole_end, hole_size; + u64 hole_start, hole_end, hole_size; if (entry->hole_follows) { hole_start = drm_mm_hole_node_start(entry); hole_end = drm_mm_hole_node_end(entry); hole_size = hole_end - hole_start; - printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n", - prefix, hole_start, hole_end, - hole_size); + pr_debug("%s %#llx-%#llx: %llu: free\n", prefix, hole_start, + hole_end, hole_size); return hole_size; } @@ -792,35 +799,34 @@ static unsigned long drm_mm_debug_hole(struct drm_mm_node *entry, void drm_mm_debug_table(struct drm_mm *mm, const char *prefix) { struct drm_mm_node *entry; - unsigned long total_used = 0, total_free = 0, total = 0; + u64 total_used = 0, total_free = 0, total = 0; total_free += drm_mm_debug_hole(&mm->head_node, prefix); drm_mm_for_each_node(entry, mm) { - printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: used\n", - prefix, entry->start, entry->start + entry->size, - entry->size); + pr_debug("%s %#llx-%#llx: %llu: used\n", prefix, entry->start, + entry->start + entry->size, entry->size); total_used += entry->size; total_free += drm_mm_debug_hole(entry, prefix); } total = total_free + total_used; - printk(KERN_DEBUG "%s total: %lu, used %lu free %lu\n", prefix, total, - total_used, total_free); + pr_debug("%s total: %llu, used %llu free %llu\n", prefix, total, + total_used, total_free); } EXPORT_SYMBOL(drm_mm_debug_table); #if defined(CONFIG_DEBUG_FS) -static unsigned long drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *entry) +static u64 drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *entry) { - unsigned long hole_start, hole_end, hole_size; + u64 hole_start, hole_end, hole_size; if (entry->hole_follows) { hole_start = drm_mm_hole_node_start(entry); hole_end = drm_mm_hole_node_end(entry); hole_size = hole_end - hole_start; - seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: free\n", - hole_start, hole_end, hole_size); + seq_printf(m, "%#llx-%#llx: %llu: free\n", hole_start, + hole_end, hole_size); return hole_size; } @@ -835,20 +841,20 @@ static unsigned long drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *en int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm) { struct drm_mm_node *entry; - unsigned long total_used = 0, total_free = 0, total = 0; + u64 total_used = 0, total_free = 0, total = 0; total_free += drm_mm_dump_hole(m, &mm->head_node); drm_mm_for_each_node(entry, mm) { - seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: used\n", - entry->start, entry->start + entry->size, - entry->size); + seq_printf(m, "%#016llx-%#016llx: %llu: used\n", entry->start, + entry->start + entry->size, entry->size); total_used += entry->size; total_free += drm_mm_dump_hole(m, entry); } total = total_free + total_used; - seq_printf(m, "total: %lu, used %lu free %lu\n", total, total_used, total_free); + seq_printf(m, "total: %llu, used %llu free %llu\n", total, + total_used, total_free); return 0; } EXPORT_SYMBOL(drm_mm_dump_table); diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 6591d48..3fee587 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -174,6 +174,7 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect struct edid *edid = (struct edid *) connector->edid_blob_ptr->data; count = drm_add_edid_modes(connector, edid); + drm_edid_to_eld(connector, edid); } else count = (*connector_funcs->get_modes)(connector); } diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index a5e7461..0a67803 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -50,7 +50,7 @@ config DRM_EXYNOS_DSI config DRM_EXYNOS_DP bool "EXYNOS DRM DP driver support" - depends on (DRM_EXYNOS_FIMD || DRM_EXYNOS7DECON) && ARCH_EXYNOS && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS) + depends on (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON) && ARCH_EXYNOS && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS) default DRM_EXYNOS select DRM_PANEL help diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c index 63f02e2..9700461 100644 --- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c @@ -888,8 +888,8 @@ static int decon_probe(struct platform_device *pdev) of_node_put(i80_if_timings); ctx->regs = of_iomap(dev->of_node, 0); - if (IS_ERR(ctx->regs)) { - ret = PTR_ERR(ctx->regs); + if (!ctx->regs) { + ret = -ENOMEM; goto err_del_component; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c deleted file mode 100644 index ba9b3d5..0000000 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include <drm/drmP.h> -#include <drm/drm_crtc_helper.h> - -#include <drm/exynos_drm.h> -#include "exynos_drm_drv.h" -#include "exynos_drm_encoder.h" -#include "exynos_drm_connector.h" - -#define to_exynos_connector(x) container_of(x, struct exynos_drm_connector,\ - drm_connector) - -struct exynos_drm_connector { - struct drm_connector drm_connector; - uint32_t encoder_id; - struct exynos_drm_display *display; -}; - -static int exynos_drm_connector_get_modes(struct drm_connector *connector) -{ - struct exynos_drm_connector *exynos_connector = - to_exynos_connector(connector); - struct exynos_drm_display *display = exynos_connector->display; - struct edid *edid = NULL; - unsigned int count = 0; - int ret; - - /* - * if get_edid() exists then get_edid() callback of hdmi side - * is called to get edid data through i2c interface else - * get timing from the FIMD driver(display controller). - * - * P.S. in case of lcd panel, count is always 1 if success - * because lcd panel has only one mode. - */ - if (display->ops->get_edid) { - edid = display->ops->get_edid(display, connector); - if (IS_ERR_OR_NULL(edid)) { - ret = PTR_ERR(edid); - edid = NULL; - DRM_ERROR("Panel operation get_edid failed %d\n", ret); - goto out; - } - - count = drm_add_edid_modes(connector, edid); - if (!count) { - DRM_ERROR("Add edid modes failed %d\n", count); - goto out; - } - - drm_mode_connector_update_edid_property(connector, edid); - } else { - struct exynos_drm_panel_info *panel; - struct drm_display_mode *mode = drm_mode_create(connector->dev); - if (!mode) { - DRM_ERROR("failed to create a new display mode.\n"); - return 0; - } - - if (display->ops->get_panel) - panel = display->ops->get_panel(display); - else { - drm_mode_destroy(connector->dev, mode); - return 0; - } - - drm_display_mode_from_videomode(&panel->vm, mode); - mode->width_mm = panel->width_mm; - mode->height_mm = panel->height_mm; - connector->display_info.width_mm = mode->width_mm; - connector->display_info.height_mm = mode->height_mm; - - mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; - drm_mode_set_name(mode); - drm_mode_probed_add(connector, mode); - - count = 1; - } - -out: - kfree(edid); - return count; -} - -static int exynos_drm_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct exynos_drm_connector *exynos_connector = - to_exynos_connector(connector); - struct exynos_drm_display *display = exynos_connector->display; - int ret = MODE_BAD; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (display->ops->check_mode) - if (!display->ops->check_mode(display, mode)) - ret = MODE_OK; - - return ret; -} - -static struct drm_encoder *exynos_drm_best_encoder( - struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - struct exynos_drm_connector *exynos_connector = - to_exynos_connector(connector); - return drm_encoder_find(dev, exynos_connector->encoder_id); -} - -static struct drm_connector_helper_funcs exynos_connector_helper_funcs = { - .get_modes = exynos_drm_connector_get_modes, - .mode_valid = exynos_drm_connector_mode_valid, - .best_encoder = exynos_drm_best_encoder, -}; - -static int exynos_drm_connector_fill_modes(struct drm_connector *connector, - unsigned int max_width, unsigned int max_height) -{ - struct exynos_drm_connector *exynos_connector = - to_exynos_connector(connector); - struct exynos_drm_display *display = exynos_connector->display; - unsigned int width, height; - - width = max_width; - height = max_height; - - /* - * if specific driver want to find desired_mode using maxmum - * resolution then get max width and height from that driver. - */ - if (display->ops->get_max_resol) - display->ops->get_max_resol(display, &width, &height); - - return drm_helper_probe_single_connector_modes(connector, width, - height); -} - -/* get detection status of display device. */ -static enum drm_connector_status -exynos_drm_connector_detect(struct drm_connector *connector, bool force) -{ - struct exynos_drm_connector *exynos_connector = - to_exynos_connector(connector); - struct exynos_drm_display *display = exynos_connector->display; - enum drm_connector_status status = connector_status_disconnected; - - if (display->ops->is_connected) { - if (display->ops->is_connected(display)) - status = connector_status_connected; - else - status = connector_status_disconnected; - } - - return status; -} - -static void exynos_drm_connector_destroy(struct drm_connector *connector) -{ - struct exynos_drm_connector *exynos_connector = - to_exynos_connector(connector); - - drm_connector_unregister(connector); - drm_connector_cleanup(connector); - kfree(exynos_connector); -} - -static struct drm_connector_funcs exynos_connector_funcs = { - .dpms = drm_helper_connector_dpms, - .fill_modes = exynos_drm_connector_fill_modes, - .detect = exynos_drm_connector_detect, - .destroy = exynos_drm_connector_destroy, -}; - -struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, - struct drm_encoder *encoder) -{ - struct exynos_drm_connector *exynos_connector; - struct exynos_drm_display *display = exynos_drm_get_display(encoder); - struct drm_connector *connector; - int type; - int err; - - exynos_connector = kzalloc(sizeof(*exynos_connector), GFP_KERNEL); - if (!exynos_connector) - return NULL; - - connector = &exynos_connector->drm_connector; - - switch (display->type) { - case EXYNOS_DISPLAY_TYPE_HDMI: - type = DRM_MODE_CONNECTOR_HDMIA; - connector->interlace_allowed = true; - connector->polled = DRM_CONNECTOR_POLL_HPD; - break; - case EXYNOS_DISPLAY_TYPE_VIDI: - type = DRM_MODE_CONNECTOR_VIRTUAL; - connector->polled = DRM_CONNECTOR_POLL_HPD; - break; - default: - type = DRM_MODE_CONNECTOR_Unknown; - break; - } - - drm_connector_init(dev, connector, &exynos_connector_funcs, type); - drm_connector_helper_add(connector, &exynos_connector_helper_funcs); - - err = drm_connector_register(connector); - if (err) - goto err_connector; - - exynos_connector->encoder_id = encoder->base.id; - exynos_connector->display = display; - connector->dpms = DRM_MODE_DPMS_OFF; - connector->encoder = encoder; - - err = drm_mode_connector_attach_encoder(connector, encoder); - if (err) { - DRM_ERROR("failed to attach a connector to a encoder\n"); - goto err_sysfs; - } - - DRM_DEBUG_KMS("connector has been created\n"); - - return connector; - -err_sysfs: - drm_connector_unregister(connector); -err_connector: - drm_connector_cleanup(connector); - kfree(exynos_connector); - return NULL; -} diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.h b/drivers/gpu/drm/exynos/exynos_drm_connector.h deleted file mode 100644 index 4eb20d7..0000000 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - * Inki Dae <inki.dae@samsung.com> - * Joonyoung Shim <jy0922.shim@samsung.com> - * Seung-Woo Kim <sw0312.kim@samsung.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef _EXYNOS_DRM_CONNECTOR_H_ -#define _EXYNOS_DRM_CONNECTOR_H_ - -struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, - struct drm_encoder *encoder); - -#endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 925fc69..33a10ce 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -147,6 +147,7 @@ struct fimd_win_data { unsigned int ovl_height; unsigned int fb_width; unsigned int fb_height; + unsigned int fb_pitch; unsigned int bpp; unsigned int pixel_format; dma_addr_t dma_addr; @@ -284,14 +285,9 @@ static void fimd_clear_channel(struct fimd_context *ctx) } } -static int fimd_ctx_initialize(struct fimd_context *ctx, +static int fimd_iommu_attach_devices(struct fimd_context *ctx, struct drm_device *drm_dev) { - struct exynos_drm_private *priv; - priv = drm_dev->dev_private; - - ctx->drm_dev = drm_dev; - ctx->pipe = priv->pipe++; /* attach this sub driver to iommu mapping if supported. */ if (is_drm_iommu_supported(ctx->drm_dev)) { @@ -313,7 +309,7 @@ static int fimd_ctx_initialize(struct fimd_context *ctx, return 0; } -static void fimd_ctx_remove(struct fimd_context *ctx) +static void fimd_iommu_detach_devices(struct fimd_context *ctx) { /* detach this sub driver from iommu mapping if supported. */ if (is_drm_iommu_supported(ctx->drm_dev)) @@ -537,13 +533,14 @@ static void fimd_win_mode_set(struct exynos_drm_crtc *crtc, win_data->offset_y = plane->crtc_y; win_data->ovl_width = plane->crtc_width; win_data->ovl_height = plane->crtc_height; + win_data->fb_pitch = plane->pitch; win_data->fb_width = plane->fb_width; win_data->fb_height = plane->fb_height; win_data->dma_addr = plane->dma_addr[0] + offset; win_data->bpp = plane->bpp; win_data->pixel_format = plane->pixel_format; - win_data->buf_offsize = (plane->fb_width - plane->crtc_width) * - (plane->bpp >> 3); + win_data->buf_offsize = + plane->pitch - (plane->crtc_width * (plane->bpp >> 3)); win_data->line_size = plane->crtc_width * (plane->bpp >> 3); DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n", @@ -709,7 +706,7 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, int zpos) writel(val, ctx->regs + VIDWx_BUF_START(win, 0)); /* buffer end address */ - size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3); + size = win_data->fb_pitch * win_data->ovl_height * (win_data->bpp >> 3); val = (unsigned long)(win_data->dma_addr + size); writel(val, ctx->regs + VIDWx_BUF_END(win, 0)); @@ -1056,25 +1053,23 @@ static int fimd_bind(struct device *dev, struct device *master, void *data) { struct fimd_context *ctx = dev_get_drvdata(dev); struct drm_device *drm_dev = data; + struct exynos_drm_private *priv = drm_dev->dev_private; int ret; - ret = fimd_ctx_initialize(ctx, drm_dev); - if (ret) { - DRM_ERROR("fimd_ctx_initialize failed.\n"); - return ret; - } + ctx->drm_dev = drm_dev; + ctx->pipe = priv->pipe++; ctx->crtc = exynos_drm_crtc_create(drm_dev, ctx->pipe, EXYNOS_DISPLAY_TYPE_LCD, &fimd_crtc_ops, ctx); - if (IS_ERR(ctx->crtc)) { - fimd_ctx_remove(ctx); - return PTR_ERR(ctx->crtc); - } if (ctx->display) exynos_drm_create_enc_conn(drm_dev, ctx->display); + ret = fimd_iommu_attach_devices(ctx, drm_dev); + if (ret) + return ret; + return 0; } @@ -1086,10 +1081,10 @@ static void fimd_unbind(struct device *dev, struct device *master, fimd_dpms(ctx->crtc, DRM_MODE_DPMS_OFF); + fimd_iommu_detach_devices(ctx); + if (ctx->display) exynos_dpi_remove(ctx->display); - - fimd_ctx_remove(ctx); } static const struct component_ops fimd_component_ops = { diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index a561687..8ad5b72 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -175,7 +175,7 @@ static int exynos_disable_plane(struct drm_plane *plane) struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(plane->crtc); - if (exynos_crtc->ops->win_disable) + if (exynos_crtc && exynos_crtc->ops->win_disable) exynos_crtc->ops->win_disable(exynos_crtc, exynos_plane->zpos); diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 3518bc4..2e3bc57 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -55,6 +55,7 @@ struct hdmi_win_data { unsigned int fb_x; unsigned int fb_y; unsigned int fb_width; + unsigned int fb_pitch; unsigned int fb_height; unsigned int src_width; unsigned int src_height; @@ -438,7 +439,7 @@ static void vp_video_buffer(struct mixer_context *ctx, int win) } else { luma_addr[0] = win_data->dma_addr; chroma_addr[0] = win_data->dma_addr - + (win_data->fb_width * win_data->fb_height); + + (win_data->fb_pitch * win_data->fb_height); } if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) { @@ -447,8 +448,8 @@ static void vp_video_buffer(struct mixer_context *ctx, int win) luma_addr[1] = luma_addr[0] + 0x40; chroma_addr[1] = chroma_addr[0] + 0x40; } else { - luma_addr[1] = luma_addr[0] + win_data->fb_width; - chroma_addr[1] = chroma_addr[0] + win_data->fb_width; + luma_addr[1] = luma_addr[0] + win_data->fb_pitch; + chroma_addr[1] = chroma_addr[0] + win_data->fb_pitch; } } else { ctx->interlace = false; @@ -469,10 +470,10 @@ static void vp_video_buffer(struct mixer_context *ctx, int win) vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK); /* setting size of input image */ - vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_width) | + vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_pitch) | VP_IMG_VSIZE(win_data->fb_height)); /* chroma height has to reduced by 2 to avoid chroma distorions */ - vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_width) | + vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_pitch) | VP_IMG_VSIZE(win_data->fb_height / 2)); vp_reg_write(res, VP_SRC_WIDTH, win_data->src_width); @@ -559,7 +560,7 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win) /* converting dma address base and source offset */ dma_addr = win_data->dma_addr + (win_data->fb_x * win_data->bpp >> 3) - + (win_data->fb_y * win_data->fb_width * win_data->bpp >> 3); + + (win_data->fb_y * win_data->fb_pitch); src_x_offset = 0; src_y_offset = 0; @@ -576,7 +577,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win) MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK); /* setup geometry */ - mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), win_data->fb_width); + mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), + win_data->fb_pitch / (win_data->bpp >> 3)); /* setup display size */ if (ctx->mxr_ver == MXR_VER_128_0_0_184 && @@ -961,6 +963,7 @@ static void mixer_win_mode_set(struct exynos_drm_crtc *crtc, win_data->fb_y = plane->fb_y; win_data->fb_width = plane->fb_width; win_data->fb_height = plane->fb_height; + win_data->fb_pitch = plane->pitch; win_data->src_width = plane->src_width; win_data->src_height = plane->src_height; diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 96e811f..e8b18e5 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -152,12 +152,12 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) seq_puts(m, " (pp"); else seq_puts(m, " (g"); - seq_printf(m, "gtt offset: %08lx, size: %08lx, type: %u)", + seq_printf(m, "gtt offset: %08llx, size: %08llx, type: %u)", vma->node.start, vma->node.size, vma->ggtt_view.type); } if (obj->stolen) - seq_printf(m, " (stolen: %08lx)", obj->stolen->start); + seq_printf(m, " (stolen: %08llx)", obj->stolen->start); if (obj->pin_mappable || obj->fault_mappable) { char s[3], *t = s; if (obj->pin_mappable) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 8039cec..5c66b56 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -622,7 +622,7 @@ static int i915_drm_suspend(struct drm_device *dev) return 0; } -static int i915_drm_suspend_late(struct drm_device *drm_dev) +static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation) { struct drm_i915_private *dev_priv = drm_dev->dev_private; int ret; @@ -636,7 +636,17 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev) } pci_disable_device(drm_dev->pdev); - pci_set_power_state(drm_dev->pdev, PCI_D3hot); + /* + * During hibernation on some GEN4 platforms the BIOS may try to access + * the device even though it's already in D3 and hang the machine. So + * leave the device in D0 on those platforms and hope the BIOS will + * power down the device properly. Platforms where this was seen: + * Lenovo Thinkpad X301, X61s + */ + if (!(hibernation && + drm_dev->pdev->subsystem_vendor == PCI_VENDOR_ID_LENOVO && + INTEL_INFO(dev_priv)->gen == 4)) + pci_set_power_state(drm_dev->pdev, PCI_D3hot); return 0; } @@ -662,7 +672,7 @@ int i915_suspend_legacy(struct drm_device *dev, pm_message_t state) if (error) return error; - return i915_drm_suspend_late(dev); + return i915_drm_suspend_late(dev, false); } static int i915_drm_resume(struct drm_device *dev) @@ -950,7 +960,17 @@ static int i915_pm_suspend_late(struct device *dev) if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - return i915_drm_suspend_late(drm_dev); + return i915_drm_suspend_late(drm_dev, false); +} + +static int i915_pm_poweroff_late(struct device *dev) +{ + struct drm_device *drm_dev = dev_to_i915(dev)->dev; + + if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) + return 0; + + return i915_drm_suspend_late(drm_dev, true); } static int i915_pm_resume_early(struct device *dev) @@ -1075,6 +1095,7 @@ static void vlv_save_gunit_s0ix_state(struct drm_i915_private *dev_priv) /* Gunit-Display CZ domain, 0x182028-0x1821CF */ s->gu_ctl0 = I915_READ(VLV_GU_CTL0); s->gu_ctl1 = I915_READ(VLV_GU_CTL1); + s->pcbr = I915_READ(VLV_PCBR); s->clock_gate_dis2 = I915_READ(VLV_GUNIT_CLOCK_GATE2); /* @@ -1169,6 +1190,7 @@ static void vlv_restore_gunit_s0ix_state(struct drm_i915_private *dev_priv) /* Gunit-Display CZ domain, 0x182028-0x1821CF */ I915_WRITE(VLV_GU_CTL0, s->gu_ctl0); I915_WRITE(VLV_GU_CTL1, s->gu_ctl1); + I915_WRITE(VLV_PCBR, s->pcbr); I915_WRITE(VLV_GUNIT_CLOCK_GATE2, s->clock_gate_dis2); } @@ -1177,19 +1199,7 @@ int vlv_force_gfx_clock(struct drm_i915_private *dev_priv, bool force_on) u32 val; int err; - val = I915_READ(VLV_GTLC_SURVIVABILITY_REG); - WARN_ON(!!(val & VLV_GFX_CLK_FORCE_ON_BIT) == force_on); - #define COND (I915_READ(VLV_GTLC_SURVIVABILITY_REG) & VLV_GFX_CLK_STATUS_BIT) - /* Wait for a previous force-off to settle */ - if (force_on) { - err = wait_for(!COND, 20); - if (err) { - DRM_ERROR("timeout waiting for GFX clock force-off (%08x)\n", - I915_READ(VLV_GTLC_SURVIVABILITY_REG)); - return err; - } - } val = I915_READ(VLV_GTLC_SURVIVABILITY_REG); val &= ~VLV_GFX_CLK_FORCE_ON_BIT; @@ -1520,7 +1530,7 @@ static const struct dev_pm_ops i915_pm_ops = { .thaw_early = i915_pm_resume_early, .thaw = i915_pm_resume, .poweroff = i915_pm_suspend, - .poweroff_late = i915_pm_suspend_late, + .poweroff_late = i915_pm_poweroff_late, .restore_early = i915_pm_resume_early, .restore = i915_pm_resume, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f2a825e..b4faa2d 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1094,6 +1094,7 @@ struct vlv_s0ix_state { /* Display 2 CZ domain */ u32 gu_ctl0; u32 gu_ctl1; + u32 pcbr; u32 clock_gate_dis2; }; @@ -2114,6 +2115,9 @@ void i915_gem_track_fb(struct drm_i915_gem_object *old, * number comparisons on buffer last_read|write_seqno. It also allows an * emission time to be associated with the request for tracking how far ahead * of the GPU the submission is. + * + * The requests are reference counted, so upon creation they should have an + * initial reference taken using kref_init */ struct drm_i915_gem_request { struct kref ref; @@ -2137,7 +2141,16 @@ struct drm_i915_gem_request { /** Position in the ringbuffer of the end of the whole request */ u32 tail; - /** Context related to this request */ + /** + * Context related to this request + * Contexts are refcounted, so when this request is associated with a + * context, we must increment the context's refcount, to guarantee that + * it persists while any request is linked to it. Requests themselves + * are also refcounted, so the request will only be freed when the last + * reference to it is dismissed, and the code in + * i915_gem_request_free() will then decrement the refcount on the + * context. + */ struct intel_context *ctx; /** Batch buffer related to this request if any */ @@ -2374,6 +2387,7 @@ struct drm_i915_cmd_table { (INTEL_DEVID(dev) & 0xFF00) == 0x0C00) #define IS_BDW_ULT(dev) (IS_BROADWELL(dev) && \ ((INTEL_DEVID(dev) & 0xf) == 0x6 || \ + (INTEL_DEVID(dev) & 0xf) == 0xb || \ (INTEL_DEVID(dev) & 0xf) == 0xe)) #define IS_BDW_GT3(dev) (IS_BROADWELL(dev) && \ (INTEL_DEVID(dev) & 0x00F0) == 0x0020) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c26d36c..27ea6bd 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2659,8 +2659,7 @@ static void i915_gem_reset_ring_cleanup(struct drm_i915_private *dev_priv, if (submit_req->ctx != ring->default_context) intel_lr_context_unpin(ring, submit_req->ctx); - i915_gem_context_unreference(submit_req->ctx); - kfree(submit_req); + i915_gem_request_unreference(submit_req); } /* @@ -2738,24 +2737,11 @@ i915_gem_retire_requests_ring(struct intel_engine_cs *ring) WARN_ON(i915_verify_lists(ring->dev)); - /* Move any buffers on the active list that are no longer referenced - * by the ringbuffer to the flushing/inactive lists as appropriate, - * before we free the context associated with the requests. + /* Retire requests first as we use it above for the early return. + * If we retire requests last, we may use a later seqno and so clear + * the requests lists without clearing the active list, leading to + * confusion. */ - while (!list_empty(&ring->active_list)) { - struct drm_i915_gem_object *obj; - - obj = list_first_entry(&ring->active_list, - struct drm_i915_gem_object, - ring_list); - - if (!i915_gem_request_completed(obj->last_read_req, true)) - break; - - i915_gem_object_move_to_inactive(obj); - } - - while (!list_empty(&ring->request_list)) { struct drm_i915_gem_request *request; struct intel_ringbuffer *ringbuf; @@ -2790,6 +2776,23 @@ i915_gem_retire_requests_ring(struct intel_engine_cs *ring) i915_gem_free_request(request); } + /* Move any buffers on the active list that are no longer referenced + * by the ringbuffer to the flushing/inactive lists as appropriate, + * before we free the context associated with the requests. + */ + while (!list_empty(&ring->active_list)) { + struct drm_i915_gem_object *obj; + + obj = list_first_entry(&ring->active_list, + struct drm_i915_gem_object, + ring_list); + + if (!i915_gem_request_completed(obj->last_read_req, true)) + break; + + i915_gem_object_move_to_inactive(obj); + } + if (unlikely(ring->trace_irq_req && i915_gem_request_completed(ring->trace_irq_req, true))) { ring->irq_put(ring); @@ -2937,9 +2940,9 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) req = obj->last_read_req; /* Do this after OLR check to make sure we make forward progress polling - * on this IOCTL with a timeout <=0 (like busy ioctl) + * on this IOCTL with a timeout == 0 (like busy ioctl) */ - if (args->timeout_ns <= 0) { + if (args->timeout_ns == 0) { ret = -ETIME; goto out; } @@ -2949,7 +2952,8 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) i915_gem_request_reference(req); mutex_unlock(&dev->struct_mutex); - ret = __i915_wait_request(req, reset_counter, true, &args->timeout_ns, + ret = __i915_wait_request(req, reset_counter, true, + args->timeout_ns > 0 ? &args->timeout_ns : NULL, file->driver_priv); mutex_lock(&dev->struct_mutex); i915_gem_request_unreference(req); @@ -4793,6 +4797,9 @@ i915_gem_init_hw(struct drm_device *dev) if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt()) return -EIO; + /* Double layer security blanket, see i915_gem_init() */ + intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); + if (dev_priv->ellc_size) I915_WRITE(HSW_IDICR, I915_READ(HSW_IDICR) | IDIHASHMSK(0xf)); @@ -4825,7 +4832,7 @@ i915_gem_init_hw(struct drm_device *dev) for_each_ring(ring, dev_priv, i) { ret = ring->init_hw(ring); if (ret) - return ret; + goto out; } for (i = 0; i < NUM_L3_SLICES(dev); i++) @@ -4842,9 +4849,11 @@ i915_gem_init_hw(struct drm_device *dev) DRM_ERROR("Context enable failed %d\n", ret); i915_gem_cleanup_ringbuffer(dev); - return ret; + goto out; } +out: + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); return ret; } @@ -4878,6 +4887,14 @@ int i915_gem_init(struct drm_device *dev) dev_priv->gt.stop_ring = intel_logical_ring_stop; } + /* This is just a security blanket to placate dragons. + * On some systems, we very sporadically observe that the first TLBs + * used by the CS may be stale, despite us poking the TLB reset. If + * we hold the forcewake during initialisation these problems + * just magically go away. + */ + intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); + ret = i915_gem_init_userptr(dev); if (ret) goto out_unlock; @@ -4904,6 +4921,7 @@ int i915_gem_init(struct drm_device *dev) } out_unlock: + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); mutex_unlock(&dev->struct_mutex); return ret; diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index b773368..38a7425 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -1487,7 +1487,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, goto err; } - if (i915_needs_cmd_parser(ring)) { + if (i915_needs_cmd_parser(ring) && args->batch_len) { batch_obj = i915_gem_execbuffer_parse(ring, &shadow_exec_entry, eb, diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 746f77f..dccdc8a 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -1145,7 +1145,7 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) ppgtt->base.clear_range(&ppgtt->base, 0, ppgtt->base.total, true); - DRM_DEBUG_DRIVER("Allocated pde space (%ldM) at GTT entry: %lx\n", + DRM_DEBUG_DRIVER("Allocated pde space (%lldM) at GTT entry: %llx\n", ppgtt->node.size >> 20, ppgtt->node.start / PAGE_SIZE); @@ -1713,8 +1713,8 @@ void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj) static void i915_gtt_color_adjust(struct drm_mm_node *node, unsigned long color, - unsigned long *start, - unsigned long *end) + u64 *start, + u64 *end) { if (node->color != color) *start += 4096; diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index a204584..9c6f93e 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -485,10 +485,8 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, stolen_offset, gtt_offset, size); /* KISS and expect everything to be page-aligned */ - BUG_ON(stolen_offset & 4095); - BUG_ON(size & 4095); - - if (WARN_ON(size == 0)) + if (WARN_ON(size == 0) || WARN_ON(size & 4095) || + WARN_ON(stolen_offset & 4095)) return NULL; stolen = kzalloc(sizeof(*stolen), GFP_KERNEL); diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 7a24bd1..6377b22 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -335,9 +335,10 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, return -EINVAL; } + mutex_lock(&dev->struct_mutex); if (i915_gem_obj_is_pinned(obj) || obj->framebuffer_references) { - drm_gem_object_unreference_unlocked(&obj->base); - return -EBUSY; + ret = -EBUSY; + goto err; } if (args->tiling_mode == I915_TILING_NONE) { @@ -369,7 +370,6 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, } } - mutex_lock(&dev->struct_mutex); if (args->tiling_mode != obj->tiling_mode || args->stride != obj->stride) { /* We need to rebind the object if its current allocation @@ -424,6 +424,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, obj->bit_17 = NULL; } +err: drm_gem_object_unreference(&obj->base); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 4145d95..ede5bbb 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1892,6 +1892,9 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) u32 iir, gt_iir, pm_iir; irqreturn_t ret = IRQ_NONE; + if (!intel_irqs_enabled(dev_priv)) + return IRQ_NONE; + while (true) { /* Find, clear, then process each source of interrupt */ @@ -1936,6 +1939,9 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg) u32 master_ctl, iir; irqreturn_t ret = IRQ_NONE; + if (!intel_irqs_enabled(dev_priv)) + return IRQ_NONE; + for (;;) { master_ctl = I915_READ(GEN8_MASTER_IRQ) & ~GEN8_MASTER_IRQ_CONTROL; iir = I915_READ(VLV_IIR); @@ -2208,6 +2214,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) u32 de_iir, gt_iir, de_ier, sde_ier = 0; irqreturn_t ret = IRQ_NONE; + if (!intel_irqs_enabled(dev_priv)) + return IRQ_NONE; + /* We get interrupts on unclaimed registers, so check for this before we * do any I915_{READ,WRITE}. */ intel_uncore_check_errors(dev); @@ -2279,6 +2288,9 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) enum pipe pipe; u32 aux_mask = GEN8_AUX_CHANNEL_A; + if (!intel_irqs_enabled(dev_priv)) + return IRQ_NONE; + if (IS_GEN9(dev)) aux_mask |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C | GEN9_AUX_CHANNEL_D; @@ -3771,6 +3783,9 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg) I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; + if (!intel_irqs_enabled(dev_priv)) + return IRQ_NONE; + iir = I915_READ16(IIR); if (iir == 0) return IRQ_NONE; @@ -3951,6 +3966,9 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; int pipe, ret = IRQ_NONE; + if (!intel_irqs_enabled(dev_priv)) + return IRQ_NONE; + iir = I915_READ(IIR); do { bool irq_received = (iir & ~flip_mask) != 0; @@ -4171,6 +4189,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; + if (!intel_irqs_enabled(dev_priv)) + return IRQ_NONE; + iir = I915_READ(IIR); for (;;) { @@ -4520,6 +4541,7 @@ void intel_runtime_pm_disable_interrupts(struct drm_i915_private *dev_priv) { dev_priv->dev->driver->irq_uninstall(dev_priv->dev); dev_priv->pm.irqs_enabled = false; + synchronize_irq(dev_priv->dev->irq); } /** diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3d220a6..f75173c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -37,6 +37,7 @@ #include <drm/i915_drm.h> #include "i915_drv.h" #include "i915_trace.h" +#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_dp_helper.h> #include <drm/drm_crtc_helper.h> @@ -2371,13 +2372,19 @@ intel_alloc_plane_obj(struct intel_crtc *crtc, struct drm_device *dev = crtc->base.dev; struct drm_i915_gem_object *obj = NULL; struct drm_mode_fb_cmd2 mode_cmd = { 0 }; - u32 base = plane_config->base; + u32 base_aligned = round_down(plane_config->base, PAGE_SIZE); + u32 size_aligned = round_up(plane_config->base + plane_config->size, + PAGE_SIZE); + + size_aligned -= base_aligned; if (plane_config->size == 0) return false; - obj = i915_gem_object_create_stolen_for_preallocated(dev, base, base, - plane_config->size); + obj = i915_gem_object_create_stolen_for_preallocated(dev, + base_aligned, + base_aligned, + size_aligned); if (!obj) return false; @@ -2410,6 +2417,14 @@ out_unref_obj: return false; } +/* Update plane->state->fb to match plane->fb after driver-internal updates */ +static void +update_state_fb(struct drm_plane *plane) +{ + if (plane->fb != plane->state->fb) + drm_atomic_set_fb_for_plane(plane->state, plane->fb); +} + static void intel_find_plane_obj(struct intel_crtc *intel_crtc, struct intel_initial_plane_config *plane_config) @@ -2423,8 +2438,15 @@ intel_find_plane_obj(struct intel_crtc *intel_crtc, if (!intel_crtc->base.primary->fb) return; - if (intel_alloc_plane_obj(intel_crtc, plane_config)) + if (intel_alloc_plane_obj(intel_crtc, plane_config)) { + struct drm_plane *primary = intel_crtc->base.primary; + + primary->state->crtc = &intel_crtc->base; + primary->crtc = &intel_crtc->base; + update_state_fb(primary); + return; + } kfree(intel_crtc->base.primary->fb); intel_crtc->base.primary->fb = NULL; @@ -2447,15 +2469,21 @@ intel_find_plane_obj(struct intel_crtc *intel_crtc, continue; if (i915_gem_obj_ggtt_offset(obj) == plane_config->base) { + struct drm_plane *primary = intel_crtc->base.primary; + if (obj->tiling_mode != I915_TILING_NONE) dev_priv->preserve_bios_swizzle = true; drm_framebuffer_reference(c->primary->fb); - intel_crtc->base.primary->fb = c->primary->fb; + primary->fb = c->primary->fb; + primary->state->crtc = &intel_crtc->base; + primary->crtc = &intel_crtc->base; obj->frontbuffer_bits |= INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe); break; } } + + update_state_fb(intel_crtc->base.primary); } static void i9xx_update_primary_plane(struct drm_crtc *crtc, @@ -2725,10 +2753,19 @@ static void skylake_update_primary_plane(struct drm_crtc *crtc, case DRM_FORMAT_XRGB8888: plane_ctl |= PLANE_CTL_FORMAT_XRGB_8888; break; + case DRM_FORMAT_ARGB8888: + plane_ctl |= PLANE_CTL_FORMAT_XRGB_8888; + plane_ctl |= PLANE_CTL_ALPHA_SW_PREMULTIPLY; + break; case DRM_FORMAT_XBGR8888: plane_ctl |= PLANE_CTL_ORDER_RGBX; plane_ctl |= PLANE_CTL_FORMAT_XRGB_8888; break; + case DRM_FORMAT_ABGR8888: + plane_ctl |= PLANE_CTL_ORDER_RGBX; + plane_ctl |= PLANE_CTL_FORMAT_XRGB_8888; + plane_ctl |= PLANE_CTL_ALPHA_SW_PREMULTIPLY; + break; case DRM_FORMAT_XRGB2101010: plane_ctl |= PLANE_CTL_FORMAT_XRGB_2101010; break; @@ -6587,6 +6624,10 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc, struct drm_framebuffer *fb; struct intel_framebuffer *intel_fb; + val = I915_READ(DSPCNTR(plane)); + if (!(val & DISPLAY_PLANE_ENABLE)) + return; + intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); if (!intel_fb) { DRM_DEBUG_KMS("failed to alloc fb\n"); @@ -6595,8 +6636,6 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc, fb = &intel_fb->base; - val = I915_READ(DSPCNTR(plane)); - if (INTEL_INFO(dev)->gen >= 4) if (val & DISPPLANE_TILED) plane_config->tiling = I915_TILING_X; @@ -6627,7 +6666,7 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc, aligned_height = intel_fb_align_height(dev, fb->height, plane_config->tiling); - plane_config->size = PAGE_ALIGN(fb->pitches[0] * aligned_height); + plane_config->size = fb->pitches[0] * aligned_height; DRM_DEBUG_KMS("pipe/plane %c/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", pipe_name(pipe), plane, fb->width, fb->height, @@ -7628,6 +7667,9 @@ skylake_get_initial_plane_config(struct intel_crtc *crtc, fb = &intel_fb->base; val = I915_READ(PLANE_CTL(pipe, 0)); + if (!(val & PLANE_CTL_ENABLE)) + goto error; + if (val & PLANE_CTL_TILED_MASK) plane_config->tiling = I915_TILING_X; @@ -7664,7 +7706,7 @@ skylake_get_initial_plane_config(struct intel_crtc *crtc, aligned_height = intel_fb_align_height(dev, fb->height, plane_config->tiling); - plane_config->size = ALIGN(fb->pitches[0] * aligned_height, PAGE_SIZE); + plane_config->size = fb->pitches[0] * aligned_height; DRM_DEBUG_KMS("pipe %c with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", pipe_name(pipe), fb->width, fb->height, @@ -7715,6 +7757,10 @@ ironlake_get_initial_plane_config(struct intel_crtc *crtc, struct drm_framebuffer *fb; struct intel_framebuffer *intel_fb; + val = I915_READ(DSPCNTR(pipe)); + if (!(val & DISPLAY_PLANE_ENABLE)) + return; + intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); if (!intel_fb) { DRM_DEBUG_KMS("failed to alloc fb\n"); @@ -7723,8 +7769,6 @@ ironlake_get_initial_plane_config(struct intel_crtc *crtc, fb = &intel_fb->base; - val = I915_READ(DSPCNTR(pipe)); - if (INTEL_INFO(dev)->gen >= 4) if (val & DISPPLANE_TILED) plane_config->tiling = I915_TILING_X; @@ -7755,7 +7799,7 @@ ironlake_get_initial_plane_config(struct intel_crtc *crtc, aligned_height = intel_fb_align_height(dev, fb->height, plane_config->tiling); - plane_config->size = PAGE_ALIGN(fb->pitches[0] * aligned_height); + plane_config->size = fb->pitches[0] * aligned_height; DRM_DEBUG_KMS("pipe %c with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", pipe_name(pipe), fb->width, fb->height, @@ -8698,6 +8742,7 @@ retry: old->release_fb->funcs->destroy(old->release_fb); goto fail; } + crtc->primary->crtc = crtc; /* let the connector get through one full cycle before testing */ intel_wait_for_vblank(dev, intel_crtc->pipe); @@ -9700,7 +9745,7 @@ void intel_check_page_flip(struct drm_device *dev, int pipe) struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - WARN_ON(!in_irq()); + WARN_ON(!in_interrupt()); if (crtc == NULL) return; @@ -9800,6 +9845,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, drm_gem_object_reference(&obj->base); crtc->primary->fb = fb; + update_state_fb(crtc->primary); work->pending_flip_obj = obj; @@ -9868,6 +9914,7 @@ cleanup_unpin: cleanup_pending: atomic_dec(&intel_crtc->unpin_work_count); crtc->primary->fb = old_fb; + update_state_fb(crtc->primary); drm_gem_object_unreference(&work->old_fb_obj->base); drm_gem_object_unreference(&obj->base); mutex_unlock(&dev->struct_mutex); @@ -12182,9 +12229,6 @@ intel_check_cursor_plane(struct drm_plane *plane, return -ENOMEM; } - if (fb == crtc->cursor->fb) - return 0; - /* we only need to pin inside GTT if cursor is non-phy */ mutex_lock(&dev->struct_mutex); if (!INTEL_INFO(dev)->cursor_needs_physical && obj->tiling_mode) { @@ -13096,6 +13140,9 @@ static struct intel_quirk intel_quirks[] = { /* HP Chromebook 14 (Celeron 2955U) */ { 0x0a06, 0x103c, 0x21ed, quirk_backlight_present }, + + /* Dell Chromebook 11 */ + { 0x0a06, 0x1028, 0x0a35, quirk_backlight_present }, }; static void intel_init_quirks(struct drm_device *dev) @@ -13702,6 +13749,7 @@ void intel_modeset_gem_init(struct drm_device *dev) to_intel_crtc(c)->pipe); drm_framebuffer_unreference(c->primary->fb); c->primary->fb = NULL; + update_state_fb(c->primary); } } mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/intel_fifo_underrun.c b/drivers/gpu/drm/i915/intel_fifo_underrun.c index 04e248d..54daa66 100644 --- a/drivers/gpu/drm/i915/intel_fifo_underrun.c +++ b/drivers/gpu/drm/i915/intel_fifo_underrun.c @@ -282,16 +282,6 @@ bool intel_set_cpu_fifo_underrun_reporting(struct drm_i915_private *dev_priv, return ret; } -static bool -__cpu_fifo_underrun_reporting_enabled(struct drm_i915_private *dev_priv, - enum pipe pipe) -{ - struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - return !intel_crtc->cpu_fifo_underrun_disabled; -} - /** * intel_set_pch_fifo_underrun_reporting - set PCH fifo underrun reporting state * @dev_priv: i915 device instance @@ -352,9 +342,15 @@ bool intel_set_pch_fifo_underrun_reporting(struct drm_i915_private *dev_priv, void intel_cpu_fifo_underrun_irq_handler(struct drm_i915_private *dev_priv, enum pipe pipe) { + struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; + + /* We may be called too early in init, thanks BIOS! */ + if (crtc == NULL) + return; + /* GMCH can't disable fifo underruns, filter them. */ if (HAS_GMCH_DISPLAY(dev_priv->dev) && - !__cpu_fifo_underrun_reporting_enabled(dev_priv, pipe)) + to_intel_crtc(crtc)->cpu_fifo_underrun_disabled) return; if (intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false)) diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 0f358c5..e8d3da9 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -503,18 +503,19 @@ static int execlists_context_queue(struct intel_engine_cs *ring, * If there isn't a request associated with this submission, * create one as a temporary holder. */ - WARN(1, "execlist context submission without request"); request = kzalloc(sizeof(*request), GFP_KERNEL); if (request == NULL) return -ENOMEM; request->ring = ring; request->ctx = to; + kref_init(&request->ref); + request->uniq = dev_priv->request_uniq++; + i915_gem_context_reference(request->ctx); } else { + i915_gem_request_reference(request); WARN_ON(to != request->ctx); } request->tail = tail; - i915_gem_request_reference(request); - i915_gem_context_reference(request->ctx); intel_runtime_pm_get(dev_priv); @@ -731,7 +732,6 @@ void intel_execlists_retire_requests(struct intel_engine_cs *ring) if (ctx_obj && (ctx != ring->default_context)) intel_lr_context_unpin(ring, ctx); intel_runtime_pm_put(dev_priv); - i915_gem_context_unreference(ctx); list_del(&req->execlist_link); i915_gem_request_unreference(req); } diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 0a52c44..9c5451c 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1322,7 +1322,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); plane = drm_plane_find(dev, set->plane_id); - if (!plane) { + if (!plane || plane->type != DRM_PLANE_TYPE_OVERLAY) { ret = -ENOENT; goto out_unlock; } @@ -1349,7 +1349,7 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); plane = drm_plane_find(dev, get->plane_id); - if (!plane) { + if (!plane || plane->type != DRM_PLANE_TYPE_OVERLAY) { ret = -ENOENT; goto out_unlock; } diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index c47a3ba..4e8fb89 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -1048,8 +1048,14 @@ static void intel_uncore_fw_domains_init(struct drm_device *dev) /* We need to init first for ECOBUS access and then * determine later if we want to reinit, in case of MT access is - * not working + * not working. In this stage we don't know which flavour this + * ivb is, so it is better to reset also the gen6 fw registers + * before the ecobus check. */ + + __raw_i915_write32(dev_priv, FORCEWAKE, 0); + __raw_posting_read(dev_priv, ECOBUS); + fw_domain_init(dev_priv, FW_DOMAIN_ID_RENDER, FORCEWAKE_MT, FORCEWAKE_MT_ACK); diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c index 121d30c..87fe8ed 100644 --- a/drivers/gpu/drm/imx/dw_hdmi-imx.c +++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c @@ -70,7 +70,9 @@ static const struct dw_hdmi_curr_ctrl imx_cur_ctr[] = { 118800000, { 0x091c, 0x091c, 0x06dc }, }, { 216000000, { 0x06dc, 0x0b5c, 0x091c }, - } + }, { + ~0UL, { 0x0000, 0x0000, 0x0000 }, + }, }; static const struct dw_hdmi_sym_term imx_sym_term[] = { @@ -136,11 +138,34 @@ static struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = { .destroy = drm_encoder_cleanup, }; +static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con, + struct drm_display_mode *mode) +{ + if (mode->clock < 13500) + return MODE_CLOCK_LOW; + if (mode->clock > 266000) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static enum drm_mode_status imx6dl_hdmi_mode_valid(struct drm_connector *con, + struct drm_display_mode *mode) +{ + if (mode->clock < 13500) + return MODE_CLOCK_LOW; + if (mode->clock > 270000) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + static struct dw_hdmi_plat_data imx6q_hdmi_drv_data = { - .mpll_cfg = imx_mpll_cfg, - .cur_ctr = imx_cur_ctr, - .sym_term = imx_sym_term, - .dev_type = IMX6Q_HDMI, + .mpll_cfg = imx_mpll_cfg, + .cur_ctr = imx_cur_ctr, + .sym_term = imx_sym_term, + .dev_type = IMX6Q_HDMI, + .mode_valid = imx6q_hdmi_mode_valid, }; static struct dw_hdmi_plat_data imx6dl_hdmi_drv_data = { @@ -148,6 +173,7 @@ static struct dw_hdmi_plat_data imx6dl_hdmi_drv_data = { .cur_ctr = imx_cur_ctr, .sym_term = imx_sym_term, .dev_type = IMX6DL_HDMI, + .mode_valid = imx6dl_hdmi_mode_valid, }; static const struct of_device_id dw_hdmi_imx_dt_ids[] = { diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c index 1b86aac..2d6dc94 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/imx-ldb.c @@ -163,22 +163,7 @@ static void imx_ldb_encoder_prepare(struct drm_encoder *encoder) { struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); struct imx_ldb *ldb = imx_ldb_ch->ldb; - struct drm_display_mode *mode = &encoder->crtc->hwmode; u32 pixel_fmt; - unsigned long serial_clk; - unsigned long di_clk = mode->clock * 1000; - int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder); - - if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { - /* dual channel LVDS mode */ - serial_clk = 3500UL * mode->clock; - imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk); - imx_ldb_set_clock(ldb, mux, 1, serial_clk, di_clk); - } else { - serial_clk = 7000UL * mode->clock; - imx_ldb_set_clock(ldb, mux, imx_ldb_ch->chno, serial_clk, - di_clk); - } switch (imx_ldb_ch->chno) { case 0: @@ -247,6 +232,9 @@ static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder, struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); struct imx_ldb *ldb = imx_ldb_ch->ldb; int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; + unsigned long serial_clk; + unsigned long di_clk = mode->clock * 1000; + int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder); if (mode->clock > 170000) { dev_warn(ldb->dev, @@ -257,6 +245,16 @@ static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder, "%s: mode exceeds 85 MHz pixel clock\n", __func__); } + if (dual) { + serial_clk = 3500UL * mode->clock; + imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk); + imx_ldb_set_clock(ldb, mux, 1, serial_clk, di_clk); + } else { + serial_clk = 7000UL * mode->clock; + imx_ldb_set_clock(ldb, mux, imx_ldb_ch->chno, serial_clk, + di_clk); + } + /* FIXME - assumes straight connections DI0 --> CH0, DI1 --> CH1 */ if (imx_ldb_ch == &ldb->channel[0]) { if (mode->flags & DRM_MODE_FLAG_NVSYNC) diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 5e83e00..900dda6 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -236,8 +236,11 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) } panel_node = of_parse_phandle(np, "fsl,panel", 0); - if (panel_node) + if (panel_node) { imxpd->panel = of_drm_find_panel(panel_node); + if (!imxpd->panel) + return -EPROBE_DEFER; + } imxpd->dev = dev; diff --git a/drivers/gpu/drm/msm/edp/edp_ctrl.c b/drivers/gpu/drm/msm/edp/edp_ctrl.c index 3e24621..0ec5abd 100644 --- a/drivers/gpu/drm/msm/edp/edp_ctrl.c +++ b/drivers/gpu/drm/msm/edp/edp_ctrl.c @@ -332,7 +332,7 @@ static int edp_regulator_enable(struct edp_ctrl *ctrl) goto vdda_set_fail; } - ret = regulator_set_optimum_mode(ctrl->vdda_vreg, VDDA_UA_ON_LOAD); + ret = regulator_set_load(ctrl->vdda_vreg, VDDA_UA_ON_LOAD); if (ret < 0) { pr_err("%s: vdda_vreg set regulator mode failed.\n", __func__); goto vdda_set_fail; @@ -356,7 +356,7 @@ static int edp_regulator_enable(struct edp_ctrl *ctrl) lvl_enable_fail: regulator_disable(ctrl->vdda_vreg); vdda_enable_fail: - regulator_set_optimum_mode(ctrl->vdda_vreg, VDDA_UA_OFF_LOAD); + regulator_set_load(ctrl->vdda_vreg, VDDA_UA_OFF_LOAD); vdda_set_fail: return ret; } @@ -365,7 +365,7 @@ static void edp_regulator_disable(struct edp_ctrl *ctrl) { regulator_disable(ctrl->lvl_vreg); regulator_disable(ctrl->vdda_vreg); - regulator_set_optimum_mode(ctrl->vdda_vreg, VDDA_UA_OFF_LOAD); + regulator_set_load(ctrl->vdda_vreg, VDDA_UA_OFF_LOAD); } static int edp_gpio_config(struct edp_ctrl *ctrl) diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c index 8edd531c..7369ee7 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_irq.c @@ -32,7 +32,10 @@ static void mdp4_irq_error_handler(struct mdp_irq *irq, uint32_t irqstatus) void mdp4_irq_preinstall(struct msm_kms *kms) { struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); + mdp4_enable(mdp4_kms); mdp4_write(mdp4_kms, REG_MDP4_INTR_CLEAR, 0xffffffff); + mdp4_write(mdp4_kms, REG_MDP4_INTR_ENABLE, 0x00000000); + mdp4_disable(mdp4_kms); } int mdp4_irq_postinstall(struct msm_kms *kms) @@ -53,7 +56,9 @@ int mdp4_irq_postinstall(struct msm_kms *kms) void mdp4_irq_uninstall(struct msm_kms *kms) { struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); + mdp4_enable(mdp4_kms); mdp4_write(mdp4_kms, REG_MDP4_INTR_ENABLE, 0x00000000); + mdp4_disable(mdp4_kms); } irqreturn_t mdp4_irq(struct msm_kms *kms) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h index 09b4a25..c276624 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h @@ -8,17 +8,9 @@ http://github.com/freedreno/envytools/ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2014-12-05 15:34:49) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20908 bytes, from 2014-12-08 16:13:00) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2357 bytes, from 2014-12-08 16:13:00) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 27208 bytes, from 2015-01-13 23:56:11) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-10-31 16:48:57) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 26848 bytes, from 2015-01-13 23:55:57) -- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 8253 bytes, from 2014-12-08 16:13:00) +- /local/mnt2/workspace2/sviau/envytools/rnndb/mdp/mdp5.xml ( 27229 bytes, from 2015-02-10 17:00:41) +- /local/mnt2/workspace2/sviau/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2014-06-02 18:31:15) +- /local/mnt2/workspace2/sviau/envytools/rnndb/mdp/mdp_common.xml ( 2357 bytes, from 2015-01-23 16:20:19) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) @@ -910,6 +902,7 @@ static inline uint32_t __offset_LM(uint32_t idx) case 2: return (mdp5_cfg->lm.base[2]); case 3: return (mdp5_cfg->lm.base[3]); case 4: return (mdp5_cfg->lm.base[4]); + case 5: return (mdp5_cfg->lm.base[5]); default: return INVALID_IDX(idx); } } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 46fac54..2f2863c 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -62,8 +62,8 @@ struct mdp5_crtc { /* current cursor being scanned out: */ struct drm_gem_object *scanout_bo; - uint32_t width; - uint32_t height; + uint32_t width, height; + uint32_t x, y; } cursor; }; #define to_mdp5_crtc(x) container_of(x, struct mdp5_crtc, base) @@ -103,8 +103,8 @@ static void crtc_flush_all(struct drm_crtc *crtc) struct drm_plane *plane; uint32_t flush_mask = 0; - /* we could have already released CTL in the disable path: */ - if (!mdp5_crtc->ctl) + /* this should not happen: */ + if (WARN_ON(!mdp5_crtc->ctl)) return; drm_atomic_crtc_for_each_plane(plane, crtc) { @@ -143,6 +143,11 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) drm_atomic_crtc_for_each_plane(plane, crtc) { mdp5_plane_complete_flip(plane); } + + if (mdp5_crtc->ctl && !crtc->state->enable) { + mdp5_ctl_release(mdp5_crtc->ctl); + mdp5_crtc->ctl = NULL; + } } static void unref_cursor_worker(struct drm_flip_work *work, void *val) @@ -386,14 +391,17 @@ static void mdp5_crtc_atomic_flush(struct drm_crtc *crtc) mdp5_crtc->event = crtc->state->event; spin_unlock_irqrestore(&dev->event_lock, flags); + /* + * If no CTL has been allocated in mdp5_crtc_atomic_check(), + * it means we are trying to flush a CRTC whose state is disabled: + * nothing else needs to be done. + */ + if (unlikely(!mdp5_crtc->ctl)) + return; + blend_setup(crtc); crtc_flush_all(crtc); request_pending(crtc, PENDING_FLIP); - - if (mdp5_crtc->ctl && !crtc->state->enable) { - mdp5_ctl_release(mdp5_crtc->ctl); - mdp5_crtc->ctl = NULL; - } } static int mdp5_crtc_set_property(struct drm_crtc *crtc, @@ -403,6 +411,32 @@ static int mdp5_crtc_set_property(struct drm_crtc *crtc, return -EINVAL; } +static void get_roi(struct drm_crtc *crtc, uint32_t *roi_w, uint32_t *roi_h) +{ + struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + uint32_t xres = crtc->mode.hdisplay; + uint32_t yres = crtc->mode.vdisplay; + + /* + * Cursor Region Of Interest (ROI) is a plane read from cursor + * buffer to render. The ROI region is determined by the visibility of + * the cursor point. In the default Cursor image the cursor point will + * be at the top left of the cursor image, unless it is specified + * otherwise using hotspot feature. + * + * If the cursor point reaches the right (xres - x < cursor.width) or + * bottom (yres - y < cursor.height) boundary of the screen, then ROI + * width and ROI height need to be evaluated to crop the cursor image + * accordingly. + * (xres-x) will be new cursor width when x > (xres - cursor.width) + * (yres-y) will be new cursor height when y > (yres - cursor.height) + */ + *roi_w = min(mdp5_crtc->cursor.width, xres - + mdp5_crtc->cursor.x); + *roi_h = min(mdp5_crtc->cursor.height, yres - + mdp5_crtc->cursor.y); +} + static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file, uint32_t handle, uint32_t width, uint32_t height) @@ -416,6 +450,7 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, unsigned int depth; enum mdp5_cursor_alpha cur_alpha = CURSOR_ALPHA_PER_PIXEL; uint32_t flush_mask = mdp_ctl_flush_mask_cursor(0); + uint32_t roi_w, roi_h; unsigned long flags; if ((width > CURSOR_WIDTH) || (height > CURSOR_HEIGHT)) { @@ -446,6 +481,12 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, spin_lock_irqsave(&mdp5_crtc->cursor.lock, flags); old_bo = mdp5_crtc->cursor.scanout_bo; + mdp5_crtc->cursor.scanout_bo = cursor_bo; + mdp5_crtc->cursor.width = width; + mdp5_crtc->cursor.height = height; + + get_roi(crtc, &roi_w, &roi_h); + mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_STRIDE(lm), stride); mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_FORMAT(lm), MDP5_LM_CURSOR_FORMAT_FORMAT(CURSOR_FMT_ARGB8888)); @@ -453,19 +494,14 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, MDP5_LM_CURSOR_IMG_SIZE_SRC_H(height) | MDP5_LM_CURSOR_IMG_SIZE_SRC_W(width)); mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_SIZE(lm), - MDP5_LM_CURSOR_SIZE_ROI_H(height) | - MDP5_LM_CURSOR_SIZE_ROI_W(width)); + MDP5_LM_CURSOR_SIZE_ROI_H(roi_h) | + MDP5_LM_CURSOR_SIZE_ROI_W(roi_w)); mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_BASE_ADDR(lm), cursor_addr); - blendcfg = MDP5_LM_CURSOR_BLEND_CONFIG_BLEND_EN; - blendcfg |= MDP5_LM_CURSOR_BLEND_CONFIG_BLEND_TRANSP_EN; blendcfg |= MDP5_LM_CURSOR_BLEND_CONFIG_BLEND_ALPHA_SEL(cur_alpha); mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_BLEND_CONFIG(lm), blendcfg); - mdp5_crtc->cursor.scanout_bo = cursor_bo; - mdp5_crtc->cursor.width = width; - mdp5_crtc->cursor.height = height; spin_unlock_irqrestore(&mdp5_crtc->cursor.lock, flags); ret = mdp5_ctl_set_cursor(mdp5_crtc->ctl, true); @@ -489,31 +525,18 @@ static int mdp5_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) struct mdp5_kms *mdp5_kms = get_kms(crtc); struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); uint32_t flush_mask = mdp_ctl_flush_mask_cursor(0); - uint32_t xres = crtc->mode.hdisplay; - uint32_t yres = crtc->mode.vdisplay; uint32_t roi_w; uint32_t roi_h; unsigned long flags; - x = (x > 0) ? x : 0; - y = (y > 0) ? y : 0; + /* In case the CRTC is disabled, just drop the cursor update */ + if (unlikely(!crtc->state->enable)) + return 0; - /* - * Cursor Region Of Interest (ROI) is a plane read from cursor - * buffer to render. The ROI region is determined by the visiblity of - * the cursor point. In the default Cursor image the cursor point will - * be at the top left of the cursor image, unless it is specified - * otherwise using hotspot feature. - * - * If the cursor point reaches the right (xres - x < cursor.width) or - * bottom (yres - y < cursor.height) boundary of the screen, then ROI - * width and ROI height need to be evaluated to crop the cursor image - * accordingly. - * (xres-x) will be new cursor width when x > (xres - cursor.width) - * (yres-y) will be new cursor height when y > (yres - cursor.height) - */ - roi_w = min(mdp5_crtc->cursor.width, xres - x); - roi_h = min(mdp5_crtc->cursor.height, yres - y); + mdp5_crtc->cursor.x = x = max(x, 0); + mdp5_crtc->cursor.y = y = max(y, 0); + + get_roi(crtc, &roi_w, &roi_h); spin_lock_irqsave(&mdp5_crtc->cursor.lock, flags); mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_SIZE(mdp5_crtc->lm), @@ -544,8 +567,8 @@ static const struct drm_crtc_funcs mdp5_crtc_funcs = { static const struct drm_crtc_helper_funcs mdp5_crtc_helper_funcs = { .mode_fixup = mdp5_crtc_mode_fixup, .mode_set_nofb = mdp5_crtc_mode_set_nofb, - .prepare = mdp5_crtc_disable, - .commit = mdp5_crtc_enable, + .disable = mdp5_crtc_disable, + .enable = mdp5_crtc_enable, .atomic_check = mdp5_crtc_atomic_check, .atomic_begin = mdp5_crtc_atomic_begin, .atomic_flush = mdp5_crtc_atomic_flush, diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c index d6a14bb..af0e02f 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c @@ -267,14 +267,14 @@ static void mdp5_encoder_enable(struct drm_encoder *encoder) mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intf), 1); spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags); - mdp5_encoder->enabled = false; + mdp5_encoder->enabled = true; } static const struct drm_encoder_helper_funcs mdp5_encoder_helper_funcs = { .mode_fixup = mdp5_encoder_mode_fixup, .mode_set = mdp5_encoder_mode_set, - .prepare = mdp5_encoder_disable, - .commit = mdp5_encoder_enable, + .disable = mdp5_encoder_disable, + .enable = mdp5_encoder_enable, }; /* initialize encoder */ diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c index 70ac81e..a940710 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c @@ -34,7 +34,10 @@ static void mdp5_irq_error_handler(struct mdp_irq *irq, uint32_t irqstatus) void mdp5_irq_preinstall(struct msm_kms *kms) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); + mdp5_enable(mdp5_kms); mdp5_write(mdp5_kms, REG_MDP5_INTR_CLEAR, 0xffffffff); + mdp5_write(mdp5_kms, REG_MDP5_INTR_EN, 0x00000000); + mdp5_disable(mdp5_kms); } int mdp5_irq_postinstall(struct msm_kms *kms) @@ -57,7 +60,9 @@ int mdp5_irq_postinstall(struct msm_kms *kms) void mdp5_irq_uninstall(struct msm_kms *kms) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); + mdp5_enable(mdp5_kms); mdp5_write(mdp5_kms, REG_MDP5_INTR_EN, 0x00000000); + mdp5_disable(mdp5_kms); } static void mdp5_irq_mdp(struct mdp_kms *mdp_kms) diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 871aa21..18fd643 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -219,8 +219,10 @@ int msm_atomic_commit(struct drm_device *dev, * mark our set of crtc's as busy: */ ret = start_atomic(dev->dev_private, c->crtc_mask); - if (ret) + if (ret) { + kfree(c); return ret; + } /* * This is the point of no return - everything below never fails except diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 79924e4..6751553 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -418,7 +418,7 @@ nouveau_fbcon_create(struct drm_fb_helper *helper, nouveau_fbcon_zfill(dev, fbcon); /* To allow resizeing without swapping buffers */ - NV_INFO(drm, "allocated %dx%d fb: 0x%lx, bo %p\n", + NV_INFO(drm, "allocated %dx%d fb: 0x%llx, bo %p\n", nouveau_fb->base.width, nouveau_fb->base.height, nvbo->bo.offset, nvbo); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index 29bd539..6efa8f3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -340,11 +340,13 @@ nvkm_devobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine, /* switch mmio to cpu's native endianness */ #ifndef __BIG_ENDIAN - if (ioread32_native(map + 0x000004) != 0x00000000) + if (ioread32_native(map + 0x000004) != 0x00000000) { #else - if (ioread32_native(map + 0x000004) == 0x00000000) + if (ioread32_native(map + 0x000004) == 0x00000000) { #endif iowrite32_native(0x01000001, map + 0x000004); + ioread32_native(map); + } /* read boot0 and strapping information */ boot0 = ioread32_native(map + 0x000000); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/gm100.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/gm100.c index 539561e..108d048 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/gm100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/gm100.c @@ -142,6 +142,49 @@ gm100_identify(struct nvkm_device *device) device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; #endif break; + case 0x126: + device->cname = "GM206"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = gk104_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = gm204_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gm107_fuse_oclass; +#if 0 + /* looks to be some non-trivial changes */ + device->oclass[NVDEV_SUBDEV_CLK ] = &gk104_clk_oclass; + /* priv ring says no to 0x10eb14 writes */ + device->oclass[NVDEV_SUBDEV_THERM ] = &gm107_therm_oclass; +#endif + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = gm204_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &gk20a_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = gm107_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gm107_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &gk104_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass; + device->oclass[NVDEV_SUBDEV_PMU ] = gk208_pmu_oclass; +#if 0 + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; +#endif + device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf110_dmaeng_oclass; +#if 0 + device->oclass[NVDEV_ENGINE_FIFO ] = gk208_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass; + device->oclass[NVDEV_ENGINE_GR ] = gm107_gr_oclass; +#endif + device->oclass[NVDEV_ENGINE_DISP ] = gm204_disp_oclass; +#if 0 + device->oclass[NVDEV_ENGINE_CE0 ] = &gm204_ce0_oclass; + device->oclass[NVDEV_ENGINE_CE1 ] = &gm204_ce1_oclass; + device->oclass[NVDEV_ENGINE_CE2 ] = &gm204_ce2_oclass; + device->oclass[NVDEV_ENGINE_MSVLD ] = &gk104_msvld_oclass; + device->oclass[NVDEV_ENGINE_MSPDEC ] = &gk104_mspdec_oclass; + device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass; +#endif + break; default: nv_fatal(device, "unknown Maxwell chipset\n"); return -EINVAL; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.c index b038b6e..043e429 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/nv04.c @@ -502,72 +502,57 @@ nv04_fifo_intr(struct nvkm_subdev *subdev) { struct nvkm_device *device = nv_device(subdev); struct nv04_fifo_priv *priv = (void *)subdev; - uint32_t status, reassign; - int cnt = 0; + u32 mask = nv_rd32(priv, NV03_PFIFO_INTR_EN_0); + u32 stat = nv_rd32(priv, NV03_PFIFO_INTR_0) & mask; + u32 reassign, chid, get, sem; reassign = nv_rd32(priv, NV03_PFIFO_CACHES) & 1; - while ((status = nv_rd32(priv, NV03_PFIFO_INTR_0)) && (cnt++ < 100)) { - uint32_t chid, get; - - nv_wr32(priv, NV03_PFIFO_CACHES, 0); - - chid = nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH1) & priv->base.max; - get = nv_rd32(priv, NV03_PFIFO_CACHE1_GET); + nv_wr32(priv, NV03_PFIFO_CACHES, 0); - if (status & NV_PFIFO_INTR_CACHE_ERROR) { - nv04_fifo_cache_error(device, priv, chid, get); - status &= ~NV_PFIFO_INTR_CACHE_ERROR; - } + chid = nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH1) & priv->base.max; + get = nv_rd32(priv, NV03_PFIFO_CACHE1_GET); - if (status & NV_PFIFO_INTR_DMA_PUSHER) { - nv04_fifo_dma_pusher(device, priv, chid); - status &= ~NV_PFIFO_INTR_DMA_PUSHER; - } + if (stat & NV_PFIFO_INTR_CACHE_ERROR) { + nv04_fifo_cache_error(device, priv, chid, get); + stat &= ~NV_PFIFO_INTR_CACHE_ERROR; + } - if (status & NV_PFIFO_INTR_SEMAPHORE) { - uint32_t sem; + if (stat & NV_PFIFO_INTR_DMA_PUSHER) { + nv04_fifo_dma_pusher(device, priv, chid); + stat &= ~NV_PFIFO_INTR_DMA_PUSHER; + } - status &= ~NV_PFIFO_INTR_SEMAPHORE; - nv_wr32(priv, NV03_PFIFO_INTR_0, - NV_PFIFO_INTR_SEMAPHORE); + if (stat & NV_PFIFO_INTR_SEMAPHORE) { + stat &= ~NV_PFIFO_INTR_SEMAPHORE; + nv_wr32(priv, NV03_PFIFO_INTR_0, NV_PFIFO_INTR_SEMAPHORE); - sem = nv_rd32(priv, NV10_PFIFO_CACHE1_SEMAPHORE); - nv_wr32(priv, NV10_PFIFO_CACHE1_SEMAPHORE, sem | 0x1); + sem = nv_rd32(priv, NV10_PFIFO_CACHE1_SEMAPHORE); + nv_wr32(priv, NV10_PFIFO_CACHE1_SEMAPHORE, sem | 0x1); - nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4); - nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1); - } + nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4); + nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1); + } - if (device->card_type == NV_50) { - if (status & 0x00000010) { - status &= ~0x00000010; - nv_wr32(priv, 0x002100, 0x00000010); - } - - if (status & 0x40000000) { - nv_wr32(priv, 0x002100, 0x40000000); - nvkm_fifo_uevent(&priv->base); - status &= ~0x40000000; - } + if (device->card_type == NV_50) { + if (stat & 0x00000010) { + stat &= ~0x00000010; + nv_wr32(priv, 0x002100, 0x00000010); } - if (status) { - nv_warn(priv, "unknown intr 0x%08x, ch %d\n", - status, chid); - nv_wr32(priv, NV03_PFIFO_INTR_0, status); - status = 0; + if (stat & 0x40000000) { + nv_wr32(priv, 0x002100, 0x40000000); + nvkm_fifo_uevent(&priv->base); + stat &= ~0x40000000; } - - nv_wr32(priv, NV03_PFIFO_CACHES, reassign); } - if (status) { - nv_error(priv, "still angry after %d spins, halt\n", cnt); - nv_wr32(priv, 0x002140, 0); - nv_wr32(priv, 0x000140, 0); + if (stat) { + nv_warn(priv, "unknown intr 0x%08x\n", stat); + nv_mask(priv, NV03_PFIFO_INTR_EN_0, stat, 0x00000000); + nv_wr32(priv, NV03_PFIFO_INTR_0, stat); } - nv_wr32(priv, 0x000100, 0x00000100); + nv_wr32(priv, NV03_PFIFO_CACHES, reassign); } static int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.c index 2e7ec38..57e2c5b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.c @@ -1032,9 +1032,9 @@ gf100_grctx_generate_bundle(struct gf100_grctx *info) const int s = 8; const int b = mmio_vram(info, impl->bundle_size, (1 << s), access); mmio_refn(info, 0x408004, 0x00000000, s, b); - mmio_refn(info, 0x408008, 0x80000000 | (impl->bundle_size >> s), 0, b); + mmio_wr32(info, 0x408008, 0x80000000 | (impl->bundle_size >> s)); mmio_refn(info, 0x418808, 0x00000000, s, b); - mmio_refn(info, 0x41880c, 0x80000000 | (impl->bundle_size >> s), 0, b); + mmio_wr32(info, 0x41880c, 0x80000000 | (impl->bundle_size >> s)); } void diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c index b52300d..5e9454b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c @@ -851,9 +851,9 @@ gk104_grctx_generate_bundle(struct gf100_grctx *info) const int s = 8; const int b = mmio_vram(info, impl->bundle_size, (1 << s), access); mmio_refn(info, 0x408004, 0x00000000, s, b); - mmio_refn(info, 0x408008, 0x80000000 | (impl->bundle_size >> s), 0, b); + mmio_wr32(info, 0x408008, 0x80000000 | (impl->bundle_size >> s)); mmio_refn(info, 0x418808, 0x00000000, s, b); - mmio_refn(info, 0x41880c, 0x80000000 | (impl->bundle_size >> s), 0, b); + mmio_wr32(info, 0x41880c, 0x80000000 | (impl->bundle_size >> s)); mmio_wr32(info, 0x4064c8, (state_limit << 16) | token_limit); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c index 956f4dc..b2fae6e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c @@ -871,9 +871,9 @@ gm107_grctx_generate_bundle(struct gf100_grctx *info) const int s = 8; const int b = mmio_vram(info, impl->bundle_size, (1 << s), access); mmio_refn(info, 0x408004, 0x00000000, s, b); - mmio_refn(info, 0x408008, 0x80000000 | (impl->bundle_size >> s), 0, b); + mmio_wr32(info, 0x408008, 0x80000000 | (impl->bundle_size >> s)); mmio_refn(info, 0x418e24, 0x00000000, s, b); - mmio_refn(info, 0x418e28, 0x80000000 | (impl->bundle_size >> s), 0, b); + mmio_wr32(info, 0x418e28, 0x80000000 | (impl->bundle_size >> s)); mmio_wr32(info, 0x4064c8, (state_limit << 16) | token_limit); } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c index d1a89b2..c4e1f08 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/i2c.c @@ -74,7 +74,11 @@ dcb_i2c_parse(struct nvkm_bios *bios, u8 idx, struct dcb_i2c_entry *info) u16 ent = dcb_i2c_entry(bios, idx, &ver, &len); if (ent) { if (ver >= 0x41) { - if (!(nv_ro32(bios, ent) & 0x80000000)) + u32 ent_value = nv_ro32(bios, ent); + u8 i2c_port = (ent_value >> 27) & 0x1f; + u8 dpaux_port = (ent_value >> 22) & 0x1f; + /* value 0x1f means unused according to DCB 4.x spec */ + if (i2c_port == 0x1f && dpaux_port == 0x1f) info->type = DCB_I2C_UNUSED; else info->type = DCB_I2C_PMGR; diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index ed644a4..86807ee 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -1405,6 +1405,9 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, (x << 16) | y); viewport_w = crtc->mode.hdisplay; viewport_h = (crtc->mode.vdisplay + 1) & ~1; + if ((rdev->family >= CHIP_BONAIRE) && + (crtc->mode.flags & DRM_MODE_FLAG_INTERLACE)) + viewport_h *= 2; WREG32(EVERGREEN_VIEWPORT_SIZE + radeon_crtc->crtc_offset, (viewport_w << 16) | viewport_h); diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 5bf825d..8d74de8 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -178,6 +178,13 @@ radeon_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) switch (msg->request & ~DP_AUX_I2C_MOT) { case DP_AUX_NATIVE_WRITE: case DP_AUX_I2C_WRITE: + /* The atom implementation only supports writes with a max payload of + * 12 bytes since it uses 4 bits for the total count (header + payload) + * in the parameter space. The atom interface supports 16 byte + * payloads for reads. The hw itself supports up to 16 bytes of payload. + */ + if (WARN_ON_ONCE(msg->size > 12)) + return -E2BIG; /* tx_size needs to be 4 even for bare address packets since the atom * table needs the info in tx_buf[3]. */ diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index 7c9df1e..c39c1d0 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -731,7 +731,9 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) dig_connector = radeon_connector->con_priv; if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) { - if (radeon_audio != 0 && ASIC_IS_DCE4(rdev) && !ASIC_IS_DCE5(rdev)) + if (radeon_audio != 0 && + drm_detect_monitor_audio(radeon_connector_edid(connector)) && + ASIC_IS_DCE4(rdev) && !ASIC_IS_DCE5(rdev)) return ATOM_ENCODER_MODE_DP_AUDIO; return ATOM_ENCODER_MODE_DP; } else if (radeon_audio != 0) { @@ -747,7 +749,9 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) } break; case DRM_MODE_CONNECTOR_eDP: - if (radeon_audio != 0 && ASIC_IS_DCE4(rdev) && !ASIC_IS_DCE5(rdev)) + if (radeon_audio != 0 && + drm_detect_monitor_audio(radeon_connector_edid(connector)) && + ASIC_IS_DCE4(rdev) && !ASIC_IS_DCE5(rdev)) return ATOM_ENCODER_MODE_DP_AUDIO; return ATOM_ENCODER_MODE_DP; case DRM_MODE_CONNECTOR_DVIA: @@ -1622,7 +1626,6 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode) struct radeon_connector *radeon_connector = NULL; struct radeon_connector_atom_dig *radeon_dig_connector = NULL; bool travis_quirk = false; - int encoder_mode; if (connector) { radeon_connector = to_radeon_connector(connector); @@ -1718,11 +1721,6 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode) } break; } - - encoder_mode = atombios_get_encoder_mode(encoder); - if (radeon_audio != 0 && - (encoder_mode == ATOM_ENCODER_MODE_HDMI || ENCODER_MODE_IS_DP(encoder_mode))) - radeon_audio_dpms(encoder, mode); } static void @@ -1731,10 +1729,19 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode) struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); + int encoder_mode = atombios_get_encoder_mode(encoder); DRM_DEBUG_KMS("encoder dpms %d to mode %d, devices %08x, active_devices %08x\n", radeon_encoder->encoder_id, mode, radeon_encoder->devices, radeon_encoder->active_device); + + if (connector && (radeon_audio != 0) && + ((encoder_mode == ATOM_ENCODER_MODE_HDMI) || + (ENCODER_MODE_IS_DP(encoder_mode) && + drm_detect_monitor_audio(radeon_connector_edid(connector))))) + radeon_audio_dpms(encoder, mode); + switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_TMDS1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: @@ -2136,6 +2143,7 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder, struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); int encoder_mode; radeon_encoder->pixel_clock = adjusted_mode->clock; @@ -2163,10 +2171,6 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder, case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: /* handled in dpms */ - encoder_mode = atombios_get_encoder_mode(encoder); - if (radeon_audio != 0 && - (encoder_mode == ATOM_ENCODER_MODE_HDMI || ENCODER_MODE_IS_DP(encoder_mode))) - radeon_audio_mode_set(encoder, adjusted_mode); break; case ENCODER_OBJECT_ID_INTERNAL_DDI: case ENCODER_OBJECT_ID_INTERNAL_DVO1: @@ -2188,6 +2192,13 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder, } atombios_apply_encoder_quirks(encoder, adjusted_mode); + + encoder_mode = atombios_get_encoder_mode(encoder); + if (connector && (radeon_audio != 0) && + ((encoder_mode == ATOM_ENCODER_MODE_HDMI) || + (ENCODER_MODE_IS_DP(encoder_mode) && + drm_detect_monitor_audio(radeon_connector_edid(connector))))) + radeon_audio_mode_set(encoder, adjusted_mode); } static bool diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index e6a4ba2..3e670d3 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -3613,6 +3613,8 @@ static void cik_gpu_init(struct radeon_device *rdev) } WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff)); + WREG32(SRBM_INT_CNTL, 0x1); + WREG32(SRBM_INT_ACK, 0x1); WREG32(BIF_FB_EN, FB_READ_EN | FB_WRITE_EN); @@ -7230,6 +7232,8 @@ static void cik_disable_interrupt_state(struct radeon_device *rdev) WREG32(CP_ME2_PIPE3_INT_CNTL, 0); /* grbm */ WREG32(GRBM_INT_CNTL, 0); + /* SRBM */ + WREG32(SRBM_INT_CNTL, 0); /* vline/vblank, etc. */ WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0); WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0); @@ -7551,6 +7555,9 @@ int cik_irq_set(struct radeon_device *rdev) WREG32(DC_HPD5_INT_CONTROL, hpd5); WREG32(DC_HPD6_INT_CONTROL, hpd6); + /* posting read */ + RREG32(SRBM_STATUS); + return 0; } @@ -8046,6 +8053,10 @@ restart_ih: break; } break; + case 96: + DRM_ERROR("SRBM_READ_ERROR: 0x%x\n", RREG32(SRBM_READ_ERROR)); + WREG32(SRBM_INT_ACK, 0x1); + break; case 124: /* UVD */ DRM_DEBUG("IH: UVD int: 0x%08x\n", src_data); radeon_fence_process(rdev, R600_RING_TYPE_UVD_INDEX); diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 03003f8..243a36c 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -482,6 +482,10 @@ #define SOFT_RESET_ORB (1 << 23) #define SOFT_RESET_VCE (1 << 24) +#define SRBM_READ_ERROR 0xE98 +#define SRBM_INT_CNTL 0xEA0 +#define SRBM_INT_ACK 0xEA8 + #define VM_L2_CNTL 0x1400 #define ENABLE_L2_CACHE (1 << 0) #define ENABLE_L2_FRAGMENT_PROCESSING (1 << 1) @@ -2125,6 +2129,7 @@ #define VCE_UENC_REG_CLOCK_GATING 0x207c0 #define VCE_SYS_INT_EN 0x21300 # define VCE_SYS_INT_TRAP_INTERRUPT_EN (1 << 3) +#define VCE_LMI_VCPU_CACHE_40BIT_BAR 0x2145c #define VCE_LMI_CTRL2 0x21474 #define VCE_LMI_CTRL 0x21498 #define VCE_LMI_VM_CTRL 0x214a0 diff --git a/drivers/gpu/drm/radeon/dce6_afmt.c b/drivers/gpu/drm/radeon/dce6_afmt.c index 192c803..3adc2af 100644 --- a/drivers/gpu/drm/radeon/dce6_afmt.c +++ b/drivers/gpu/drm/radeon/dce6_afmt.c @@ -26,6 +26,9 @@ #include "radeon_audio.h" #include "sid.h" +#define DCE8_DCCG_AUDIO_DTO1_PHASE 0x05b8 +#define DCE8_DCCG_AUDIO_DTO1_MODULE 0x05bc + u32 dce6_endpoint_rreg(struct radeon_device *rdev, u32 block_offset, u32 reg) { @@ -252,72 +255,67 @@ void dce6_audio_enable(struct radeon_device *rdev, void dce6_hdmi_audio_set_dto(struct radeon_device *rdev, struct radeon_crtc *crtc, unsigned int clock) { - /* Two dtos; generally use dto0 for HDMI */ + /* Two dtos; generally use dto0 for HDMI */ u32 value = 0; - if (crtc) + if (crtc) value |= DCCG_AUDIO_DTO0_SOURCE_SEL(crtc->crtc_id); WREG32(DCCG_AUDIO_DTO_SOURCE, value); - /* Express [24MHz / target pixel clock] as an exact rational - * number (coefficient of two integer numbers. DCCG_AUDIO_DTOx_PHASE - * is the numerator, DCCG_AUDIO_DTOx_MODULE is the denominator - */ - WREG32(DCCG_AUDIO_DTO0_PHASE, 24000); - WREG32(DCCG_AUDIO_DTO0_MODULE, clock); + /* Express [24MHz / target pixel clock] as an exact rational + * number (coefficient of two integer numbers. DCCG_AUDIO_DTOx_PHASE + * is the numerator, DCCG_AUDIO_DTOx_MODULE is the denominator + */ + WREG32(DCCG_AUDIO_DTO0_PHASE, 24000); + WREG32(DCCG_AUDIO_DTO0_MODULE, clock); } void dce6_dp_audio_set_dto(struct radeon_device *rdev, struct radeon_crtc *crtc, unsigned int clock) { - /* Two dtos; generally use dto1 for DP */ + /* Two dtos; generally use dto1 for DP */ u32 value = 0; value |= DCCG_AUDIO_DTO_SEL; - if (crtc) + if (crtc) value |= DCCG_AUDIO_DTO0_SOURCE_SEL(crtc->crtc_id); WREG32(DCCG_AUDIO_DTO_SOURCE, value); - /* Express [24MHz / target pixel clock] as an exact rational - * number (coefficient of two integer numbers. DCCG_AUDIO_DTOx_PHASE - * is the numerator, DCCG_AUDIO_DTOx_MODULE is the denominator - */ - WREG32(DCCG_AUDIO_DTO1_PHASE, 24000); - WREG32(DCCG_AUDIO_DTO1_MODULE, clock); + /* Express [24MHz / target pixel clock] as an exact rational + * number (coefficient of two integer numbers. DCCG_AUDIO_DTOx_PHASE + * is the numerator, DCCG_AUDIO_DTOx_MODULE is the denominator + */ + if (ASIC_IS_DCE8(rdev)) { + WREG32(DCE8_DCCG_AUDIO_DTO1_PHASE, 24000); + WREG32(DCE8_DCCG_AUDIO_DTO1_MODULE, clock); + } else { + WREG32(DCCG_AUDIO_DTO1_PHASE, 24000); + WREG32(DCCG_AUDIO_DTO1_MODULE, clock); + } } -void dce6_enable_dp_audio_packets(struct drm_encoder *encoder, bool enable) +void dce6_dp_enable(struct drm_encoder *encoder, bool enable) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; - uint32_t offset; if (!dig || !dig->afmt) return; - offset = dig->afmt->offset; - if (enable) { - if (dig->afmt->enabled) - return; - - WREG32(EVERGREEN_DP_SEC_TIMESTAMP + offset, EVERGREEN_DP_SEC_TIMESTAMP_MODE(1)); - WREG32(EVERGREEN_DP_SEC_CNTL + offset, - EVERGREEN_DP_SEC_ASP_ENABLE | /* Audio packet transmission */ - EVERGREEN_DP_SEC_ATP_ENABLE | /* Audio timestamp packet transmission */ - EVERGREEN_DP_SEC_AIP_ENABLE | /* Audio infoframe packet transmission */ - EVERGREEN_DP_SEC_STREAM_ENABLE); /* Master enable for secondary stream engine */ - radeon_audio_enable(rdev, dig->afmt->pin, true); + WREG32(EVERGREEN_DP_SEC_TIMESTAMP + dig->afmt->offset, + EVERGREEN_DP_SEC_TIMESTAMP_MODE(1)); + WREG32(EVERGREEN_DP_SEC_CNTL + dig->afmt->offset, + EVERGREEN_DP_SEC_ASP_ENABLE | /* Audio packet transmission */ + EVERGREEN_DP_SEC_ATP_ENABLE | /* Audio timestamp packet transmission */ + EVERGREEN_DP_SEC_AIP_ENABLE | /* Audio infoframe packet transmission */ + EVERGREEN_DP_SEC_STREAM_ENABLE); /* Master enable for secondary stream engine */ } else { - if (!dig->afmt->enabled) - return; - - WREG32(EVERGREEN_DP_SEC_CNTL + offset, 0); - radeon_audio_enable(rdev, dig->afmt->pin, false); + WREG32(EVERGREEN_DP_SEC_CNTL + dig->afmt->offset, 0); } dig->afmt->enabled = enable; diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 78600f5..973df06 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -3253,6 +3253,8 @@ static void evergreen_gpu_init(struct radeon_device *rdev) } WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff)); + WREG32(SRBM_INT_CNTL, 0x1); + WREG32(SRBM_INT_ACK, 0x1); evergreen_fix_pci_max_read_req_size(rdev); @@ -4324,6 +4326,7 @@ void evergreen_disable_interrupt_state(struct radeon_device *rdev) tmp = RREG32(DMA_CNTL) & ~TRAP_ENABLE; WREG32(DMA_CNTL, tmp); WREG32(GRBM_INT_CNTL, 0); + WREG32(SRBM_INT_CNTL, 0); WREG32(INT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0); WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0); if (rdev->num_crtc >= 4) { @@ -4590,6 +4593,9 @@ int evergreen_irq_set(struct radeon_device *rdev) WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, afmt5); WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, afmt6); + /* posting read */ + RREG32(SRBM_STATUS); + return 0; } @@ -5066,6 +5072,10 @@ restart_ih: DRM_ERROR("Unhandled interrupt: %d %d\n", src_id, src_data); break; } + case 96: + DRM_ERROR("SRBM_READ_ERROR: 0x%x\n", RREG32(SRBM_READ_ERROR)); + WREG32(SRBM_INT_ACK, 0x1); + break; case 124: /* UVD */ DRM_DEBUG("IH: UVD int: 0x%08x\n", src_data); radeon_fence_process(rdev, R600_RING_TYPE_UVD_INDEX); diff --git a/drivers/gpu/drm/radeon/evergreen_hdmi.c b/drivers/gpu/drm/radeon/evergreen_hdmi.c index 1d9aebc..c18d4ec 100644 --- a/drivers/gpu/drm/radeon/evergreen_hdmi.c +++ b/drivers/gpu/drm/radeon/evergreen_hdmi.c @@ -272,7 +272,7 @@ void dce4_hdmi_audio_set_dto(struct radeon_device *rdev, } void dce4_dp_audio_set_dto(struct radeon_device *rdev, - struct radeon_crtc *crtc, unsigned int clock) + struct radeon_crtc *crtc, unsigned int clock) { u32 value; @@ -294,7 +294,7 @@ void dce4_dp_audio_set_dto(struct radeon_device *rdev, * is the numerator, DCCG_AUDIO_DTOx_MODULE is the denominator */ WREG32(DCCG_AUDIO_DTO1_PHASE, 24000); - WREG32(DCCG_AUDIO_DTO1_MODULE, rdev->clock.max_pixel_clock * 10); + WREG32(DCCG_AUDIO_DTO1_MODULE, clock); } void dce4_set_vbi_packet(struct drm_encoder *encoder, u32 offset) @@ -350,20 +350,9 @@ void dce4_set_audio_packet(struct drm_encoder *encoder, u32 offset) struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; - WREG32(HDMI_INFOFRAME_CONTROL0 + offset, - HDMI_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */ - HDMI_AUDIO_INFO_CONT); /* required for audio info values to be updated */ - WREG32(AFMT_INFOFRAME_CONTROL0 + offset, AFMT_AUDIO_INFO_UPDATE); /* required for audio info values to be updated */ - WREG32(HDMI_INFOFRAME_CONTROL1 + offset, - HDMI_AUDIO_INFO_LINE(2)); /* anything other than 0 */ - - WREG32(HDMI_AUDIO_PACKET_CONTROL + offset, - HDMI_AUDIO_DELAY_EN(1) | /* set the default audio delay */ - HDMI_AUDIO_PACKETS_PER_LINE(3)); /* should be suffient for all audio modes and small enough for all hblanks */ - WREG32(AFMT_60958_0 + offset, AFMT_60958_CS_CHANNEL_NUMBER_L(1)); @@ -408,15 +397,19 @@ void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable) if (!dig || !dig->afmt) return; - /* Silent, r600_hdmi_enable will raise WARN for us */ - if (enable && dig->afmt->enabled) - return; - if (!enable && !dig->afmt->enabled) - return; + if (enable) { + WREG32(HDMI_INFOFRAME_CONTROL1 + dig->afmt->offset, + HDMI_AUDIO_INFO_LINE(2)); /* anything other than 0 */ + + WREG32(HDMI_AUDIO_PACKET_CONTROL + dig->afmt->offset, + HDMI_AUDIO_DELAY_EN(1) | /* set the default audio delay */ + HDMI_AUDIO_PACKETS_PER_LINE(3)); /* should be suffient for all audio modes and small enough for all hblanks */ - if (!enable && dig->afmt->pin) { - radeon_audio_enable(rdev, dig->afmt->pin, 0); - dig->afmt->pin = NULL; + WREG32(HDMI_INFOFRAME_CONTROL0 + dig->afmt->offset, + HDMI_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */ + HDMI_AUDIO_INFO_CONT); /* required for audio info values to be updated */ + } else { + WREG32(HDMI_INFOFRAME_CONTROL0 + dig->afmt->offset, 0); } dig->afmt->enabled = enable; @@ -425,33 +418,28 @@ void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable) enable ? "En" : "Dis", dig->afmt->offset, radeon_encoder->encoder_id); } -void evergreen_enable_dp_audio_packets(struct drm_encoder *encoder, bool enable) +void evergreen_dp_enable(struct drm_encoder *encoder, bool enable) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; - uint32_t offset; if (!dig || !dig->afmt) return; - offset = dig->afmt->offset; - if (enable) { struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct radeon_connector_atom_dig *dig_connector; uint32_t val; - if (dig->afmt->enabled) - return; - - WREG32(EVERGREEN_DP_SEC_TIMESTAMP + offset, EVERGREEN_DP_SEC_TIMESTAMP_MODE(1)); + WREG32(EVERGREEN_DP_SEC_TIMESTAMP + dig->afmt->offset, + EVERGREEN_DP_SEC_TIMESTAMP_MODE(1)); if (radeon_connector->con_priv) { dig_connector = radeon_connector->con_priv; - val = RREG32(EVERGREEN_DP_SEC_AUD_N + offset); + val = RREG32(EVERGREEN_DP_SEC_AUD_N + dig->afmt->offset); val &= ~EVERGREEN_DP_SEC_N_BASE_MULTIPLE(0xf); if (dig_connector->dp_clock == 162000) @@ -459,21 +447,16 @@ void evergreen_enable_dp_audio_packets(struct drm_encoder *encoder, bool enable) else val |= EVERGREEN_DP_SEC_N_BASE_MULTIPLE(5); - WREG32(EVERGREEN_DP_SEC_AUD_N + offset, val); + WREG32(EVERGREEN_DP_SEC_AUD_N + dig->afmt->offset, val); } - WREG32(EVERGREEN_DP_SEC_CNTL + offset, + WREG32(EVERGREEN_DP_SEC_CNTL + dig->afmt->offset, EVERGREEN_DP_SEC_ASP_ENABLE | /* Audio packet transmission */ EVERGREEN_DP_SEC_ATP_ENABLE | /* Audio timestamp packet transmission */ EVERGREEN_DP_SEC_AIP_ENABLE | /* Audio infoframe packet transmission */ EVERGREEN_DP_SEC_STREAM_ENABLE); /* Master enable for secondary stream engine */ - radeon_audio_enable(rdev, dig->afmt->pin, 0xf); } else { - if (!dig->afmt->enabled) - return; - - WREG32(EVERGREEN_DP_SEC_CNTL + offset, 0); - radeon_audio_enable(rdev, dig->afmt->pin, 0); + WREG32(EVERGREEN_DP_SEC_CNTL + dig->afmt->offset, 0); } dig->afmt->enabled = enable; diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h index ee83d2a..a8d1d52 100644 --- a/drivers/gpu/drm/radeon/evergreend.h +++ b/drivers/gpu/drm/radeon/evergreend.h @@ -1191,6 +1191,10 @@ #define SOFT_RESET_REGBB (1 << 22) #define SOFT_RESET_ORB (1 << 23) +#define SRBM_READ_ERROR 0xE98 +#define SRBM_INT_CNTL 0xEA0 +#define SRBM_INT_ACK 0xEA8 + /* display watermarks */ #define DC_LB_MEMORY_SPLIT 0x6b0c #define PRIORITY_A_CNT 0x6b18 diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index 24242a7..dab0081 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -962,6 +962,8 @@ static void cayman_gpu_init(struct radeon_device *rdev) } WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff)); + WREG32(SRBM_INT_CNTL, 0x1); + WREG32(SRBM_INT_ACK, 0x1); evergreen_fix_pci_max_read_req_size(rdev); @@ -1086,12 +1088,12 @@ static void cayman_gpu_init(struct radeon_device *rdev) if ((rdev->config.cayman.max_backends_per_se == 1) && (rdev->flags & RADEON_IS_IGP)) { - if ((disabled_rb_mask & 3) == 1) { - /* RB0 disabled, RB1 enabled */ - tmp = 0x11111111; - } else { + if ((disabled_rb_mask & 3) == 2) { /* RB1 disabled, RB0 enabled */ tmp = 0x00000000; + } else { + /* RB0 disabled, RB1 enabled */ + tmp = 0x11111111; } } else { tmp = gb_addr_config & NUM_PIPES_MASK; diff --git a/drivers/gpu/drm/radeon/nid.h b/drivers/gpu/drm/radeon/nid.h index ad71254..6b44580 100644 --- a/drivers/gpu/drm/radeon/nid.h +++ b/drivers/gpu/drm/radeon/nid.h @@ -82,6 +82,10 @@ #define SOFT_RESET_REGBB (1 << 22) #define SOFT_RESET_ORB (1 << 23) +#define SRBM_READ_ERROR 0xE98 +#define SRBM_INT_CNTL 0xEA0 +#define SRBM_INT_ACK 0xEA8 + #define SRBM_STATUS2 0x0EC4 #define DMA_BUSY (1 << 5) #define DMA1_BUSY (1 << 6) diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 279801c..04f2514 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -728,6 +728,10 @@ int r100_irq_set(struct radeon_device *rdev) tmp |= RADEON_FP2_DETECT_MASK; } WREG32(RADEON_GEN_INT_CNTL, tmp); + + /* read back to post the write */ + RREG32(RADEON_GEN_INT_CNTL); + return 0; } diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 07a71a2..2fcad34 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -3784,6 +3784,9 @@ int r600_irq_set(struct radeon_device *rdev) WREG32(RV770_CG_THERMAL_INT, thermal_int); } + /* posting read */ + RREG32(R_000E50_SRBM_STATUS); + return 0; } diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index 843b65f..fa21544 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -188,7 +188,7 @@ u32 r600_dpm_get_vrefresh(struct radeon_device *rdev) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { radeon_crtc = to_radeon_crtc(crtc); if (crtc->enabled && radeon_crtc->enabled && radeon_crtc->hw_mode.clock) { - vrefresh = radeon_crtc->hw_mode.vrefresh; + vrefresh = drm_mode_vrefresh(&radeon_crtc->hw_mode); break; } } diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c index 62c91ed..dd6606b 100644 --- a/drivers/gpu/drm/radeon/r600_hdmi.c +++ b/drivers/gpu/drm/radeon/r600_hdmi.c @@ -476,17 +476,6 @@ void r600_hdmi_enable(struct drm_encoder *encoder, bool enable) if (!dig || !dig->afmt) return; - /* Silent, r600_hdmi_enable will raise WARN for us */ - if (enable && dig->afmt->enabled) - return; - if (!enable && !dig->afmt->enabled) - return; - - if (!enable && dig->afmt->pin) { - radeon_audio_enable(rdev, dig->afmt->pin, 0); - dig->afmt->pin = NULL; - } - /* Older chipsets require setting HDMI and routing manually */ if (!ASIC_IS_DCE3(rdev)) { if (enable) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 5587603..33d5a4f 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1565,6 +1565,7 @@ struct radeon_dpm { int new_active_crtc_count; u32 current_active_crtcs; int current_active_crtc_count; + bool single_display; struct radeon_dpm_dynamic_state dyn_state; struct radeon_dpm_fan fan; u32 tdp_limit; diff --git a/drivers/gpu/drm/radeon/radeon_audio.c b/drivers/gpu/drm/radeon/radeon_audio.c index a3ceef6..b21ef69 100644 --- a/drivers/gpu/drm/radeon/radeon_audio.c +++ b/drivers/gpu/drm/radeon/radeon_audio.c @@ -101,8 +101,8 @@ static void radeon_audio_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode); void r600_hdmi_enable(struct drm_encoder *encoder, bool enable); void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable); -void evergreen_enable_dp_audio_packets(struct drm_encoder *encoder, bool enable); -void dce6_enable_dp_audio_packets(struct drm_encoder *encoder, bool enable); +void evergreen_dp_enable(struct drm_encoder *encoder, bool enable); +void dce6_dp_enable(struct drm_encoder *encoder, bool enable); static const u32 pin_offsets[7] = { @@ -210,7 +210,7 @@ static struct radeon_audio_funcs dce4_dp_funcs = { .set_avi_packet = evergreen_set_avi_packet, .set_audio_packet = dce4_set_audio_packet, .mode_set = radeon_audio_dp_mode_set, - .dpms = evergreen_enable_dp_audio_packets, + .dpms = evergreen_dp_enable, }; static struct radeon_audio_funcs dce6_hdmi_funcs = { @@ -240,7 +240,7 @@ static struct radeon_audio_funcs dce6_dp_funcs = { .set_avi_packet = evergreen_set_avi_packet, .set_audio_packet = dce4_set_audio_packet, .mode_set = radeon_audio_dp_mode_set, - .dpms = dce6_enable_dp_audio_packets, + .dpms = dce6_dp_enable, }; static void radeon_audio_interface_init(struct radeon_device *rdev) @@ -452,7 +452,7 @@ void radeon_audio_enable(struct radeon_device *rdev, } void radeon_audio_detect(struct drm_connector *connector, - enum drm_connector_status status) + enum drm_connector_status status) { struct radeon_device *rdev; struct radeon_encoder *radeon_encoder; @@ -483,14 +483,11 @@ void radeon_audio_detect(struct drm_connector *connector, else radeon_encoder->audio = rdev->audio.hdmi_funcs; - radeon_audio_write_speaker_allocation(connector->encoder); - radeon_audio_write_sad_regs(connector->encoder); - if (connector->encoder->crtc) - radeon_audio_write_latency_fields(connector->encoder, - &connector->encoder->crtc->mode); + dig->afmt->pin = radeon_audio_get_pin(connector->encoder); radeon_audio_enable(rdev, dig->afmt->pin, 0xf); } else { radeon_audio_enable(rdev, dig->afmt->pin, 0); + dig->afmt->pin = NULL; } } @@ -694,23 +691,22 @@ static void radeon_audio_set_mute(struct drm_encoder *encoder, bool mute) * update the info frames with the data from the current display mode */ static void radeon_audio_hdmi_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode) + struct drm_display_mode *mode) { - struct radeon_device *rdev = encoder->dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; if (!dig || !dig->afmt) return; - /* disable audio prior to setting up hw */ - dig->afmt->pin = radeon_audio_get_pin(encoder); - radeon_audio_enable(rdev, dig->afmt->pin, 0); + radeon_audio_set_mute(encoder, true); + radeon_audio_write_speaker_allocation(encoder); + radeon_audio_write_sad_regs(encoder); + radeon_audio_write_latency_fields(encoder, mode); radeon_audio_set_dto(encoder, mode->clock); radeon_audio_set_vbi_packet(encoder); radeon_hdmi_set_color_depth(encoder); - radeon_audio_set_mute(encoder, false); radeon_audio_update_acr(encoder, mode->clock); radeon_audio_set_audio_packet(encoder); radeon_audio_select_pin(encoder); @@ -718,8 +714,7 @@ static void radeon_audio_hdmi_mode_set(struct drm_encoder *encoder, if (radeon_audio_set_avi_packet(encoder, mode) < 0) return; - /* enable audio after to setting up hw */ - radeon_audio_enable(rdev, dig->afmt->pin, 0xf); + radeon_audio_set_mute(encoder, false); } static void radeon_audio_dp_mode_set(struct drm_encoder *encoder, @@ -729,23 +724,26 @@ static void radeon_audio_dp_mode_set(struct drm_encoder *encoder, struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct radeon_connector_atom_dig *dig_connector = + radeon_connector->con_priv; if (!dig || !dig->afmt) return; - /* disable audio prior to setting up hw */ - dig->afmt->pin = radeon_audio_get_pin(encoder); - radeon_audio_enable(rdev, dig->afmt->pin, 0); - - radeon_audio_set_dto(encoder, rdev->clock.default_dispclk * 10); + radeon_audio_write_speaker_allocation(encoder); + radeon_audio_write_sad_regs(encoder); + radeon_audio_write_latency_fields(encoder, mode); + if (rdev->clock.dp_extclk || ASIC_IS_DCE5(rdev)) + radeon_audio_set_dto(encoder, rdev->clock.default_dispclk * 10); + else + radeon_audio_set_dto(encoder, dig_connector->dp_clock); radeon_audio_set_audio_packet(encoder); radeon_audio_select_pin(encoder); if (radeon_audio_set_avi_packet(encoder, mode) < 0) return; - - /* enable audio after to setting up hw */ - radeon_audio_enable(rdev, dig->afmt->pin, 0xf); } void radeon_audio_mode_set(struct drm_encoder *encoder, diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c index 63ccb8f..d27e4cc 100644 --- a/drivers/gpu/drm/radeon/radeon_bios.c +++ b/drivers/gpu/drm/radeon/radeon_bios.c @@ -76,7 +76,7 @@ static bool igp_read_bios_from_vram(struct radeon_device *rdev) static bool radeon_read_bios(struct radeon_device *rdev) { - uint8_t __iomem *bios; + uint8_t __iomem *bios, val1, val2; size_t size; rdev->bios = NULL; @@ -86,15 +86,19 @@ static bool radeon_read_bios(struct radeon_device *rdev) return false; } - if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) { + val1 = readb(&bios[0]); + val2 = readb(&bios[1]); + + if (size == 0 || val1 != 0x55 || val2 != 0xaa) { pci_unmap_rom(rdev->pdev, bios); return false; } - rdev->bios = kmemdup(bios, size, GFP_KERNEL); + rdev->bios = kzalloc(size, GFP_KERNEL); if (rdev->bios == NULL) { pci_unmap_rom(rdev->pdev, bios); return false; } + memcpy_fromio(rdev->bios, bios, size); pci_unmap_rom(rdev->pdev, bios); return true; } diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index c830863..4d0f96c 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -256,11 +256,13 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) u32 ring = RADEON_CS_RING_GFX; s32 priority = 0; + INIT_LIST_HEAD(&p->validated); + if (!cs->num_chunks) { return 0; } + /* get chunks */ - INIT_LIST_HEAD(&p->validated); p->idx = 0; p->ib.sa_bo = NULL; p->const_ib.sa_bo = NULL; @@ -715,6 +717,7 @@ int radeon_cs_packet_parse(struct radeon_cs_parser *p, struct radeon_cs_chunk *ib_chunk = p->chunk_ib; struct radeon_device *rdev = p->rdev; uint32_t header; + int ret = 0, i; if (idx >= ib_chunk->length_dw) { DRM_ERROR("Can not parse packet at %d after CS end %d !\n", @@ -743,14 +746,25 @@ int radeon_cs_packet_parse(struct radeon_cs_parser *p, break; default: DRM_ERROR("Unknown packet type %d at %d !\n", pkt->type, idx); - return -EINVAL; + ret = -EINVAL; + goto dump_ib; } if ((pkt->count + 1 + pkt->idx) >= ib_chunk->length_dw) { DRM_ERROR("Packet (%d:%d:%d) end after CS buffer (%d) !\n", pkt->idx, pkt->type, pkt->count, ib_chunk->length_dw); - return -EINVAL; + ret = -EINVAL; + goto dump_ib; } return 0; + +dump_ib: + for (i = 0; i < ib_chunk->length_dw; i++) { + if (i == idx) + printk("\t0x%08x <---\n", radeon_get_ib_value(p, i)); + else + printk("\t0x%08x\n", radeon_get_ib_value(p, i)); + } + return ret; } /** diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c index 6b670b0..3a29703 100644 --- a/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_encoders.c @@ -179,9 +179,12 @@ static void radeon_encoder_add_backlight(struct radeon_encoder *radeon_encoder, (rdev->pdev->subsystem_vendor == 0x1734) && (rdev->pdev->subsystem_device == 0x1107)) use_bl = false; +/* Older PPC macs use on-GPU backlight controller */ +#ifndef CONFIG_PPC_PMAC /* disable native backlight control on older asics */ else if (rdev->family < CHIP_R600) use_bl = false; +#endif else use_bl = true; } diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index d13d1b5..df09ca7 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -1030,37 +1030,59 @@ static inline bool radeon_test_signaled(struct radeon_fence *fence) return test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags); } +struct radeon_wait_cb { + struct fence_cb base; + struct task_struct *task; +}; + +static void +radeon_fence_wait_cb(struct fence *fence, struct fence_cb *cb) +{ + struct radeon_wait_cb *wait = + container_of(cb, struct radeon_wait_cb, base); + + wake_up_process(wait->task); +} + static signed long radeon_fence_default_wait(struct fence *f, bool intr, signed long t) { struct radeon_fence *fence = to_radeon_fence(f); struct radeon_device *rdev = fence->rdev; - bool signaled; + struct radeon_wait_cb cb; - fence_enable_sw_signaling(&fence->base); + cb.task = current; - /* - * This function has to return -EDEADLK, but cannot hold - * exclusive_lock during the wait because some callers - * may already hold it. This means checking needs_reset without - * lock, and not fiddling with any gpu internals. - * - * The callback installed with fence_enable_sw_signaling will - * run before our wait_event_*timeout call, so we will see - * both the signaled fence and the changes to needs_reset. - */ + if (fence_add_callback(f, &cb.base, radeon_fence_wait_cb)) + return t; + + while (t > 0) { + if (intr) + set_current_state(TASK_INTERRUPTIBLE); + else + set_current_state(TASK_UNINTERRUPTIBLE); + + /* + * radeon_test_signaled must be called after + * set_current_state to prevent a race with wake_up_process + */ + if (radeon_test_signaled(fence)) + break; + + if (rdev->needs_reset) { + t = -EDEADLK; + break; + } + + t = schedule_timeout(t); + + if (t > 0 && intr && signal_pending(current)) + t = -ERESTARTSYS; + } + + __set_current_state(TASK_RUNNING); + fence_remove_callback(f, &cb.base); - if (intr) - t = wait_event_interruptible_timeout(rdev->fence_queue, - ((signaled = radeon_test_signaled(fence)) || - rdev->needs_reset), t); - else - t = wait_event_timeout(rdev->fence_queue, - ((signaled = radeon_test_signaled(fence)) || - rdev->needs_reset), t); - - if (t > 0 && !signaled) - return -EDEADLK; return t; } diff --git a/drivers/gpu/drm/radeon/radeon_kfd.c b/drivers/gpu/drm/radeon/radeon_kfd.c index 061eaa9..122eb56 100644 --- a/drivers/gpu/drm/radeon/radeon_kfd.c +++ b/drivers/gpu/drm/radeon/radeon_kfd.c @@ -153,7 +153,7 @@ void radeon_kfd_device_init(struct radeon_device *rdev) .compute_vmid_bitmap = 0xFF00, .first_compute_pipe = 1, - .compute_pipe_count = 8 - 1, + .compute_pipe_count = 4 - 1, }; radeon_doorbell_get_kfd_info(rdev, diff --git a/drivers/gpu/drm/radeon/radeon_mn.c b/drivers/gpu/drm/radeon/radeon_mn.c index a69bd44..572b4db 100644 --- a/drivers/gpu/drm/radeon/radeon_mn.c +++ b/drivers/gpu/drm/radeon/radeon_mn.c @@ -122,7 +122,6 @@ static void radeon_mn_invalidate_range_start(struct mmu_notifier *mn, it = interval_tree_iter_first(&rmn->objects, start, end); while (it) { struct radeon_bo *bo; - struct fence *fence; int r; bo = container_of(it, struct radeon_bo, mn_it); @@ -134,12 +133,10 @@ static void radeon_mn_invalidate_range_start(struct mmu_notifier *mn, continue; } - fence = reservation_object_get_excl(bo->tbo.resv); - if (fence) { - r = radeon_fence_wait((struct radeon_fence *)fence, false); - if (r) - DRM_ERROR("(%d) failed to wait for user bo\n", r); - } + r = reservation_object_wait_timeout_rcu(bo->tbo.resv, true, + false, MAX_SCHEDULE_TIMEOUT); + if (r) + DRM_ERROR("(%d) failed to wait for user bo\n", r); radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_CPU); r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 43e0994..318165d 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -173,17 +173,6 @@ void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain) else rbo->placements[i].lpfn = 0; } - - /* - * Use two-ended allocation depending on the buffer size to - * improve fragmentation quality. - * 512kb was measured as the most optimal number. - */ - if (rbo->tbo.mem.size > 512 * 1024) { - for (i = 0; i < c; i++) { - rbo->placements[i].flags |= TTM_PL_FLAG_TOPDOWN; - } - } } int radeon_bo_create(struct radeon_device *rdev, diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 9f758d3..c1ba83a 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -837,12 +837,8 @@ static void radeon_dpm_thermal_work_handler(struct work_struct *work) radeon_pm_compute_clocks(rdev); } -static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev, - enum radeon_pm_state_type dpm_state) +static bool radeon_dpm_single_display(struct radeon_device *rdev) { - int i; - struct radeon_ps *ps; - u32 ui_class; bool single_display = (rdev->pm.dpm.new_active_crtc_count < 2) ? true : false; @@ -852,6 +848,23 @@ static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev, single_display = false; } + /* 120hz tends to be problematic even if they are under the + * vblank limit. + */ + if (single_display && (r600_dpm_get_vrefresh(rdev) >= 120)) + single_display = false; + + return single_display; +} + +static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev, + enum radeon_pm_state_type dpm_state) +{ + int i; + struct radeon_ps *ps; + u32 ui_class; + bool single_display = radeon_dpm_single_display(rdev); + /* certain older asics have a separare 3D performance state, * so try that first if the user selected performance */ @@ -977,6 +990,7 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) struct radeon_ps *ps; enum radeon_pm_state_type dpm_state; int ret; + bool single_display = radeon_dpm_single_display(rdev); /* if dpm init failed */ if (!rdev->pm.dpm_enabled) @@ -1001,6 +1015,9 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) /* vce just modifies an existing state so force a change */ if (ps->vce_active != rdev->pm.dpm.vce_active) goto force; + /* user has made a display change (such as timing) */ + if (rdev->pm.dpm.single_display != single_display) + goto force; if ((rdev->family < CHIP_BARTS) || (rdev->flags & RADEON_IS_IGP)) { /* for pre-BTC and APUs if the num crtcs changed but state is the same, * all we need to do is update the display configuration. @@ -1063,6 +1080,7 @@ force: rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; + rdev->pm.dpm.single_display = single_display; /* wait for the rings to drain */ for (i = 0; i < RADEON_NUM_RINGS; i++) { diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index 2456f69..8c78723 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -495,7 +495,7 @@ static int radeon_debugfs_ring_info(struct seq_file *m, void *data) seq_printf(m, "%u free dwords in ring\n", ring->ring_free_dw); seq_printf(m, "%u dwords in ring\n", count); - if (!ring->ready) + if (!ring->ring) return 0; /* print 8 dw before current rptr as often it's the last executed diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index d02aa1d..b292aca 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -598,6 +598,10 @@ static void radeon_ttm_tt_unpin_userptr(struct ttm_tt *ttm) enum dma_data_direction direction = write ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE; + /* double check that we don't free the table twice */ + if (!ttm->sg->sgl) + return; + /* free the sg table and pages again */ dma_unmap_sg(rdev->dev, ttm->sg->sgl, ttm->sg->nents, direction); diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index d81182a..97a9048 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -694,6 +694,10 @@ int rs600_irq_set(struct radeon_device *rdev) WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2); if (ASIC_IS_DCE2(rdev)) WREG32(R_007408_HDMI0_AUDIO_PACKET_CONTROL, hdmi0); + + /* posting read */ + RREG32(R_000040_GEN_INT_CNTL); + return 0; } diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 73107fe..a7fb273 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -3162,6 +3162,8 @@ static void si_gpu_init(struct radeon_device *rdev) } WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff)); + WREG32(SRBM_INT_CNTL, 1); + WREG32(SRBM_INT_ACK, 1); evergreen_fix_pci_max_read_req_size(rdev); @@ -4699,12 +4701,6 @@ int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib) switch (pkt.type) { case RADEON_PACKET_TYPE0: dev_err(rdev->dev, "Packet0 not allowed!\n"); - for (i = 0; i < ib->length_dw; i++) { - if (i == idx) - printk("\t0x%08x <---\n", ib->ptr[i]); - else - printk("\t0x%08x\n", ib->ptr[i]); - } ret = -EINVAL; break; case RADEON_PACKET_TYPE2: @@ -4736,8 +4732,15 @@ int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib) ret = -EINVAL; break; } - if (ret) + if (ret) { + for (i = 0; i < ib->length_dw; i++) { + if (i == idx) + printk("\t0x%08x <---\n", ib->ptr[i]); + else + printk("\t0x%08x\n", ib->ptr[i]); + } break; + } } while (idx < ib->length_dw); return ret; @@ -5910,6 +5913,7 @@ static void si_disable_interrupt_state(struct radeon_device *rdev) tmp = RREG32(DMA_CNTL + DMA1_REGISTER_OFFSET) & ~TRAP_ENABLE; WREG32(DMA_CNTL + DMA1_REGISTER_OFFSET, tmp); WREG32(GRBM_INT_CNTL, 0); + WREG32(SRBM_INT_CNTL, 0); if (rdev->num_crtc >= 2) { WREG32(INT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0); WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0); @@ -6199,6 +6203,9 @@ int si_irq_set(struct radeon_device *rdev) WREG32(CG_THERMAL_INT, thermal_int); + /* posting read */ + RREG32(SRBM_STATUS); + return 0; } @@ -6609,6 +6616,10 @@ restart_ih: break; } break; + case 96: + DRM_ERROR("SRBM_READ_ERROR: 0x%x\n", RREG32(SRBM_READ_ERROR)); + WREG32(SRBM_INT_ACK, 0x1); + break; case 124: /* UVD */ DRM_DEBUG("IH: UVD int: 0x%08x\n", src_data); radeon_fence_process(rdev, R600_RING_TYPE_UVD_INDEX); @@ -7119,8 +7130,7 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK); if (!vclk || !dclk) { - /* keep the Bypass mode, put PLL to sleep */ - WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK); + /* keep the Bypass mode */ return 0; } @@ -7136,8 +7146,7 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) /* set VCO_MODE to 1 */ WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK); - /* toggle UPLL_SLEEP to 1 then back to 0 */ - WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK); + /* disable sleep mode */ WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_SLEEP_MASK); /* deassert UPLL_RESET */ diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h index cbd91d2..99a9835 100644 --- a/drivers/gpu/drm/radeon/sid.h +++ b/drivers/gpu/drm/radeon/sid.h @@ -358,6 +358,10 @@ #define CC_SYS_RB_BACKEND_DISABLE 0xe80 #define GC_USER_SYS_RB_BACKEND_DISABLE 0xe84 +#define SRBM_READ_ERROR 0xE98 +#define SRBM_INT_CNTL 0xEA0 +#define SRBM_INT_ACK 0xEA8 + #define SRBM_STATUS2 0x0EC4 #define DMA_BUSY (1 << 5) #define DMA1_BUSY (1 << 6) @@ -908,8 +912,8 @@ #define DCCG_AUDIO_DTO0_PHASE 0x05b0 #define DCCG_AUDIO_DTO0_MODULE 0x05b4 -#define DCCG_AUDIO_DTO1_PHASE 0x05b8 -#define DCCG_AUDIO_DTO1_MODULE 0x05bc +#define DCCG_AUDIO_DTO1_PHASE 0x05c0 +#define DCCG_AUDIO_DTO1_MODULE 0x05c4 #define AFMT_AUDIO_SRC_CONTROL 0x713c #define AFMT_AUDIO_SRC_SELECT(x) (((x) & 7) << 0) diff --git a/drivers/gpu/drm/radeon/vce_v2_0.c b/drivers/gpu/drm/radeon/vce_v2_0.c index 1ac7bb8..fbbe78f 100644 --- a/drivers/gpu/drm/radeon/vce_v2_0.c +++ b/drivers/gpu/drm/radeon/vce_v2_0.c @@ -156,6 +156,9 @@ int vce_v2_0_resume(struct radeon_device *rdev) WREG32(VCE_LMI_SWAP_CNTL1, 0); WREG32(VCE_LMI_VM_CTRL, 0); + WREG32(VCE_LMI_VCPU_CACHE_40BIT_BAR, addr >> 8); + + addr &= 0xff; size = RADEON_GPU_PAGE_ALIGN(rdev->vce_fw->size); WREG32(VCE_VCPU_CACHE_OFFSET0, addr & 0x7fffffff); WREG32(VCE_VCPU_CACHE_SIZE0, size); diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 3aaa84a..1a52522 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -997,8 +997,10 @@ static void tegra_crtc_reset(struct drm_crtc *crtc) crtc->state = NULL; state = kzalloc(sizeof(*state), GFP_KERNEL); - if (state) + if (state) { crtc->state = &state->base; + crtc->state->crtc = crtc; + } } static struct drm_crtc_state * @@ -1012,6 +1014,7 @@ tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc) return NULL; copy->base.mode_changed = false; + copy->base.active_changed = false; copy->base.planes_changed = false; copy->base.event = NULL; @@ -1227,9 +1230,6 @@ static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc) /* program display mode */ tegra_dc_set_timings(dc, mode); - if (dc->soc->supports_border_color) - tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); - /* interlacing isn't supported yet, so disable it */ if (dc->soc->supports_interlacing) { value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL); @@ -1252,42 +1252,7 @@ static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc) static void tegra_crtc_prepare(struct drm_crtc *crtc) { - struct tegra_dc *dc = to_tegra_dc(crtc); - unsigned int syncpt; - unsigned long value; - drm_crtc_vblank_off(crtc); - - if (dc->pipe) - syncpt = SYNCPT_VBLANK1; - else - syncpt = SYNCPT_VBLANK0; - - /* initialize display controller */ - tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); - tegra_dc_writel(dc, 0x100 | syncpt, DC_CMD_CONT_SYNCPT_VSYNC); - - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | WIN_A_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); - - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); - - /* initialize timer */ - value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | - WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); - tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); - - value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | - WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); - tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); - - value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); - - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_MASK); } static void tegra_crtc_commit(struct drm_crtc *crtc) @@ -1664,6 +1629,8 @@ static int tegra_dc_init(struct host1x_client *client) struct tegra_drm *tegra = drm->dev_private; struct drm_plane *primary = NULL; struct drm_plane *cursor = NULL; + unsigned int syncpt; + u32 value; int err; if (tegra->domain) { @@ -1730,6 +1697,40 @@ static int tegra_dc_init(struct host1x_client *client) goto cleanup; } + /* initialize display controller */ + if (dc->pipe) + syncpt = SYNCPT_VBLANK1; + else + syncpt = SYNCPT_VBLANK0; + + tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); + tegra_dc_writel(dc, 0x100 | syncpt, DC_CMD_CONT_SYNCPT_VSYNC); + + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | WIN_A_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); + + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); + + /* initialize timer */ + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | + WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); + + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | + WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); + + value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); + + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + + if (dc->soc->supports_border_color) + tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); + return 0; cleanup: diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 7e06657..7eaaee74 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -851,6 +851,14 @@ static void tegra_hdmi_encoder_mode_set(struct drm_encoder *encoder, h_back_porch = mode->htotal - mode->hsync_end; h_front_porch = mode->hsync_start - mode->hdisplay; + err = clk_set_rate(hdmi->clk, pclk); + if (err < 0) { + dev_err(hdmi->dev, "failed to set HDMI clock frequency: %d\n", + err); + } + + DRM_DEBUG_KMS("HDMI clock rate: %lu Hz\n", clk_get_rate(hdmi->clk)); + /* power up sequence */ value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0); value &= ~SOR_PLL_PDBG; diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index d395b0b..8d9b7de 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -74,7 +74,7 @@ static void ttm_mem_type_debug(struct ttm_bo_device *bdev, int mem_type) pr_err(" has_type: %d\n", man->has_type); pr_err(" use_type: %d\n", man->use_type); pr_err(" flags: 0x%08X\n", man->flags); - pr_err(" gpu_offset: 0x%08lX\n", man->gpu_offset); + pr_err(" gpu_offset: 0x%08llX\n", man->gpu_offset); pr_err(" size: %llu\n", man->size); pr_err(" available_caching: 0x%08X\n", man->available_caching); pr_err(" default_caching: 0x%08X\n", man->default_caching); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 6c6b655..e13b9cb 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -725,32 +725,6 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) goto out_err1; } - ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM, - (dev_priv->vram_size >> PAGE_SHIFT)); - if (unlikely(ret != 0)) { - DRM_ERROR("Failed initializing memory manager for VRAM.\n"); - goto out_err2; - } - - dev_priv->has_gmr = true; - if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) || - refuse_dma || ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, - VMW_PL_GMR) != 0) { - DRM_INFO("No GMR memory available. " - "Graphics memory resources are very limited.\n"); - dev_priv->has_gmr = false; - } - - if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS) { - dev_priv->has_mob = true; - if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_MOB, - VMW_PL_MOB) != 0) { - DRM_INFO("No MOB memory available. " - "3D will be disabled.\n"); - dev_priv->has_mob = false; - } - } - dev_priv->mmio_mtrr = arch_phys_wc_add(dev_priv->mmio_start, dev_priv->mmio_size); @@ -813,6 +787,33 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) goto out_no_fman; } + + ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM, + (dev_priv->vram_size >> PAGE_SHIFT)); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed initializing memory manager for VRAM.\n"); + goto out_no_vram; + } + + dev_priv->has_gmr = true; + if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) || + refuse_dma || ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, + VMW_PL_GMR) != 0) { + DRM_INFO("No GMR memory available. " + "Graphics memory resources are very limited.\n"); + dev_priv->has_gmr = false; + } + + if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS) { + dev_priv->has_mob = true; + if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_MOB, + VMW_PL_MOB) != 0) { + DRM_INFO("No MOB memory available. " + "3D will be disabled.\n"); + dev_priv->has_mob = false; + } + } + vmw_kms_save_vga(dev_priv); /* Start kms and overlay systems, needs fifo. */ @@ -838,6 +839,12 @@ out_no_fifo: vmw_kms_close(dev_priv); out_no_kms: vmw_kms_restore_vga(dev_priv); + if (dev_priv->has_mob) + (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB); + if (dev_priv->has_gmr) + (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); +out_no_vram: vmw_fence_manager_takedown(dev_priv->fman); out_no_fman: if (dev_priv->capabilities & SVGA_CAP_IRQMASK) @@ -853,12 +860,6 @@ out_err4: iounmap(dev_priv->mmio_virt); out_err3: arch_phys_wc_del(dev_priv->mmio_mtrr); - if (dev_priv->has_mob) - (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB); - if (dev_priv->has_gmr) - (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); - (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); -out_err2: (void)ttm_bo_device_release(&dev_priv->bdev); out_err1: vmw_ttm_global_release(dev_priv); @@ -887,6 +888,13 @@ static int vmw_driver_unload(struct drm_device *dev) } vmw_kms_close(dev_priv); vmw_overlay_close(dev_priv); + + if (dev_priv->has_mob) + (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB); + if (dev_priv->has_gmr) + (void)ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); + vmw_fence_manager_takedown(dev_priv->fman); if (dev_priv->capabilities & SVGA_CAP_IRQMASK) drm_irq_uninstall(dev_priv->dev); @@ -898,11 +906,6 @@ static int vmw_driver_unload(struct drm_device *dev) ttm_object_device_release(&dev_priv->tdev); iounmap(dev_priv->mmio_virt); arch_phys_wc_del(dev_priv->mmio_mtrr); - if (dev_priv->has_mob) - (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB); - if (dev_priv->has_gmr) - (void)ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); - (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); (void)ttm_bo_device_release(&dev_priv->bdev); vmw_ttm_global_release(dev_priv); @@ -1235,6 +1238,7 @@ static void vmw_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); + pci_disable_device(pdev); drm_put_dev(dev); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 33176d0..654c8da 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -890,7 +890,8 @@ static int vmw_translate_mob_ptr(struct vmw_private *dev_priv, ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo); if (unlikely(ret != 0)) { DRM_ERROR("Could not find or use MOB buffer.\n"); - return -EINVAL; + ret = -EINVAL; + goto out_no_reloc; } bo = &vmw_bo->base; @@ -914,7 +915,7 @@ static int vmw_translate_mob_ptr(struct vmw_private *dev_priv, out_no_reloc: vmw_dmabuf_unreference(&vmw_bo); - vmw_bo_p = NULL; + *vmw_bo_p = NULL; return ret; } @@ -951,7 +952,8 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv, ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo); if (unlikely(ret != 0)) { DRM_ERROR("Could not find or use GMR region.\n"); - return -EINVAL; + ret = -EINVAL; + goto out_no_reloc; } bo = &vmw_bo->base; @@ -974,7 +976,7 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv, out_no_reloc: vmw_dmabuf_unreference(&vmw_bo); - vmw_bo_p = NULL; + *vmw_bo_p = NULL; return ret; } @@ -2780,13 +2782,11 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data, NULL, arg->command_size, arg->throttle_us, (void __user *)(unsigned long)arg->fence_rep, NULL); - + ttm_read_unlock(&dev_priv->reservation_sem); if (unlikely(ret != 0)) - goto out_unlock; + return ret; vmw_kms_cursor_post_execbuf(dev_priv); -out_unlock: - ttm_read_unlock(&dev_priv->reservation_sem); - return ret; + return 0; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 8725b79..07cda8c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -2033,23 +2033,17 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, int i; struct drm_mode_config *mode_config = &dev->mode_config; - ret = ttm_read_lock(&dev_priv->reservation_sem, true); - if (unlikely(ret != 0)) - return ret; - if (!arg->num_outputs) { struct drm_vmw_rect def_rect = {0, 0, 800, 600}; vmw_du_update_layout(dev_priv, 1, &def_rect); - goto out_unlock; + return 0; } rects_size = arg->num_outputs * sizeof(struct drm_vmw_rect); rects = kcalloc(arg->num_outputs, sizeof(struct drm_vmw_rect), GFP_KERNEL); - if (unlikely(!rects)) { - ret = -ENOMEM; - goto out_unlock; - } + if (unlikely(!rects)) + return -ENOMEM; user_rects = (void __user *)(unsigned long)arg->rects; ret = copy_from_user(rects, user_rects, rects_size); @@ -2074,7 +2068,5 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, out_free: kfree(rects); -out_unlock: - ttm_read_unlock(&dev_priv->reservation_sem); return ret; } diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c index b61d6be..3ddfb3d 100644 --- a/drivers/gpu/ipu-v3/ipu-di.c +++ b/drivers/gpu/ipu-v3/ipu-di.c @@ -459,6 +459,8 @@ static void ipu_di_config_clock(struct ipu_di *di, clkrate = clk_get_rate(di->clk_ipu); div = DIV_ROUND_CLOSEST(clkrate, sig->mode.pixelclock); + if (div == 0) + div = 1; rate = clkrate / div; error = rate / (sig->mode.pixelclock / 1000); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index db4fb6e..56ce8c2 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1872,6 +1872,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE7K) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) }, @@ -1926,6 +1927,7 @@ static const struct hid_device_id hid_have_special_driver[] = { #endif #if IS_ENABLED(CONFIG_HID_SAITEK) { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7) }, { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9) }, @@ -1957,6 +1959,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb65a) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_BT) }, { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_PRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) }, { HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 46edb4d..9c47867 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -586,6 +586,7 @@ #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e #define USB_DEVICE_ID_LOGITECH_T651 0xb00c +#define USB_DEVICE_ID_LOGITECH_C077 0xc007 #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f @@ -654,6 +655,7 @@ #define USB_DEVICE_ID_MS_LK6K 0x00f9 #define USB_DEVICE_ID_MS_PRESENTER_8K_BT 0x0701 #define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713 +#define USB_DEVICE_ID_MS_NE7K 0x071d #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730 #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c #define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799 @@ -802,6 +804,7 @@ #define USB_VENDOR_ID_SAITEK 0x06a3 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 #define USB_DEVICE_ID_SAITEK_PS1000 0x0621 +#define USB_DEVICE_ID_SAITEK_RAT7_OLD 0x0ccb #define USB_DEVICE_ID_SAITEK_RAT7 0x0cd7 #define USB_DEVICE_ID_SAITEK_MMO7 0x0cd0 @@ -896,6 +899,7 @@ #define USB_VENDOR_ID_TIVO 0x150a #define USB_DEVICE_ID_TIVO_SLIDE_BT 0x1200 #define USB_DEVICE_ID_TIVO_SLIDE 0x1201 +#define USB_DEVICE_ID_TIVO_SLIDE_PRO 0x1203 #define USB_VENDOR_ID_TOPSEED 0x0766 #define USB_DEVICE_ID_TOPSEED_CYBERLINK 0x0204 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 052869d..32c2da4 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -339,7 +339,7 @@ static int hidinput_get_battery_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) { - struct hid_device *dev = container_of(psy, struct hid_device, battery); + struct hid_device *dev = power_supply_get_drvdata(psy); int ret = 0; __u8 *buf; @@ -397,26 +397,32 @@ static int hidinput_get_battery_property(struct power_supply *psy, static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field) { - struct power_supply *battery = &dev->battery; - int ret; + struct power_supply_desc *psy_desc = NULL; + struct power_supply_config psy_cfg = { .drv_data = dev, }; unsigned quirks; s32 min, max; if (field->usage->hid != HID_DC_BATTERYSTRENGTH) return false; /* no match */ - if (battery->name != NULL) + if (dev->battery != NULL) goto out; /* already initialized? */ - battery->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq); - if (battery->name == NULL) + psy_desc = kzalloc(sizeof(*psy_desc), GFP_KERNEL); + if (psy_desc == NULL) goto out; - battery->type = POWER_SUPPLY_TYPE_BATTERY; - battery->properties = hidinput_battery_props; - battery->num_properties = ARRAY_SIZE(hidinput_battery_props); - battery->use_for_apm = 0; - battery->get_property = hidinput_get_battery_property; + psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq); + if (psy_desc->name == NULL) { + kfree(psy_desc); + goto out; + } + + psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; + psy_desc->properties = hidinput_battery_props; + psy_desc->num_properties = ARRAY_SIZE(hidinput_battery_props); + psy_desc->use_for_apm = 0; + psy_desc->get_property = hidinput_get_battery_property; quirks = find_battery_quirk(dev); @@ -439,27 +445,30 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, dev->battery_report_type = report_type; dev->battery_report_id = field->report->id; - ret = power_supply_register(&dev->dev, battery); - if (ret != 0) { - hid_warn(dev, "can't register power supply: %d\n", ret); - kfree(battery->name); - battery->name = NULL; + dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg); + if (IS_ERR(dev->battery)) { + hid_warn(dev, "can't register power supply: %ld\n", + PTR_ERR(dev->battery)); + kfree(psy_desc->name); + kfree(psy_desc); + dev->battery = NULL; + } else { + power_supply_powers(dev->battery, &dev->dev); } - power_supply_powers(battery, &dev->dev); - out: return true; } static void hidinput_cleanup_battery(struct hid_device *dev) { - if (!dev->battery.name) + if (!dev->battery) return; - power_supply_unregister(&dev->battery); - kfree(dev->battery.name); - dev->battery.name = NULL; + power_supply_unregister(dev->battery); + kfree(dev->battery->desc->name); + kfree(dev->battery->desc); + dev->battery = NULL; } #else /* !CONFIG_HID_BATTERY_STRENGTH */ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index fbaea6e..af935eb 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -264,6 +264,8 @@ static const struct hid_device_id ms_devices[] = { .driver_data = MS_ERGONOMY }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP), .driver_data = MS_ERGONOMY }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE7K), + .driver_data = MS_ERGONOMY }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K), .driver_data = MS_ERGONOMY | MS_RDESC }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB), diff --git a/drivers/hid/hid-saitek.c b/drivers/hid/hid-saitek.c index 5632c54..a014f21 100644 --- a/drivers/hid/hid-saitek.c +++ b/drivers/hid/hid-saitek.c @@ -177,6 +177,8 @@ static int saitek_event(struct hid_device *hdev, struct hid_field *field, static const struct hid_device_id saitek_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000), .driver_data = SAITEK_FIX_PS1000 }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD), + .driver_data = SAITEK_RELEASE_MODE_RAT7 }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7), .driver_data = SAITEK_RELEASE_MODE_RAT7 }, { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9), diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 6a58b6c..e54ce10 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -135,8 +135,9 @@ static struct hid_sensor_hub_callbacks *sensor_hub_get_callback( { struct hid_sensor_hub_callbacks_list *callback; struct sensor_hub_data *pdata = hid_get_drvdata(hdev); + unsigned long flags; - spin_lock(&pdata->dyn_callback_lock); + spin_lock_irqsave(&pdata->dyn_callback_lock, flags); list_for_each_entry(callback, &pdata->dyn_callback_list, list) if (callback->usage_id == usage_id && (collection_index >= @@ -145,10 +146,11 @@ static struct hid_sensor_hub_callbacks *sensor_hub_get_callback( callback->hsdev->end_collection_index)) { *priv = callback->priv; *hsdev = callback->hsdev; - spin_unlock(&pdata->dyn_callback_lock); + spin_unlock_irqrestore(&pdata->dyn_callback_lock, + flags); return callback->usage_callback; } - spin_unlock(&pdata->dyn_callback_lock); + spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return NULL; } diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 31e9d25..c906300 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -804,7 +804,7 @@ union sixaxis_output_report_01 { #define DS4_REPORT_0x81_SIZE 7 #define SIXAXIS_REPORT_0xF2_SIZE 18 -static spinlock_t sony_dev_list_lock; +static DEFINE_SPINLOCK(sony_dev_list_lock); static LIST_HEAD(sony_device_list); static DEFINE_IDA(sony_device_id_allocator); @@ -815,7 +815,8 @@ struct sony_sc { struct led_classdev *leds[MAX_LEDS]; unsigned long quirks; struct work_struct state_worker; - struct power_supply battery; + struct power_supply *battery; + struct power_supply_desc battery_desc; int device_id; __u8 *output_report_dmabuf; @@ -1660,7 +1661,7 @@ static int sony_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct sony_sc *sc = container_of(psy, struct sony_sc, battery); + struct sony_sc *sc = power_supply_get_drvdata(psy); unsigned long flags; int ret = 0; u8 battery_charging, battery_capacity, cable_state; @@ -1699,6 +1700,7 @@ static int sony_battery_get_property(struct power_supply *psy, static int sony_battery_probe(struct sony_sc *sc) { + struct power_supply_config psy_cfg = { .drv_data = sc, }; struct hid_device *hdev = sc->hdev; int ret; @@ -1708,39 +1710,42 @@ static int sony_battery_probe(struct sony_sc *sc) */ sc->battery_capacity = 100; - sc->battery.properties = sony_battery_props; - sc->battery.num_properties = ARRAY_SIZE(sony_battery_props); - sc->battery.get_property = sony_battery_get_property; - sc->battery.type = POWER_SUPPLY_TYPE_BATTERY; - sc->battery.use_for_apm = 0; - sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%pMR", - sc->mac_address); - if (!sc->battery.name) + sc->battery_desc.properties = sony_battery_props; + sc->battery_desc.num_properties = ARRAY_SIZE(sony_battery_props); + sc->battery_desc.get_property = sony_battery_get_property; + sc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; + sc->battery_desc.use_for_apm = 0; + sc->battery_desc.name = kasprintf(GFP_KERNEL, + "sony_controller_battery_%pMR", + sc->mac_address); + if (!sc->battery_desc.name) return -ENOMEM; - ret = power_supply_register(&hdev->dev, &sc->battery); - if (ret) { + sc->battery = power_supply_register(&hdev->dev, &sc->battery_desc, + &psy_cfg); + if (IS_ERR(sc->battery)) { + ret = PTR_ERR(sc->battery); hid_err(hdev, "Unable to register battery device\n"); goto err_free; } - power_supply_powers(&sc->battery, &hdev->dev); + power_supply_powers(sc->battery, &hdev->dev); return 0; err_free: - kfree(sc->battery.name); - sc->battery.name = NULL; + kfree(sc->battery_desc.name); + sc->battery_desc.name = NULL; return ret; } static void sony_battery_remove(struct sony_sc *sc) { - if (!sc->battery.name) + if (!sc->battery_desc.name) return; - power_supply_unregister(&sc->battery); - kfree(sc->battery.name); - sc->battery.name = NULL; + power_supply_unregister(sc->battery); + kfree(sc->battery_desc.name); + sc->battery_desc.name = NULL; } /* @@ -1944,6 +1949,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) return -ENOMEM; } + spin_lock_init(&sc->lock); + sc->quirks = quirks; hid_set_drvdata(hdev, sc); sc->hdev = hdev; @@ -2147,8 +2154,8 @@ static void __exit sony_exit(void) { dbg_hid("Sony:%s\n", __func__); - ida_destroy(&sony_device_id_allocator); hid_unregister_driver(&sony_driver); + ida_destroy(&sony_device_id_allocator); } module_init(sony_init); module_exit(sony_exit); diff --git a/drivers/hid/hid-tivo.c b/drivers/hid/hid-tivo.c index d790d8d..d986969 100644 --- a/drivers/hid/hid-tivo.c +++ b/drivers/hid/hid-tivo.c @@ -64,6 +64,7 @@ static const struct hid_device_id tivo_devices[] = { /* TiVo Slide Bluetooth remote, pairs with a Broadcom dongle */ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_BT) }, { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_PRO) }, { } }; MODULE_DEVICE_TABLE(hid, tivo_devices); diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c index 6b61f01..05e23c4 100644 --- a/drivers/hid/hid-wiimote-modules.c +++ b/drivers/hid/hid-wiimote-modules.c @@ -203,8 +203,7 @@ static int wiimod_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct wiimote_data *wdata = container_of(psy, struct wiimote_data, - battery); + struct wiimote_data *wdata = power_supply_get_drvdata(psy); int ret = 0, state; unsigned long flags; @@ -238,42 +237,46 @@ static int wiimod_battery_get_property(struct power_supply *psy, static int wiimod_battery_probe(const struct wiimod_ops *ops, struct wiimote_data *wdata) { + struct power_supply_config psy_cfg = { .drv_data = wdata, }; int ret; - wdata->battery.properties = wiimod_battery_props; - wdata->battery.num_properties = ARRAY_SIZE(wiimod_battery_props); - wdata->battery.get_property = wiimod_battery_get_property; - wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY; - wdata->battery.use_for_apm = 0; - wdata->battery.name = kasprintf(GFP_KERNEL, "wiimote_battery_%s", - wdata->hdev->uniq); - if (!wdata->battery.name) + wdata->battery_desc.properties = wiimod_battery_props; + wdata->battery_desc.num_properties = ARRAY_SIZE(wiimod_battery_props); + wdata->battery_desc.get_property = wiimod_battery_get_property; + wdata->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; + wdata->battery_desc.use_for_apm = 0; + wdata->battery_desc.name = kasprintf(GFP_KERNEL, "wiimote_battery_%s", + wdata->hdev->uniq); + if (!wdata->battery_desc.name) return -ENOMEM; - ret = power_supply_register(&wdata->hdev->dev, &wdata->battery); - if (ret) { + wdata->battery = power_supply_register(&wdata->hdev->dev, + &wdata->battery_desc, + &psy_cfg); + if (IS_ERR(wdata->battery)) { hid_err(wdata->hdev, "cannot register battery device\n"); + ret = PTR_ERR(wdata->battery); goto err_free; } - power_supply_powers(&wdata->battery, &wdata->hdev->dev); + power_supply_powers(wdata->battery, &wdata->hdev->dev); return 0; err_free: - kfree(wdata->battery.name); - wdata->battery.name = NULL; + kfree(wdata->battery_desc.name); + wdata->battery_desc.name = NULL; return ret; } static void wiimod_battery_remove(const struct wiimod_ops *ops, struct wiimote_data *wdata) { - if (!wdata->battery.name) + if (!wdata->battery_desc.name) return; - power_supply_unregister(&wdata->battery); - kfree(wdata->battery.name); - wdata->battery.name = NULL; + power_supply_unregister(wdata->battery); + kfree(wdata->battery_desc.name); + wdata->battery_desc.name = NULL; } static const struct wiimod_ops wiimod_battery = { diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h index 10934aa..875694d 100644 --- a/drivers/hid/hid-wiimote.h +++ b/drivers/hid/hid-wiimote.h @@ -147,7 +147,8 @@ struct wiimote_data { struct led_classdev *leds[4]; struct input_dev *accel; struct input_dev *ir; - struct power_supply battery; + struct power_supply *battery; + struct power_supply_desc battery_desc; struct input_dev *mp; struct timer_list timer; struct wiimote_debug *debug; diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index d43e967..36053f3 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -370,7 +370,10 @@ static int i2c_hid_hwreset(struct i2c_client *client) static void i2c_hid_get_input(struct i2c_hid *ihid) { int ret, ret_size; - int size = ihid->bufsize; + int size = le16_to_cpu(ihid->hdesc.wMaxInputLength); + + if (size > ihid->bufsize) + size = ihid->bufsize; ret = i2c_master_recv(ihid->client, ihid->inbuf, size); if (ret != size) { @@ -785,7 +788,7 @@ static int i2c_hid_init_irq(struct i2c_client *client) dev_dbg(&client->dev, "Requesting IRQ: %d\n", client->irq); ret = request_threaded_irq(client->irq, NULL, i2c_hid_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, ihid); if (ret < 0) { dev_warn(&client->dev, diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 9be99a67..a821277 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -78,6 +78,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C077, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP, HID_QUIRK_NO_INIT_REPORTS }, diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h index 7db4328..0d0d0dd 100644 --- a/drivers/hid/wacom.h +++ b/drivers/hid/wacom.h @@ -119,8 +119,10 @@ struct wacom { u8 img_lum; /* OLED matrix display brightness */ } led; bool led_initialized; - struct power_supply battery; - struct power_supply ac; + struct power_supply *battery; + struct power_supply *ac; + struct power_supply_desc battery_desc; + struct power_supply_desc ac_desc; }; static inline void wacom_schedule_work(struct wacom_wac *wacom_wac) @@ -133,7 +135,7 @@ static inline void wacom_notify_battery(struct wacom_wac *wacom_wac) { struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); - power_supply_changed(&wacom->battery); + power_supply_changed(wacom->battery); } extern const struct hid_device_id wacom_ids[]; diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index f0568a7..ba9af47 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -944,7 +944,7 @@ static int wacom_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct wacom *wacom = container_of(psy, struct wacom, battery); + struct wacom *wacom = power_supply_get_drvdata(psy); int ret = 0; switch (psp) { @@ -976,7 +976,7 @@ static int wacom_ac_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct wacom *wacom = container_of(psy, struct wacom, ac); + struct wacom *wacom = power_supply_get_drvdata(psy); int ret = 0; switch (psp) { @@ -998,42 +998,46 @@ static int wacom_ac_get_property(struct power_supply *psy, static int wacom_initialize_battery(struct wacom *wacom) { static atomic_t battery_no = ATOMIC_INIT(0); - int error; + struct power_supply_config psy_cfg = { .drv_data = wacom, }; unsigned long n; if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) { + struct power_supply_desc *bat_desc = &wacom->battery_desc; + struct power_supply_desc *ac_desc = &wacom->ac_desc; n = atomic_inc_return(&battery_no) - 1; - wacom->battery.properties = wacom_battery_props; - wacom->battery.num_properties = ARRAY_SIZE(wacom_battery_props); - wacom->battery.get_property = wacom_battery_get_property; + bat_desc->properties = wacom_battery_props; + bat_desc->num_properties = ARRAY_SIZE(wacom_battery_props); + bat_desc->get_property = wacom_battery_get_property; sprintf(wacom->wacom_wac.bat_name, "wacom_battery_%ld", n); - wacom->battery.name = wacom->wacom_wac.bat_name; - wacom->battery.type = POWER_SUPPLY_TYPE_BATTERY; - wacom->battery.use_for_apm = 0; + bat_desc->name = wacom->wacom_wac.bat_name; + bat_desc->type = POWER_SUPPLY_TYPE_BATTERY; + bat_desc->use_for_apm = 0; - wacom->ac.properties = wacom_ac_props; - wacom->ac.num_properties = ARRAY_SIZE(wacom_ac_props); - wacom->ac.get_property = wacom_ac_get_property; + ac_desc->properties = wacom_ac_props; + ac_desc->num_properties = ARRAY_SIZE(wacom_ac_props); + ac_desc->get_property = wacom_ac_get_property; sprintf(wacom->wacom_wac.ac_name, "wacom_ac_%ld", n); - wacom->ac.name = wacom->wacom_wac.ac_name; - wacom->ac.type = POWER_SUPPLY_TYPE_MAINS; - wacom->ac.use_for_apm = 0; - - error = power_supply_register(&wacom->hdev->dev, - &wacom->battery); - if (error) - return error; - - power_supply_powers(&wacom->battery, &wacom->hdev->dev); - - error = power_supply_register(&wacom->hdev->dev, &wacom->ac); - if (error) { - power_supply_unregister(&wacom->battery); - return error; + ac_desc->name = wacom->wacom_wac.ac_name; + ac_desc->type = POWER_SUPPLY_TYPE_MAINS; + ac_desc->use_for_apm = 0; + + wacom->battery = power_supply_register(&wacom->hdev->dev, + &wacom->battery_desc, &psy_cfg); + if (IS_ERR(wacom->battery)) + return PTR_ERR(wacom->battery); + + power_supply_powers(wacom->battery, &wacom->hdev->dev); + + wacom->ac = power_supply_register(&wacom->hdev->dev, + &wacom->ac_desc, + &psy_cfg); + if (IS_ERR(wacom->ac)) { + power_supply_unregister(wacom->battery); + return PTR_ERR(wacom->ac); } - power_supply_powers(&wacom->ac, &wacom->hdev->dev); + power_supply_powers(wacom->ac, &wacom->hdev->dev); } return 0; @@ -1042,11 +1046,11 @@ static int wacom_initialize_battery(struct wacom *wacom) static void wacom_destroy_battery(struct wacom *wacom) { if ((wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) && - wacom->battery.dev) { - power_supply_unregister(&wacom->battery); - wacom->battery.dev = NULL; - power_supply_unregister(&wacom->ac); - wacom->ac.dev = NULL; + wacom->battery) { + power_supply_unregister(wacom->battery); + wacom->battery = NULL; + power_supply_unregister(wacom->ac); + wacom->ac = NULL; } } diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 1a65079..bbe32d6 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -551,9 +551,13 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) (features->type == CINTIQ && !(data[1] & 0x40))) return 1; - if (features->quirks & WACOM_QUIRK_MULTI_INPUT) + if (wacom->shared) { wacom->shared->stylus_in_proximity = true; + if (wacom->shared->touch_down) + return 1; + } + /* in Range while exiting */ if (((data[1] & 0xfe) == 0x20) && wacom->reporting_data) { input_report_key(input, BTN_TOUCH, 0); @@ -778,6 +782,11 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) input_report_abs(input, ABS_X, be16_to_cpup((__be16 *)&data[4])); input_report_abs(input, ABS_Y, be16_to_cpup((__be16 *)&data[6])); input_report_abs(input, ABS_Z, be16_to_cpup((__be16 *)&data[8])); + if ((data[2] & 0x07) | data[4] | data[5] | data[6] | data[7] | data[8] | data[9]) { + input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); + } else { + input_report_abs(input, ABS_MISC, 0); + } } else if (features->type == CINTIQ_HYBRID) { /* * Do not send hardware buttons under Android. They @@ -1038,27 +1047,28 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom) struct input_dev *input = wacom->input; unsigned char *data = wacom->data; int i; - int current_num_contacts = 0; + int current_num_contacts = data[61]; int contacts_to_send = 0; int num_contacts_left = 4; /* maximum contacts per packet */ int byte_per_packet = WACOM_BYTES_PER_24HDT_PACKET; int y_offset = 2; + static int contact_with_no_pen_down_count = 0; if (wacom->features.type == WACOM_27QHDT) { current_num_contacts = data[63]; num_contacts_left = 10; byte_per_packet = WACOM_BYTES_PER_QHDTHID_PACKET; y_offset = 0; - } else { - current_num_contacts = data[61]; } /* * First packet resets the counter since only the first * packet in series will have non-zero current_num_contacts. */ - if (current_num_contacts) + if (current_num_contacts) { wacom->num_contacts_left = current_num_contacts; + contact_with_no_pen_down_count = 0; + } contacts_to_send = min(num_contacts_left, wacom->num_contacts_left); @@ -1091,15 +1101,16 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom) input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h)); input_report_abs(input, ABS_MT_ORIENTATION, w > h); } + contact_with_no_pen_down_count++; } } input_mt_report_pointer_emulation(input, true); wacom->num_contacts_left -= contacts_to_send; - if (wacom->num_contacts_left <= 0) + if (wacom->num_contacts_left <= 0) { wacom->num_contacts_left = 0; - - wacom->shared->touch_down = (wacom->num_contacts_left > 0); + wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); + } return 1; } @@ -1111,6 +1122,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom) int current_num_contacts = data[2]; int contacts_to_send = 0; int x_offset = 0; + static int contact_with_no_pen_down_count = 0; /* MTTPC does not support Height and Width */ if (wacom->features.type == MTTPC || wacom->features.type == MTTPC_B) @@ -1120,8 +1132,10 @@ static int wacom_mt_touch(struct wacom_wac *wacom) * First packet resets the counter since only the first * packet in series will have non-zero current_num_contacts. */ - if (current_num_contacts) + if (current_num_contacts) { wacom->num_contacts_left = current_num_contacts; + contact_with_no_pen_down_count = 0; + } /* There are at most 5 contacts per packet */ contacts_to_send = min(5, wacom->num_contacts_left); @@ -1142,15 +1156,16 @@ static int wacom_mt_touch(struct wacom_wac *wacom) int y = get_unaligned_le16(&data[offset + x_offset + 9]); input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); + contact_with_no_pen_down_count++; } } input_mt_report_pointer_emulation(input, true); wacom->num_contacts_left -= contacts_to_send; - if (wacom->num_contacts_left < 0) + if (wacom->num_contacts_left <= 0) { wacom->num_contacts_left = 0; - - wacom->shared->touch_down = (wacom->num_contacts_left > 0); + wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); + } return 1; } @@ -1188,29 +1203,25 @@ static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len) { unsigned char *data = wacom->data; struct input_dev *input = wacom->input; - bool prox; + bool prox = !wacom->shared->stylus_in_proximity; int x = 0, y = 0; if (wacom->features.touch_max > 1 || len > WACOM_PKGLEN_TPC2FG) return 0; - if (!wacom->shared->stylus_in_proximity) { - if (len == WACOM_PKGLEN_TPC1FG) { - prox = data[0] & 0x01; - x = get_unaligned_le16(&data[1]); - y = get_unaligned_le16(&data[3]); - } else if (len == WACOM_PKGLEN_TPC1FG_B) { - prox = data[2] & 0x01; - x = get_unaligned_le16(&data[3]); - y = get_unaligned_le16(&data[5]); - } else { - prox = data[1] & 0x01; - x = le16_to_cpup((__le16 *)&data[2]); - y = le16_to_cpup((__le16 *)&data[4]); - } - } else - /* force touch out when pen is in prox */ - prox = 0; + if (len == WACOM_PKGLEN_TPC1FG) { + prox = prox && (data[0] & 0x01); + x = get_unaligned_le16(&data[1]); + y = get_unaligned_le16(&data[3]); + } else if (len == WACOM_PKGLEN_TPC1FG_B) { + prox = prox && (data[2] & 0x01); + x = get_unaligned_le16(&data[3]); + y = get_unaligned_le16(&data[5]); + } else { + prox = prox && (data[1] & 0x01); + x = le16_to_cpup((__le16 *)&data[2]); + y = le16_to_cpup((__le16 *)&data[4]); + } if (prox) { input_report_abs(input, ABS_X, x); @@ -1608,6 +1619,7 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) struct input_dev *pad_input = wacom->pad_input; unsigned char *data = wacom->data; int i; + int contact_with_no_pen_down_count = 0; if (data[0] != 0x02) return 0; @@ -1635,6 +1647,7 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) } input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); + contact_with_no_pen_down_count++; } } @@ -1644,11 +1657,12 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) input_report_key(pad_input, BTN_FORWARD, (data[1] & 0x04) != 0); input_report_key(pad_input, BTN_BACK, (data[1] & 0x02) != 0); input_report_key(pad_input, BTN_RIGHT, (data[1] & 0x01) != 0); + wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); return 1; } -static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) +static int wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data, int last_touch_count) { struct wacom_features *features = &wacom->features; struct input_dev *input = wacom->input; @@ -1656,7 +1670,7 @@ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) int slot = input_mt_get_slot_by_key(input, data[0]); if (slot < 0) - return; + return 0; touch = touch && !wacom->shared->stylus_in_proximity; @@ -1688,7 +1702,9 @@ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) input_report_abs(input, ABS_MT_POSITION_Y, y); input_report_abs(input, ABS_MT_TOUCH_MAJOR, width); input_report_abs(input, ABS_MT_TOUCH_MINOR, height); + last_touch_count++; } + return last_touch_count; } static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data) @@ -1713,6 +1729,7 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom) unsigned char *data = wacom->data; int count = data[1] & 0x07; int i; + int contact_with_no_pen_down_count = 0; if (data[0] != 0x02) return 0; @@ -1723,12 +1740,15 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom) int msg_id = data[offset]; if (msg_id >= 2 && msg_id <= 17) - wacom_bpt3_touch_msg(wacom, data + offset); + contact_with_no_pen_down_count = + wacom_bpt3_touch_msg(wacom, data + offset, + contact_with_no_pen_down_count); else if (msg_id == 128) wacom_bpt3_button_msg(wacom, data + offset); } input_mt_report_pointer_emulation(input, true); + wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); return 1; } @@ -1754,6 +1774,9 @@ static int wacom_bpt_pen(struct wacom_wac *wacom) return 0; } + if (wacom->shared->touch_down) + return 0; + prox = (data[1] & 0x20) == 0x20; /* @@ -2725,9 +2748,9 @@ static const struct wacom_features wacom_features_0xF6 = .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf8, .touch_max = 10, .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0x32A = - { "Wacom Cintiq 27QHD", 119740, 67520, 2047, - 63, WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "Wacom Cintiq 27QHD", 119740, 67520, 2047, 63, + WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; static const struct wacom_features wacom_features_0x32B = { "Wacom Cintiq 27QHD touch", 119740, 67520, 2047, 63, WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, diff --git a/drivers/hsi/clients/Kconfig b/drivers/hsi/clients/Kconfig index bc60dec..d612620 100644 --- a/drivers/hsi/clients/Kconfig +++ b/drivers/hsi/clients/Kconfig @@ -6,13 +6,23 @@ comment "HSI clients" config NOKIA_MODEM tristate "Nokia Modem" - depends on HSI && SSI_PROTOCOL + depends on HSI && SSI_PROTOCOL && CMT_SPEECH help Say Y here if you want to add support for the modem on Nokia N900 (Nokia RX-51) hardware. If unsure, say N. +config CMT_SPEECH + tristate "CMT speech" + depends on HSI && SSI_PROTOCOL + help + If you say Y here, you will enable the CMT speech protocol used + by Nokia modems. If you say M the protocol will be available as + module named cmt_speech. + + If unsure, say N. + config SSI_PROTOCOL tristate "SSI protocol" depends on HSI && PHONET && OMAP_SSI diff --git a/drivers/hsi/clients/Makefile b/drivers/hsi/clients/Makefile index 4d5bc0e..2607232 100644 --- a/drivers/hsi/clients/Makefile +++ b/drivers/hsi/clients/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_NOKIA_MODEM) += nokia-modem.o obj-$(CONFIG_SSI_PROTOCOL) += ssi_protocol.o +obj-$(CONFIG_CMT_SPEECH) += cmt_speech.o obj-$(CONFIG_HSI_CHAR) += hsi_char.o diff --git a/drivers/hsi/clients/cmt_speech.c b/drivers/hsi/clients/cmt_speech.c new file mode 100644 index 0000000..4983529 --- /dev/null +++ b/drivers/hsi/clients/cmt_speech.c @@ -0,0 +1,1457 @@ +/* + * cmt_speech.c - HSI CMT speech driver + * + * Copyright (C) 2008,2009,2010 Nokia Corporation. All rights reserved. + * + * Contact: Kai Vehmanen <kai.vehmanen@nokia.com> + * Original author: Peter Ujfalusi <peter.ujfalusi@nokia.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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <linux/sched.h> +#include <linux/ioctl.h> +#include <linux/uaccess.h> +#include <linux/pm_qos.h> +#include <linux/hsi/hsi.h> +#include <linux/hsi/ssi_protocol.h> +#include <linux/hsi/cs-protocol.h> + +#define CS_MMAP_SIZE PAGE_SIZE + +struct char_queue { + struct list_head list; + u32 msg; +}; + +struct cs_char { + unsigned int opened; + struct hsi_client *cl; + struct cs_hsi_iface *hi; + struct list_head chardev_queue; + struct list_head dataind_queue; + int dataind_pending; + /* mmap things */ + unsigned long mmap_base; + unsigned long mmap_size; + spinlock_t lock; + struct fasync_struct *async_queue; + wait_queue_head_t wait; + /* hsi channel ids */ + int channel_id_cmd; + int channel_id_data; +}; + +#define SSI_CHANNEL_STATE_READING 1 +#define SSI_CHANNEL_STATE_WRITING (1 << 1) +#define SSI_CHANNEL_STATE_POLL (1 << 2) +#define SSI_CHANNEL_STATE_ERROR (1 << 3) + +#define TARGET_MASK 0xf000000 +#define TARGET_REMOTE (1 << CS_DOMAIN_SHIFT) +#define TARGET_LOCAL 0 + +/* Number of pre-allocated commands buffers */ +#define CS_MAX_CMDS 4 + +/* + * During data transfers, transactions must be handled + * within 20ms (fixed value in cmtspeech HSI protocol) + */ +#define CS_QOS_LATENCY_FOR_DATA_USEC 20000 + +/* Timeout to wait for pending HSI transfers to complete */ +#define CS_HSI_TRANSFER_TIMEOUT_MS 500 + + +#define RX_PTR_BOUNDARY_SHIFT 8 +#define RX_PTR_MAX_SHIFT (RX_PTR_BOUNDARY_SHIFT + \ + CS_MAX_BUFFERS_SHIFT) +struct cs_hsi_iface { + struct hsi_client *cl; + struct hsi_client *master; + + unsigned int iface_state; + unsigned int wakeline_state; + unsigned int control_state; + unsigned int data_state; + + /* state exposed to application */ + struct cs_mmap_config_block *mmap_cfg; + + unsigned long mmap_base; + unsigned long mmap_size; + + unsigned int rx_slot; + unsigned int tx_slot; + + /* note: for security reasons, we do not trust the contents of + * mmap_cfg, but instead duplicate the variables here */ + unsigned int buf_size; + unsigned int rx_bufs; + unsigned int tx_bufs; + unsigned int rx_ptr_boundary; + unsigned int rx_offsets[CS_MAX_BUFFERS]; + unsigned int tx_offsets[CS_MAX_BUFFERS]; + + /* size of aligned memory blocks */ + unsigned int slot_size; + unsigned int flags; + + struct list_head cmdqueue; + + struct hsi_msg *data_rx_msg; + struct hsi_msg *data_tx_msg; + wait_queue_head_t datawait; + + struct pm_qos_request pm_qos_req; + + spinlock_t lock; +}; + +static struct cs_char cs_char_data; + +static void cs_hsi_read_on_control(struct cs_hsi_iface *hi); +static void cs_hsi_read_on_data(struct cs_hsi_iface *hi); + +static inline void rx_ptr_shift_too_big(void) +{ + BUILD_BUG_ON((1LLU << RX_PTR_MAX_SHIFT) > UINT_MAX); +} + +static void cs_notify(u32 message, struct list_head *head) +{ + struct char_queue *entry; + + spin_lock(&cs_char_data.lock); + + if (!cs_char_data.opened) { + spin_unlock(&cs_char_data.lock); + goto out; + } + + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) { + dev_err(&cs_char_data.cl->device, + "Can't allocate new entry for the queue.\n"); + spin_unlock(&cs_char_data.lock); + goto out; + } + + entry->msg = message; + list_add_tail(&entry->list, head); + + spin_unlock(&cs_char_data.lock); + + wake_up_interruptible(&cs_char_data.wait); + kill_fasync(&cs_char_data.async_queue, SIGIO, POLL_IN); + +out: + return; +} + +static u32 cs_pop_entry(struct list_head *head) +{ + struct char_queue *entry; + u32 data; + + entry = list_entry(head->next, struct char_queue, list); + data = entry->msg; + list_del(&entry->list); + kfree(entry); + + return data; +} + +static void cs_notify_control(u32 message) +{ + cs_notify(message, &cs_char_data.chardev_queue); +} + +static void cs_notify_data(u32 message, int maxlength) +{ + cs_notify(message, &cs_char_data.dataind_queue); + + spin_lock(&cs_char_data.lock); + cs_char_data.dataind_pending++; + while (cs_char_data.dataind_pending > maxlength && + !list_empty(&cs_char_data.dataind_queue)) { + dev_dbg(&cs_char_data.cl->device, "data notification " + "queue overrun (%u entries)\n", cs_char_data.dataind_pending); + + cs_pop_entry(&cs_char_data.dataind_queue); + cs_char_data.dataind_pending--; + } + spin_unlock(&cs_char_data.lock); +} + +static inline void cs_set_cmd(struct hsi_msg *msg, u32 cmd) +{ + u32 *data = sg_virt(msg->sgt.sgl); + *data = cmd; +} + +static inline u32 cs_get_cmd(struct hsi_msg *msg) +{ + u32 *data = sg_virt(msg->sgt.sgl); + return *data; +} + +static void cs_release_cmd(struct hsi_msg *msg) +{ + struct cs_hsi_iface *hi = msg->context; + + list_add_tail(&msg->link, &hi->cmdqueue); +} + +static void cs_cmd_destructor(struct hsi_msg *msg) +{ + struct cs_hsi_iface *hi = msg->context; + + spin_lock(&hi->lock); + + dev_dbg(&cs_char_data.cl->device, "control cmd destructor\n"); + + if (hi->iface_state != CS_STATE_CLOSED) + dev_err(&hi->cl->device, "Cmd flushed while driver active\n"); + + if (msg->ttype == HSI_MSG_READ) + hi->control_state &= + ~(SSI_CHANNEL_STATE_POLL | SSI_CHANNEL_STATE_READING); + else if (msg->ttype == HSI_MSG_WRITE && + hi->control_state & SSI_CHANNEL_STATE_WRITING) + hi->control_state &= ~SSI_CHANNEL_STATE_WRITING; + + cs_release_cmd(msg); + + spin_unlock(&hi->lock); +} + +static struct hsi_msg *cs_claim_cmd(struct cs_hsi_iface* ssi) +{ + struct hsi_msg *msg; + + BUG_ON(list_empty(&ssi->cmdqueue)); + + msg = list_first_entry(&ssi->cmdqueue, struct hsi_msg, link); + list_del(&msg->link); + msg->destructor = cs_cmd_destructor; + + return msg; +} + +static void cs_free_cmds(struct cs_hsi_iface *ssi) +{ + struct hsi_msg *msg, *tmp; + + list_for_each_entry_safe(msg, tmp, &ssi->cmdqueue, link) { + list_del(&msg->link); + msg->destructor = NULL; + kfree(sg_virt(msg->sgt.sgl)); + hsi_free_msg(msg); + } +} + +static int cs_alloc_cmds(struct cs_hsi_iface *hi) +{ + struct hsi_msg *msg; + u32 *buf; + unsigned int i; + + INIT_LIST_HEAD(&hi->cmdqueue); + + for (i = 0; i < CS_MAX_CMDS; i++) { + msg = hsi_alloc_msg(1, GFP_KERNEL); + if (!msg) + goto out; + buf = kmalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) { + hsi_free_msg(msg); + goto out; + } + sg_init_one(msg->sgt.sgl, buf, sizeof(*buf)); + msg->channel = cs_char_data.channel_id_cmd; + msg->context = hi; + list_add_tail(&msg->link, &hi->cmdqueue); + } + + return 0; + +out: + cs_free_cmds(hi); + return -ENOMEM; +} + +static void cs_hsi_data_destructor(struct hsi_msg *msg) +{ + struct cs_hsi_iface *hi = msg->context; + const char *dir = (msg->ttype == HSI_MSG_READ) ? "TX" : "RX"; + + dev_dbg(&cs_char_data.cl->device, "Freeing data %s message\n", dir); + + spin_lock(&hi->lock); + if (hi->iface_state != CS_STATE_CLOSED) + dev_err(&cs_char_data.cl->device, + "Data %s flush while device active\n", dir); + if (msg->ttype == HSI_MSG_READ) + hi->data_state &= + ~(SSI_CHANNEL_STATE_POLL | SSI_CHANNEL_STATE_READING); + else + hi->data_state &= ~SSI_CHANNEL_STATE_WRITING; + + msg->status = HSI_STATUS_COMPLETED; + if (unlikely(waitqueue_active(&hi->datawait))) + wake_up_interruptible(&hi->datawait); + + spin_unlock(&hi->lock); +} + +static int cs_hsi_alloc_data(struct cs_hsi_iface *hi) +{ + struct hsi_msg *txmsg, *rxmsg; + int res = 0; + + rxmsg = hsi_alloc_msg(1, GFP_KERNEL); + if (!rxmsg) { + res = -ENOMEM; + goto out1; + } + rxmsg->channel = cs_char_data.channel_id_data; + rxmsg->destructor = cs_hsi_data_destructor; + rxmsg->context = hi; + + txmsg = hsi_alloc_msg(1, GFP_KERNEL); + if (!txmsg) { + res = -ENOMEM; + goto out2; + } + txmsg->channel = cs_char_data.channel_id_data; + txmsg->destructor = cs_hsi_data_destructor; + txmsg->context = hi; + + hi->data_rx_msg = rxmsg; + hi->data_tx_msg = txmsg; + + return 0; + +out2: + hsi_free_msg(rxmsg); +out1: + return res; +} + +static void cs_hsi_free_data_msg(struct hsi_msg *msg) +{ + WARN_ON(msg->status != HSI_STATUS_COMPLETED && + msg->status != HSI_STATUS_ERROR); + hsi_free_msg(msg); +} + +static void cs_hsi_free_data(struct cs_hsi_iface *hi) +{ + cs_hsi_free_data_msg(hi->data_rx_msg); + cs_hsi_free_data_msg(hi->data_tx_msg); +} + +static inline void __cs_hsi_error_pre(struct cs_hsi_iface *hi, + struct hsi_msg *msg, const char *info, + unsigned int *state) +{ + spin_lock(&hi->lock); + dev_err(&hi->cl->device, "HSI %s error, msg %d, state %u\n", + info, msg->status, *state); +} + +static inline void __cs_hsi_error_post(struct cs_hsi_iface *hi) +{ + spin_unlock(&hi->lock); +} + +static inline void __cs_hsi_error_read_bits(unsigned int *state) +{ + *state |= SSI_CHANNEL_STATE_ERROR; + *state &= ~(SSI_CHANNEL_STATE_READING | SSI_CHANNEL_STATE_POLL); +} + +static inline void __cs_hsi_error_write_bits(unsigned int *state) +{ + *state |= SSI_CHANNEL_STATE_ERROR; + *state &= ~SSI_CHANNEL_STATE_WRITING; +} + +static void cs_hsi_control_read_error(struct cs_hsi_iface *hi, + struct hsi_msg *msg) +{ + __cs_hsi_error_pre(hi, msg, "control read", &hi->control_state); + cs_release_cmd(msg); + __cs_hsi_error_read_bits(&hi->control_state); + __cs_hsi_error_post(hi); +} + +static void cs_hsi_control_write_error(struct cs_hsi_iface *hi, + struct hsi_msg *msg) +{ + __cs_hsi_error_pre(hi, msg, "control write", &hi->control_state); + cs_release_cmd(msg); + __cs_hsi_error_write_bits(&hi->control_state); + __cs_hsi_error_post(hi); + +} + +static void cs_hsi_data_read_error(struct cs_hsi_iface *hi, struct hsi_msg *msg) +{ + __cs_hsi_error_pre(hi, msg, "data read", &hi->data_state); + __cs_hsi_error_read_bits(&hi->data_state); + __cs_hsi_error_post(hi); +} + +static void cs_hsi_data_write_error(struct cs_hsi_iface *hi, + struct hsi_msg *msg) +{ + __cs_hsi_error_pre(hi, msg, "data write", &hi->data_state); + __cs_hsi_error_write_bits(&hi->data_state); + __cs_hsi_error_post(hi); +} + +static void cs_hsi_read_on_control_complete(struct hsi_msg *msg) +{ + u32 cmd = cs_get_cmd(msg); + struct cs_hsi_iface *hi = msg->context; + + spin_lock(&hi->lock); + hi->control_state &= ~SSI_CHANNEL_STATE_READING; + if (msg->status == HSI_STATUS_ERROR) { + dev_err(&hi->cl->device, "Control RX error detected\n"); + cs_hsi_control_read_error(hi, msg); + spin_unlock(&hi->lock); + goto out; + } + dev_dbg(&hi->cl->device, "Read on control: %08X\n", cmd); + cs_release_cmd(msg); + if (hi->flags & CS_FEAT_TSTAMP_RX_CTRL) { + struct timespec *tstamp = + &hi->mmap_cfg->tstamp_rx_ctrl; + do_posix_clock_monotonic_gettime(tstamp); + } + spin_unlock(&hi->lock); + + cs_notify_control(cmd); + +out: + cs_hsi_read_on_control(hi); +} + +static void cs_hsi_peek_on_control_complete(struct hsi_msg *msg) +{ + struct cs_hsi_iface *hi = msg->context; + int ret; + + if (msg->status == HSI_STATUS_ERROR) { + dev_err(&hi->cl->device, "Control peek RX error detected\n"); + cs_hsi_control_read_error(hi, msg); + return; + } + + WARN_ON(!(hi->control_state & SSI_CHANNEL_STATE_READING)); + + dev_dbg(&hi->cl->device, "Peek on control complete, reading\n"); + msg->sgt.nents = 1; + msg->complete = cs_hsi_read_on_control_complete; + ret = hsi_async_read(hi->cl, msg); + if (ret) + cs_hsi_control_read_error(hi, msg); +} + +static void cs_hsi_read_on_control(struct cs_hsi_iface *hi) +{ + struct hsi_msg *msg; + int ret; + + spin_lock(&hi->lock); + if (hi->control_state & SSI_CHANNEL_STATE_READING) { + dev_err(&hi->cl->device, "Control read already pending (%d)\n", + hi->control_state); + spin_unlock(&hi->lock); + return; + } + if (hi->control_state & SSI_CHANNEL_STATE_ERROR) { + dev_err(&hi->cl->device, "Control read error (%d)\n", + hi->control_state); + spin_unlock(&hi->lock); + return; + } + hi->control_state |= SSI_CHANNEL_STATE_READING; + dev_dbg(&hi->cl->device, "Issuing RX on control\n"); + msg = cs_claim_cmd(hi); + spin_unlock(&hi->lock); + + msg->sgt.nents = 0; + msg->complete = cs_hsi_peek_on_control_complete; + ret = hsi_async_read(hi->cl, msg); + if (ret) + cs_hsi_control_read_error(hi, msg); +} + +static void cs_hsi_write_on_control_complete(struct hsi_msg *msg) +{ + struct cs_hsi_iface *hi = msg->context; + if (msg->status == HSI_STATUS_COMPLETED) { + spin_lock(&hi->lock); + hi->control_state &= ~SSI_CHANNEL_STATE_WRITING; + cs_release_cmd(msg); + spin_unlock(&hi->lock); + } else if (msg->status == HSI_STATUS_ERROR) { + cs_hsi_control_write_error(hi, msg); + } else { + dev_err(&hi->cl->device, + "unexpected status in control write callback %d\n", + msg->status); + } +} + +static int cs_hsi_write_on_control(struct cs_hsi_iface *hi, u32 message) +{ + struct hsi_msg *msg; + int ret; + + spin_lock(&hi->lock); + if (hi->control_state & SSI_CHANNEL_STATE_ERROR) { + spin_unlock(&hi->lock); + return -EIO; + } + if (hi->control_state & SSI_CHANNEL_STATE_WRITING) { + dev_err(&hi->cl->device, + "Write still pending on control channel.\n"); + spin_unlock(&hi->lock); + return -EBUSY; + } + hi->control_state |= SSI_CHANNEL_STATE_WRITING; + msg = cs_claim_cmd(hi); + spin_unlock(&hi->lock); + + cs_set_cmd(msg, message); + msg->sgt.nents = 1; + msg->complete = cs_hsi_write_on_control_complete; + dev_dbg(&hi->cl->device, + "Sending control message %08X\n", message); + ret = hsi_async_write(hi->cl, msg); + if (ret) { + dev_err(&hi->cl->device, + "async_write failed with %d\n", ret); + cs_hsi_control_write_error(hi, msg); + } + + /* + * Make sure control read is always pending when issuing + * new control writes. This is needed as the controller + * may flush our messages if e.g. the peer device reboots + * unexpectedly (and we cannot directly resubmit a new read from + * the message destructor; see cs_cmd_destructor()). + */ + if (!(hi->control_state & SSI_CHANNEL_STATE_READING)) { + dev_err(&hi->cl->device, "Restarting control reads\n"); + cs_hsi_read_on_control(hi); + } + + return 0; +} + +static void cs_hsi_read_on_data_complete(struct hsi_msg *msg) +{ + struct cs_hsi_iface *hi = msg->context; + u32 payload; + + if (unlikely(msg->status == HSI_STATUS_ERROR)) { + cs_hsi_data_read_error(hi, msg); + return; + } + + spin_lock(&hi->lock); + WARN_ON(!(hi->data_state & SSI_CHANNEL_STATE_READING)); + hi->data_state &= ~SSI_CHANNEL_STATE_READING; + payload = CS_RX_DATA_RECEIVED; + payload |= hi->rx_slot; + hi->rx_slot++; + hi->rx_slot %= hi->rx_ptr_boundary; + /* expose current rx ptr in mmap area */ + hi->mmap_cfg->rx_ptr = hi->rx_slot; + if (unlikely(waitqueue_active(&hi->datawait))) + wake_up_interruptible(&hi->datawait); + spin_unlock(&hi->lock); + + cs_notify_data(payload, hi->rx_bufs); + cs_hsi_read_on_data(hi); +} + +static void cs_hsi_peek_on_data_complete(struct hsi_msg *msg) +{ + struct cs_hsi_iface *hi = msg->context; + u32 *address; + int ret; + + if (unlikely(msg->status == HSI_STATUS_ERROR)) { + cs_hsi_data_read_error(hi, msg); + return; + } + if (unlikely(hi->iface_state != CS_STATE_CONFIGURED)) { + dev_err(&hi->cl->device, "Data received in invalid state\n"); + cs_hsi_data_read_error(hi, msg); + return; + } + + spin_lock(&hi->lock); + WARN_ON(!(hi->data_state & SSI_CHANNEL_STATE_POLL)); + hi->data_state &= ~SSI_CHANNEL_STATE_POLL; + hi->data_state |= SSI_CHANNEL_STATE_READING; + spin_unlock(&hi->lock); + + address = (u32 *)(hi->mmap_base + + hi->rx_offsets[hi->rx_slot % hi->rx_bufs]); + sg_init_one(msg->sgt.sgl, address, hi->buf_size); + msg->sgt.nents = 1; + msg->complete = cs_hsi_read_on_data_complete; + ret = hsi_async_read(hi->cl, msg); + if (ret) + cs_hsi_data_read_error(hi, msg); +} + +/* + * Read/write transaction is ongoing. Returns false if in + * SSI_CHANNEL_STATE_POLL state. + */ +static inline int cs_state_xfer_active(unsigned int state) +{ + return (state & SSI_CHANNEL_STATE_WRITING) || + (state & SSI_CHANNEL_STATE_READING); +} + +/* + * No pending read/writes + */ +static inline int cs_state_idle(unsigned int state) +{ + return !(state & ~SSI_CHANNEL_STATE_ERROR); +} + +static void cs_hsi_read_on_data(struct cs_hsi_iface *hi) +{ + struct hsi_msg *rxmsg; + int ret; + + spin_lock(&hi->lock); + if (hi->data_state & + (SSI_CHANNEL_STATE_READING | SSI_CHANNEL_STATE_POLL)) { + dev_dbg(&hi->cl->device, "Data read already pending (%u)\n", + hi->data_state); + spin_unlock(&hi->lock); + return; + } + hi->data_state |= SSI_CHANNEL_STATE_POLL; + spin_unlock(&hi->lock); + + rxmsg = hi->data_rx_msg; + sg_init_one(rxmsg->sgt.sgl, (void *)hi->mmap_base, 0); + rxmsg->sgt.nents = 0; + rxmsg->complete = cs_hsi_peek_on_data_complete; + + ret = hsi_async_read(hi->cl, rxmsg); + if (ret) + cs_hsi_data_read_error(hi, rxmsg); +} + +static void cs_hsi_write_on_data_complete(struct hsi_msg *msg) +{ + struct cs_hsi_iface *hi = msg->context; + + if (msg->status == HSI_STATUS_COMPLETED) { + spin_lock(&hi->lock); + hi->data_state &= ~SSI_CHANNEL_STATE_WRITING; + if (unlikely(waitqueue_active(&hi->datawait))) + wake_up_interruptible(&hi->datawait); + spin_unlock(&hi->lock); + } else { + cs_hsi_data_write_error(hi, msg); + } +} + +static int cs_hsi_write_on_data(struct cs_hsi_iface *hi, unsigned int slot) +{ + u32 *address; + struct hsi_msg *txmsg; + int ret; + + spin_lock(&hi->lock); + if (hi->iface_state != CS_STATE_CONFIGURED) { + dev_err(&hi->cl->device, "Not configured, aborting\n"); + ret = -EINVAL; + goto error; + } + if (hi->data_state & SSI_CHANNEL_STATE_ERROR) { + dev_err(&hi->cl->device, "HSI error, aborting\n"); + ret = -EIO; + goto error; + } + if (hi->data_state & SSI_CHANNEL_STATE_WRITING) { + dev_err(&hi->cl->device, "Write pending on data channel.\n"); + ret = -EBUSY; + goto error; + } + hi->data_state |= SSI_CHANNEL_STATE_WRITING; + spin_unlock(&hi->lock); + + hi->tx_slot = slot; + address = (u32 *)(hi->mmap_base + hi->tx_offsets[hi->tx_slot]); + txmsg = hi->data_tx_msg; + sg_init_one(txmsg->sgt.sgl, address, hi->buf_size); + txmsg->complete = cs_hsi_write_on_data_complete; + ret = hsi_async_write(hi->cl, txmsg); + if (ret) + cs_hsi_data_write_error(hi, txmsg); + + return ret; + +error: + spin_unlock(&hi->lock); + if (ret == -EIO) + cs_hsi_data_write_error(hi, hi->data_tx_msg); + + return ret; +} + +static unsigned int cs_hsi_get_state(struct cs_hsi_iface *hi) +{ + return hi->iface_state; +} + +static int cs_hsi_command(struct cs_hsi_iface *hi, u32 cmd) +{ + int ret = 0; + + local_bh_disable(); + switch (cmd & TARGET_MASK) { + case TARGET_REMOTE: + ret = cs_hsi_write_on_control(hi, cmd); + break; + case TARGET_LOCAL: + if ((cmd & CS_CMD_MASK) == CS_TX_DATA_READY) + ret = cs_hsi_write_on_data(hi, cmd & CS_PARAM_MASK); + else + ret = -EINVAL; + break; + default: + ret = -EINVAL; + break; + } + local_bh_enable(); + + return ret; +} + +static void cs_hsi_set_wakeline(struct cs_hsi_iface *hi, bool new_state) +{ + int change = 0; + + spin_lock_bh(&hi->lock); + if (hi->wakeline_state != new_state) { + hi->wakeline_state = new_state; + change = 1; + dev_dbg(&hi->cl->device, "setting wake line to %d (%p)\n", + new_state, hi->cl); + } + spin_unlock_bh(&hi->lock); + + if (change) { + if (new_state) + ssip_slave_start_tx(hi->master); + else + ssip_slave_stop_tx(hi->master); + } + + dev_dbg(&hi->cl->device, "wake line set to %d (%p)\n", + new_state, hi->cl); +} + +static void set_buffer_sizes(struct cs_hsi_iface *hi, int rx_bufs, int tx_bufs) +{ + hi->rx_bufs = rx_bufs; + hi->tx_bufs = tx_bufs; + hi->mmap_cfg->rx_bufs = rx_bufs; + hi->mmap_cfg->tx_bufs = tx_bufs; + + if (hi->flags & CS_FEAT_ROLLING_RX_COUNTER) { + /* + * For more robust overrun detection, let the rx + * pointer run in range 0..'boundary-1'. Boundary + * is a multiple of rx_bufs, and limited in max size + * by RX_PTR_MAX_SHIFT to allow for fast ptr-diff + * calculation. + */ + hi->rx_ptr_boundary = (rx_bufs << RX_PTR_BOUNDARY_SHIFT); + hi->mmap_cfg->rx_ptr_boundary = hi->rx_ptr_boundary; + } else { + hi->rx_ptr_boundary = hi->rx_bufs; + } +} + +static int check_buf_params(struct cs_hsi_iface *hi, + const struct cs_buffer_config *buf_cfg) +{ + size_t buf_size_aligned = L1_CACHE_ALIGN(buf_cfg->buf_size) * + (buf_cfg->rx_bufs + buf_cfg->tx_bufs); + size_t ctrl_size_aligned = L1_CACHE_ALIGN(sizeof(*hi->mmap_cfg)); + int r = 0; + + if (buf_cfg->rx_bufs > CS_MAX_BUFFERS || + buf_cfg->tx_bufs > CS_MAX_BUFFERS) { + r = -EINVAL; + } else if ((buf_size_aligned + ctrl_size_aligned) >= hi->mmap_size) { + dev_err(&hi->cl->device, "No space for the requested buffer " + "configuration\n"); + r = -ENOBUFS; + } + + return r; +} + +/** + * Block until pending data transfers have completed. + */ +static int cs_hsi_data_sync(struct cs_hsi_iface *hi) +{ + int r = 0; + + spin_lock_bh(&hi->lock); + + if (!cs_state_xfer_active(hi->data_state)) { + dev_dbg(&hi->cl->device, "hsi_data_sync break, idle\n"); + goto out; + } + + for (;;) { + int s; + DEFINE_WAIT(wait); + if (!cs_state_xfer_active(hi->data_state)) + goto out; + if (signal_pending(current)) { + r = -ERESTARTSYS; + goto out; + } + /** + * prepare_to_wait must be called with hi->lock held + * so that callbacks can check for waitqueue_active() + */ + prepare_to_wait(&hi->datawait, &wait, TASK_INTERRUPTIBLE); + spin_unlock_bh(&hi->lock); + s = schedule_timeout( + msecs_to_jiffies(CS_HSI_TRANSFER_TIMEOUT_MS)); + spin_lock_bh(&hi->lock); + finish_wait(&hi->datawait, &wait); + if (!s) { + dev_dbg(&hi->cl->device, + "hsi_data_sync timeout after %d ms\n", + CS_HSI_TRANSFER_TIMEOUT_MS); + r = -EIO; + goto out; + } + } + +out: + spin_unlock_bh(&hi->lock); + dev_dbg(&hi->cl->device, "hsi_data_sync done with res %d\n", r); + + return r; +} + +static void cs_hsi_data_enable(struct cs_hsi_iface *hi, + struct cs_buffer_config *buf_cfg) +{ + unsigned int data_start, i; + + BUG_ON(hi->buf_size == 0); + + set_buffer_sizes(hi, buf_cfg->rx_bufs, buf_cfg->tx_bufs); + + hi->slot_size = L1_CACHE_ALIGN(hi->buf_size); + dev_dbg(&hi->cl->device, + "setting slot size to %u, buf size %u, align %u\n", + hi->slot_size, hi->buf_size, L1_CACHE_BYTES); + + data_start = L1_CACHE_ALIGN(sizeof(*hi->mmap_cfg)); + dev_dbg(&hi->cl->device, + "setting data start at %u, cfg block %u, align %u\n", + data_start, sizeof(*hi->mmap_cfg), L1_CACHE_BYTES); + + for (i = 0; i < hi->mmap_cfg->rx_bufs; i++) { + hi->rx_offsets[i] = data_start + i * hi->slot_size; + hi->mmap_cfg->rx_offsets[i] = hi->rx_offsets[i]; + dev_dbg(&hi->cl->device, "DL buf #%u at %u\n", + i, hi->rx_offsets[i]); + } + for (i = 0; i < hi->mmap_cfg->tx_bufs; i++) { + hi->tx_offsets[i] = data_start + + (i + hi->mmap_cfg->rx_bufs) * hi->slot_size; + hi->mmap_cfg->tx_offsets[i] = hi->tx_offsets[i]; + dev_dbg(&hi->cl->device, "UL buf #%u at %u\n", + i, hi->rx_offsets[i]); + } + + hi->iface_state = CS_STATE_CONFIGURED; +} + +static void cs_hsi_data_disable(struct cs_hsi_iface *hi, int old_state) +{ + if (old_state == CS_STATE_CONFIGURED) { + dev_dbg(&hi->cl->device, + "closing data channel with slot size 0\n"); + hi->iface_state = CS_STATE_OPENED; + } +} + +static int cs_hsi_buf_config(struct cs_hsi_iface *hi, + struct cs_buffer_config *buf_cfg) +{ + int r = 0; + unsigned int old_state = hi->iface_state; + + spin_lock_bh(&hi->lock); + /* Prevent new transactions during buffer reconfig */ + if (old_state == CS_STATE_CONFIGURED) + hi->iface_state = CS_STATE_OPENED; + spin_unlock_bh(&hi->lock); + + /* + * make sure that no non-zero data reads are ongoing before + * proceeding to change the buffer layout + */ + r = cs_hsi_data_sync(hi); + if (r < 0) + return r; + + WARN_ON(cs_state_xfer_active(hi->data_state)); + + spin_lock_bh(&hi->lock); + r = check_buf_params(hi, buf_cfg); + if (r < 0) + goto error; + + hi->buf_size = buf_cfg->buf_size; + hi->mmap_cfg->buf_size = hi->buf_size; + hi->flags = buf_cfg->flags; + + hi->rx_slot = 0; + hi->tx_slot = 0; + hi->slot_size = 0; + + if (hi->buf_size) + cs_hsi_data_enable(hi, buf_cfg); + else + cs_hsi_data_disable(hi, old_state); + + spin_unlock_bh(&hi->lock); + + if (old_state != hi->iface_state) { + if (hi->iface_state == CS_STATE_CONFIGURED) { + pm_qos_add_request(&hi->pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + CS_QOS_LATENCY_FOR_DATA_USEC); + local_bh_disable(); + cs_hsi_read_on_data(hi); + local_bh_enable(); + } else if (old_state == CS_STATE_CONFIGURED) { + pm_qos_remove_request(&hi->pm_qos_req); + } + } + return r; + +error: + spin_unlock_bh(&hi->lock); + return r; +} + +static int cs_hsi_start(struct cs_hsi_iface **hi, struct hsi_client *cl, + unsigned long mmap_base, unsigned long mmap_size) +{ + int err = 0; + struct cs_hsi_iface *hsi_if = kzalloc(sizeof(*hsi_if), GFP_KERNEL); + + dev_dbg(&cl->device, "cs_hsi_start\n"); + + if (!hsi_if) { + err = -ENOMEM; + goto leave0; + } + spin_lock_init(&hsi_if->lock); + hsi_if->cl = cl; + hsi_if->iface_state = CS_STATE_CLOSED; + hsi_if->mmap_cfg = (struct cs_mmap_config_block *)mmap_base; + hsi_if->mmap_base = mmap_base; + hsi_if->mmap_size = mmap_size; + memset(hsi_if->mmap_cfg, 0, sizeof(*hsi_if->mmap_cfg)); + init_waitqueue_head(&hsi_if->datawait); + err = cs_alloc_cmds(hsi_if); + if (err < 0) { + dev_err(&cl->device, "Unable to alloc HSI messages\n"); + goto leave1; + } + err = cs_hsi_alloc_data(hsi_if); + if (err < 0) { + dev_err(&cl->device, "Unable to alloc HSI messages for data\n"); + goto leave2; + } + err = hsi_claim_port(cl, 1); + if (err < 0) { + dev_err(&cl->device, + "Could not open, HSI port already claimed\n"); + goto leave3; + } + hsi_if->master = ssip_slave_get_master(cl); + if (IS_ERR(hsi_if->master)) { + err = PTR_ERR(hsi_if->master); + dev_err(&cl->device, "Could not get HSI master client\n"); + goto leave4; + } + if (!ssip_slave_running(hsi_if->master)) { + err = -ENODEV; + dev_err(&cl->device, + "HSI port not initialized\n"); + goto leave4; + } + + hsi_if->iface_state = CS_STATE_OPENED; + local_bh_disable(); + cs_hsi_read_on_control(hsi_if); + local_bh_enable(); + + dev_dbg(&cl->device, "cs_hsi_start...done\n"); + + BUG_ON(!hi); + *hi = hsi_if; + + return 0; + +leave4: + hsi_release_port(cl); +leave3: + cs_hsi_free_data(hsi_if); +leave2: + cs_free_cmds(hsi_if); +leave1: + kfree(hsi_if); +leave0: + dev_dbg(&cl->device, "cs_hsi_start...done/error\n\n"); + + return err; +} + +static void cs_hsi_stop(struct cs_hsi_iface *hi) +{ + dev_dbg(&hi->cl->device, "cs_hsi_stop\n"); + cs_hsi_set_wakeline(hi, 0); + ssip_slave_put_master(hi->master); + + /* hsi_release_port() needs to be called with CS_STATE_CLOSED */ + hi->iface_state = CS_STATE_CLOSED; + hsi_release_port(hi->cl); + + /* + * hsi_release_port() should flush out all the pending + * messages, so cs_state_idle() should be true for both + * control and data channels. + */ + WARN_ON(!cs_state_idle(hi->control_state)); + WARN_ON(!cs_state_idle(hi->data_state)); + + if (pm_qos_request_active(&hi->pm_qos_req)) + pm_qos_remove_request(&hi->pm_qos_req); + + spin_lock_bh(&hi->lock); + cs_hsi_free_data(hi); + cs_free_cmds(hi); + spin_unlock_bh(&hi->lock); + kfree(hi); +} + +static int cs_char_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct cs_char *csdata = vma->vm_private_data; + struct page *page; + + page = virt_to_page(csdata->mmap_base); + get_page(page); + vmf->page = page; + + return 0; +} + +static struct vm_operations_struct cs_char_vm_ops = { + .fault = cs_char_vma_fault, +}; + +static int cs_char_fasync(int fd, struct file *file, int on) +{ + struct cs_char *csdata = file->private_data; + + if (fasync_helper(fd, file, on, &csdata->async_queue) < 0) + return -EIO; + + return 0; +} + +static unsigned int cs_char_poll(struct file *file, poll_table *wait) +{ + struct cs_char *csdata = file->private_data; + unsigned int ret = 0; + + poll_wait(file, &cs_char_data.wait, wait); + spin_lock_bh(&csdata->lock); + if (!list_empty(&csdata->chardev_queue)) + ret = POLLIN | POLLRDNORM; + else if (!list_empty(&csdata->dataind_queue)) + ret = POLLIN | POLLRDNORM; + spin_unlock_bh(&csdata->lock); + + return ret; +} + +static ssize_t cs_char_read(struct file *file, char __user *buf, size_t count, + loff_t *unused) +{ + struct cs_char *csdata = file->private_data; + u32 data; + ssize_t retval; + + if (count < sizeof(data)) + return -EINVAL; + + for (;;) { + DEFINE_WAIT(wait); + + spin_lock_bh(&csdata->lock); + if (!list_empty(&csdata->chardev_queue)) { + data = cs_pop_entry(&csdata->chardev_queue); + } else if (!list_empty(&csdata->dataind_queue)) { + data = cs_pop_entry(&csdata->dataind_queue); + csdata->dataind_pending--; + } else { + data = 0; + } + spin_unlock_bh(&csdata->lock); + + if (data) + break; + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto out; + } else if (signal_pending(current)) { + retval = -ERESTARTSYS; + goto out; + } + prepare_to_wait_exclusive(&csdata->wait, &wait, + TASK_INTERRUPTIBLE); + schedule(); + finish_wait(&csdata->wait, &wait); + } + + retval = put_user(data, (u32 __user *)buf); + if (!retval) + retval = sizeof(data); + +out: + return retval; +} + +static ssize_t cs_char_write(struct file *file, const char __user *buf, + size_t count, loff_t *unused) +{ + struct cs_char *csdata = file->private_data; + u32 data; + int err; + ssize_t retval; + + if (count < sizeof(data)) + return -EINVAL; + + if (get_user(data, (u32 __user *)buf)) + retval = -EFAULT; + else + retval = count; + + err = cs_hsi_command(csdata->hi, data); + if (err < 0) + retval = err; + + return retval; +} + +static long cs_char_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct cs_char *csdata = file->private_data; + int r = 0; + + switch (cmd) { + case CS_GET_STATE: { + unsigned int state; + + state = cs_hsi_get_state(csdata->hi); + if (copy_to_user((void __user *)arg, &state, sizeof(state))) + r = -EFAULT; + + break; + } + case CS_SET_WAKELINE: { + unsigned int state; + + if (copy_from_user(&state, (void __user *)arg, sizeof(state))) { + r = -EFAULT; + break; + } + + if (state > 1) { + r = -EINVAL; + break; + } + + cs_hsi_set_wakeline(csdata->hi, !!state); + + break; + } + case CS_GET_IF_VERSION: { + unsigned int ifver = CS_IF_VERSION; + + if (copy_to_user((void __user *)arg, &ifver, sizeof(ifver))) + r = -EFAULT; + + break; + } + case CS_CONFIG_BUFS: { + struct cs_buffer_config buf_cfg; + + if (copy_from_user(&buf_cfg, (void __user *)arg, + sizeof(buf_cfg))) + r = -EFAULT; + else + r = cs_hsi_buf_config(csdata->hi, &buf_cfg); + + break; + } + default: + r = -ENOTTY; + break; + } + + return r; +} + +static int cs_char_mmap(struct file *file, struct vm_area_struct *vma) +{ + if (vma->vm_end < vma->vm_start) + return -EINVAL; + + if (((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) != 1) + return -EINVAL; + + vma->vm_flags |= VM_IO | VM_DONTDUMP | VM_DONTEXPAND; + vma->vm_ops = &cs_char_vm_ops; + vma->vm_private_data = file->private_data; + + return 0; +} + +static int cs_char_open(struct inode *unused, struct file *file) +{ + int ret = 0; + unsigned long p; + + spin_lock_bh(&cs_char_data.lock); + if (cs_char_data.opened) { + ret = -EBUSY; + spin_unlock_bh(&cs_char_data.lock); + goto out1; + } + cs_char_data.opened = 1; + cs_char_data.dataind_pending = 0; + spin_unlock_bh(&cs_char_data.lock); + + p = get_zeroed_page(GFP_KERNEL); + if (!p) { + ret = -ENOMEM; + goto out2; + } + + ret = cs_hsi_start(&cs_char_data.hi, cs_char_data.cl, p, CS_MMAP_SIZE); + if (ret) { + dev_err(&cs_char_data.cl->device, "Unable to initialize HSI\n"); + goto out3; + } + + /* these are only used in release so lock not needed */ + cs_char_data.mmap_base = p; + cs_char_data.mmap_size = CS_MMAP_SIZE; + + file->private_data = &cs_char_data; + + return 0; + +out3: + free_page(p); +out2: + spin_lock_bh(&cs_char_data.lock); + cs_char_data.opened = 0; + spin_unlock_bh(&cs_char_data.lock); +out1: + return ret; +} + +static void cs_free_char_queue(struct list_head *head) +{ + struct char_queue *entry; + struct list_head *cursor, *next; + + if (!list_empty(head)) { + list_for_each_safe(cursor, next, head) { + entry = list_entry(cursor, struct char_queue, list); + list_del(&entry->list); + kfree(entry); + } + } + +} + +static int cs_char_release(struct inode *unused, struct file *file) +{ + struct cs_char *csdata = file->private_data; + + cs_hsi_stop(csdata->hi); + spin_lock_bh(&csdata->lock); + csdata->hi = NULL; + free_page(csdata->mmap_base); + cs_free_char_queue(&csdata->chardev_queue); + cs_free_char_queue(&csdata->dataind_queue); + csdata->opened = 0; + spin_unlock_bh(&csdata->lock); + + return 0; +} + +static const struct file_operations cs_char_fops = { + .owner = THIS_MODULE, + .read = cs_char_read, + .write = cs_char_write, + .poll = cs_char_poll, + .unlocked_ioctl = cs_char_ioctl, + .mmap = cs_char_mmap, + .open = cs_char_open, + .release = cs_char_release, + .fasync = cs_char_fasync, +}; + +static struct miscdevice cs_char_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "cmt_speech", + .fops = &cs_char_fops +}; + +static int cs_hsi_client_probe(struct device *dev) +{ + int err = 0; + struct hsi_client *cl = to_hsi_client(dev); + + dev_dbg(dev, "hsi_client_probe\n"); + init_waitqueue_head(&cs_char_data.wait); + spin_lock_init(&cs_char_data.lock); + cs_char_data.opened = 0; + cs_char_data.cl = cl; + cs_char_data.hi = NULL; + INIT_LIST_HEAD(&cs_char_data.chardev_queue); + INIT_LIST_HEAD(&cs_char_data.dataind_queue); + + cs_char_data.channel_id_cmd = hsi_get_channel_id_by_name(cl, + "speech-control"); + if (cs_char_data.channel_id_cmd < 0) { + err = cs_char_data.channel_id_cmd; + dev_err(dev, "Could not get cmd channel (%d)\n", err); + return err; + } + + cs_char_data.channel_id_data = hsi_get_channel_id_by_name(cl, + "speech-data"); + if (cs_char_data.channel_id_data < 0) { + err = cs_char_data.channel_id_data; + dev_err(dev, "Could not get data channel (%d)\n", err); + return err; + } + + err = misc_register(&cs_char_miscdev); + if (err) + dev_err(dev, "Failed to register: %d\n", err); + + return err; +} + +static int cs_hsi_client_remove(struct device *dev) +{ + struct cs_hsi_iface *hi; + + dev_dbg(dev, "hsi_client_remove\n"); + misc_deregister(&cs_char_miscdev); + spin_lock_bh(&cs_char_data.lock); + hi = cs_char_data.hi; + cs_char_data.hi = NULL; + spin_unlock_bh(&cs_char_data.lock); + if (hi) + cs_hsi_stop(hi); + + return 0; +} + +static struct hsi_client_driver cs_hsi_driver = { + .driver = { + .name = "cmt-speech", + .owner = THIS_MODULE, + .probe = cs_hsi_client_probe, + .remove = cs_hsi_client_remove, + }, +}; + +static int __init cs_char_init(void) +{ + pr_info("CMT speech driver added\n"); + return hsi_register_client_driver(&cs_hsi_driver); +} +module_init(cs_char_init); + +static void __exit cs_char_exit(void) +{ + hsi_unregister_client_driver(&cs_hsi_driver); + pr_info("CMT speech driver removed\n"); +} +module_exit(cs_char_exit); + +MODULE_ALIAS("hsi:cmt-speech"); +MODULE_AUTHOR("Kai Vehmanen <kai.vehmanen@nokia.com>"); +MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>"); +MODULE_DESCRIPTION("CMT speech driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hsi/clients/nokia-modem.c b/drivers/hsi/clients/nokia-modem.c index eb4dc63..bbb1923 100644 --- a/drivers/hsi/clients/nokia-modem.c +++ b/drivers/hsi/clients/nokia-modem.c @@ -46,6 +46,7 @@ struct nokia_modem_device { struct nokia_modem_gpio *gpios; int gpio_amount; struct hsi_client *ssi_protocol; + struct hsi_client *cmt_speech; }; static void do_nokia_modem_rst_ind_tasklet(unsigned long data) @@ -149,6 +150,7 @@ static int nokia_modem_probe(struct device *dev) struct hsi_port *port = hsi_get_port(cl); int irq, pflags, err; struct hsi_board_info ssip; + struct hsi_board_info cmtspeech; np = dev->of_node; if (!np) { @@ -200,6 +202,7 @@ static int nokia_modem_probe(struct device *dev) modem->ssi_protocol = hsi_new_client(port, &ssip); if (!modem->ssi_protocol) { dev_err(dev, "Could not register ssi-protocol device\n"); + err = -ENOMEM; goto error2; } @@ -213,12 +216,35 @@ static int nokia_modem_probe(struct device *dev) goto error3; } - /* TODO: register cmt-speech hsi client */ + cmtspeech.name = "cmt-speech"; + cmtspeech.tx_cfg = cl->tx_cfg; + cmtspeech.rx_cfg = cl->rx_cfg; + cmtspeech.platform_data = NULL; + cmtspeech.archdata = NULL; + + modem->cmt_speech = hsi_new_client(port, &cmtspeech); + if (!modem->cmt_speech) { + dev_err(dev, "Could not register cmt-speech device\n"); + err = -ENOMEM; + goto error3; + } + + err = device_attach(&modem->cmt_speech->device); + if (err == 0) { + dev_err(dev, "Missing cmt-speech driver\n"); + err = -EPROBE_DEFER; + goto error4; + } else if (err < 0) { + dev_err(dev, "Could not load cmt-speech driver (%d)\n", err); + goto error4; + } dev_info(dev, "Registered Nokia HSI modem\n"); return 0; +error4: + hsi_remove_client(&modem->cmt_speech->device, NULL); error3: hsi_remove_client(&modem->ssi_protocol->device, NULL); error2: @@ -237,6 +263,11 @@ static int nokia_modem_remove(struct device *dev) if (!modem) return 0; + if (modem->cmt_speech) { + hsi_remove_client(&modem->cmt_speech->device, NULL); + modem->cmt_speech = NULL; + } + if (modem->ssi_protocol) { hsi_remove_client(&modem->ssi_protocol->device, NULL); modem->ssi_protocol = NULL; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 110fade..25d9e72 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -510,6 +510,7 @@ config SENSORS_G762 config SENSORS_GPIO_FAN tristate "GPIO fan" depends on GPIOLIB + depends on THERMAL || THERMAL=n help If you say yes here you get support for fans connected to GPIO lines. @@ -599,8 +600,8 @@ config SENSORS_IT87 help If you say yes here you get support for ITE IT8705F, IT8712F, IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E, - IT8771E, IT8772E, IT8782F, IT8783E/F and IT8603E sensor chips, - and the SiS950 clone. + IT8771E, IT8772E, IT8781F, IT8782F, IT8783E/F, IT8786E, IT8790E, + IT8603E, IT8620E, and IT8623E sensor chips, and the SiS950 clone. This driver can also be built as a module. If so, the module will be called it87. @@ -624,7 +625,7 @@ config SENSORS_JC42 mobile devices and servers. Support will include, but not be limited to, ADT7408, AT30TS00, CAT34TS02, CAT6095, MAX6604, MCP9804, MCP9805, MCP98242, MCP98243, MCP98244, MCP9843, SE97, SE98, STTS424(E), - STTS2002, STTS3000, TSE2002B3, TSE2002GB2, TS3000B3, and TS3000GB2. + STTS2002, STTS3000, TSE2002, TSE2004, TS3000, and TS3001. This driver can also be built as a module. If so, the module will be called jc42. @@ -1145,6 +1146,16 @@ config SENSORS_NCT7802 This driver can also be built as a module. If so, the module will be called nct7802. +config SENSORS_NCT7904 + tristate "Nuvoton NCT7904" + depends on I2C + help + If you say yes here you get support for the Nuvoton NCT7904 + hardware monitoring chip, including manual fan speed control. + + This driver can also be built as a module. If so, the module + will be called nct7904. + config SENSORS_PCF8591 tristate "Philips PCF8591 ADC/DAC" depends on I2C @@ -1164,6 +1175,7 @@ source drivers/hwmon/pmbus/Kconfig config SENSORS_PWM_FAN tristate "PWM fan" depends on (PWM && OF) || COMPILE_TEST + depends on THERMAL || THERMAL=n help If you say yes here you get support for fans connected to PWM lines. The driver uses the generic PWM interface, thus it will work on a diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 6c94147..b4a40f1 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -120,6 +120,7 @@ obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o +obj-$(CONFIG_SENSORS_NCT7904) += nct7904.o obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index bce4e9f..6c99ee7 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -147,6 +147,9 @@ static int ads7828_probe(struct i2c_client *client, &ads2830_regmap_config); } + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + data->cmd_byte = ext_vref ? ADS7828_CMD_PD1 : ADS7828_CMD_PD3; if (!diff_input) data->cmd_byte |= ADS7828_CMD_SD_SE; diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 5b7fec8..ed303ba 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -397,14 +397,13 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev, struct device_attribute *devattr, char *buf) = { show_label, show_crit_alarm, show_temp, show_tjmax, show_ttarget }; - static const char *const names[TOTAL_ATTRS] = { - "temp%d_label", "temp%d_crit_alarm", - "temp%d_input", "temp%d_crit", - "temp%d_max" }; + static const char *const suffixes[TOTAL_ATTRS] = { + "label", "crit_alarm", "input", "crit", "max" + }; for (i = 0; i < tdata->attr_size; i++) { - snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i], - attr_no); + snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, + "temp%d_%s", attr_no, suffixes[i]); sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr); tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i]; tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO; diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 36abf81..a3dae6d 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -34,10 +34,13 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/of_gpio.h> +#include <linux/thermal.h> struct gpio_fan_data { struct platform_device *pdev; struct device *hwmon_dev; + /* Cooling device if any */ + struct thermal_cooling_device *cdev; struct mutex lock; /* lock GPIOs operations. */ int num_ctrl; unsigned *ctrl; @@ -387,6 +390,53 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data, return 0; } +static int gpio_fan_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct gpio_fan_data *fan_data = cdev->devdata; + + if (!fan_data) + return -EINVAL; + + *state = fan_data->num_speed - 1; + return 0; +} + +static int gpio_fan_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct gpio_fan_data *fan_data = cdev->devdata; + int r; + + if (!fan_data) + return -EINVAL; + + r = get_fan_speed_index(fan_data); + if (r < 0) + return r; + + *state = r; + return 0; +} + +static int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct gpio_fan_data *fan_data = cdev->devdata; + + if (!fan_data) + return -EINVAL; + + set_fan_speed(fan_data, state); + return 0; +} + +static const struct thermal_cooling_device_ops gpio_fan_cool_ops = { + .get_max_state = gpio_fan_get_max_state, + .get_cur_state = gpio_fan_get_cur_state, + .set_cur_state = gpio_fan_set_cur_state, +}; + #ifdef CONFIG_OF_GPIO /* * Translate OpenFirmware node properties into platform_data @@ -404,10 +454,32 @@ static int gpio_fan_get_of_pdata(struct device *dev, node = dev->of_node; + /* Alarm GPIO if one exists */ + if (of_gpio_named_count(node, "alarm-gpios") > 0) { + struct gpio_fan_alarm *alarm; + int val; + enum of_gpio_flags flags; + + alarm = devm_kzalloc(dev, sizeof(struct gpio_fan_alarm), + GFP_KERNEL); + if (!alarm) + return -ENOMEM; + + val = of_get_named_gpio_flags(node, "alarm-gpios", 0, &flags); + if (val < 0) + return val; + alarm->gpio = val; + alarm->active_low = flags & OF_GPIO_ACTIVE_LOW; + + pdata->alarm = alarm; + } + /* Fill GPIO pin array */ pdata->num_ctrl = of_gpio_count(node); if (pdata->num_ctrl <= 0) { - dev_err(dev, "gpios DT property empty / missing"); + if (pdata->alarm) + return 0; + dev_err(dev, "DT properties empty / missing"); return -ENODEV; } ctrl = devm_kzalloc(dev, pdata->num_ctrl * sizeof(unsigned), @@ -460,26 +532,6 @@ static int gpio_fan_get_of_pdata(struct device *dev, } pdata->speed = speed; - /* Alarm GPIO if one exists */ - if (of_gpio_named_count(node, "alarm-gpios") > 0) { - struct gpio_fan_alarm *alarm; - int val; - enum of_gpio_flags flags; - - alarm = devm_kzalloc(dev, sizeof(struct gpio_fan_alarm), - GFP_KERNEL); - if (!alarm) - return -ENOMEM; - - val = of_get_named_gpio_flags(node, "alarm-gpios", 0, &flags); - if (val < 0) - return val; - alarm->gpio = val; - alarm->active_low = flags & OF_GPIO_ACTIVE_LOW; - - pdata->alarm = alarm; - } - return 0; } @@ -495,6 +547,11 @@ static int gpio_fan_probe(struct platform_device *pdev) struct gpio_fan_data *fan_data; struct gpio_fan_platform_data *pdata = dev_get_platdata(&pdev->dev); + fan_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_fan_data), + GFP_KERNEL); + if (!fan_data) + return -ENOMEM; + #ifdef CONFIG_OF_GPIO if (!pdata) { pdata = devm_kzalloc(&pdev->dev, @@ -512,11 +569,6 @@ static int gpio_fan_probe(struct platform_device *pdev) return -EINVAL; #endif /* CONFIG_OF_GPIO */ - fan_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_fan_data), - GFP_KERNEL); - if (!fan_data) - return -ENOMEM; - fan_data->pdev = pdev; platform_set_drvdata(pdev, fan_data); mutex_init(&fan_data->lock); @@ -544,18 +596,39 @@ static int gpio_fan_probe(struct platform_device *pdev) gpio_fan_groups); if (IS_ERR(fan_data->hwmon_dev)) return PTR_ERR(fan_data->hwmon_dev); +#ifdef CONFIG_OF_GPIO + /* Optional cooling device register for Device tree platforms */ + fan_data->cdev = thermal_of_cooling_device_register(pdev->dev.of_node, + "gpio-fan", + fan_data, + &gpio_fan_cool_ops); +#else /* CONFIG_OF_GPIO */ + /* Optional cooling device register for non Device tree platforms */ + fan_data->cdev = thermal_cooling_device_register("gpio-fan", fan_data, + &gpio_fan_cool_ops); +#endif /* CONFIG_OF_GPIO */ dev_info(&pdev->dev, "GPIO fan initialized\n"); return 0; } -static void gpio_fan_shutdown(struct platform_device *pdev) +static int gpio_fan_remove(struct platform_device *pdev) { - struct gpio_fan_data *fan_data = dev_get_drvdata(&pdev->dev); + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); + + if (!IS_ERR(fan_data->cdev)) + thermal_cooling_device_unregister(fan_data->cdev); if (fan_data->ctrl) set_fan_speed(fan_data, 0); + + return 0; +} + +static void gpio_fan_shutdown(struct platform_device *pdev) +{ + gpio_fan_remove(pdev); } #ifdef CONFIG_PM_SLEEP @@ -589,6 +662,7 @@ static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume); static struct platform_driver gpio_fan_driver = { .probe = gpio_fan_probe, + .remove = gpio_fan_remove, .shutdown = gpio_fan_shutdown, .driver = { .name = "gpio-fan", diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c index 030e7ff..21b9c72 100644 --- a/drivers/hwmon/ibmpex.c +++ b/drivers/hwmon/ibmpex.c @@ -56,15 +56,10 @@ static u8 const temp_sensor_sig[] = {0x74, 0x65, 0x6D}; static u8 const watt_sensor_sig[] = {0x41, 0x43}; #define PEX_NUM_SENSOR_FUNCS 3 -static char const * const power_sensor_name_templates[] = { - "%s%d_average", - "%s%d_average_lowest", - "%s%d_average_highest" -}; -static char const * const temp_sensor_name_templates[] = { - "%s%d_input", - "%s%d_input_lowest", - "%s%d_input_highest" +static const char * const sensor_name_suffixes[] = { + "", + "_lowest", + "_highest" }; static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); @@ -355,9 +350,11 @@ static int create_sensor(struct ibmpex_bmc_data *data, int type, return -ENOMEM; if (type == TEMP_SENSOR) - sprintf(n, temp_sensor_name_templates[func], "temp", counter); + sprintf(n, "temp%d_input%s", + counter, sensor_name_suffixes[func]); else if (type == POWER_SENSOR) - sprintf(n, power_sensor_name_templates[func], "power", counter); + sprintf(n, "power%d_average%s", + counter, sensor_name_suffixes[func]); sysfs_attr_init(&data->sensors[sensor].attr[func].dev_attr.attr); data->sensors[sensor].attr[func].dev_attr.attr.name = n; diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index febe817..4255514 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -30,8 +30,11 @@ #include <linux/platform_device.h> #include <asm/opal.h> #include <linux/err.h> +#include <asm/cputhreads.h> +#include <asm/smp.h> #define MAX_ATTR_LEN 32 +#define MAX_LABEL_LEN 64 /* Sensor suffix name from DT */ #define DT_FAULT_ATTR_SUFFIX "faulted" @@ -44,17 +47,20 @@ */ enum sensors { FAN, - AMBIENT_TEMP, + TEMP, POWER_SUPPLY, POWER_INPUT, MAX_SENSOR_TYPE, }; +#define INVALID_INDEX (-1U) + static struct sensor_group { const char *name; const char *compatible; struct attribute_group group; u32 attr_count; + u32 hwmon_index; } sensor_groups[] = { {"fan", "ibm,opal-sensor-cooling-fan"}, {"temp", "ibm,opal-sensor-amb-temp"}, @@ -64,7 +70,10 @@ static struct sensor_group { struct sensor_data { u32 id; /* An opaque id of the firmware for each sensor */ + u32 hwmon_index; + u32 opal_index; enum sensors type; + char label[MAX_LABEL_LEN]; char name[MAX_ATTR_LEN]; struct device_attribute dev_attr; }; @@ -87,7 +96,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, return ret; /* Convert temperature to milli-degrees */ - if (sdata->type == AMBIENT_TEMP) + if (sdata->type == TEMP) x *= 1000; /* Convert power to micro-watts */ else if (sdata->type == POWER_INPUT) @@ -96,8 +105,65 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, return sprintf(buf, "%u\n", x); } -static int get_sensor_index_attr(const char *name, u32 *index, - char *attr) +static ssize_t show_label(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_data *sdata = container_of(devattr, struct sensor_data, + dev_attr); + + return sprintf(buf, "%s\n", sdata->label); +} + +static int __init get_logical_cpu(int hwcpu) +{ + int cpu; + + for_each_possible_cpu(cpu) + if (get_hard_smp_processor_id(cpu) == hwcpu) + return cpu; + + return -ENOENT; +} + +static void __init make_sensor_label(struct device_node *np, + struct sensor_data *sdata, + const char *label) +{ + u32 id; + size_t n; + + n = snprintf(sdata->label, sizeof(sdata->label), "%s", label); + + /* + * Core temp pretty print + */ + if (!of_property_read_u32(np, "ibm,pir", &id)) { + int cpuid = get_logical_cpu(id); + + if (cpuid >= 0) + /* + * The digital thermal sensors are associated + * with a core. Let's print out the range of + * cpu ids corresponding to the hardware + * threads of the core. + */ + n += snprintf(sdata->label + n, + sizeof(sdata->label) - n, " %d-%d", + cpuid, cpuid + threads_per_core - 1); + else + n += snprintf(sdata->label + n, + sizeof(sdata->label) - n, " phy%d", id); + } + + /* + * Membuffer pretty print + */ + if (!of_property_read_u32(np, "ibm,chip-id", &id)) + n += snprintf(sdata->label + n, sizeof(sdata->label) - n, + " %d", id & 0xffff); +} + +static int get_sensor_index_attr(const char *name, u32 *index, char *attr) { char *hash_pos = strchr(name, '#'); char buf[8] = { 0 }; @@ -127,46 +193,90 @@ static int get_sensor_index_attr(const char *name, u32 *index, return 0; } +static const char *convert_opal_attr_name(enum sensors type, + const char *opal_attr) +{ + const char *attr_name = NULL; + + if (!strcmp(opal_attr, DT_FAULT_ATTR_SUFFIX)) { + attr_name = "fault"; + } else if (!strcmp(opal_attr, DT_DATA_ATTR_SUFFIX)) { + attr_name = "input"; + } else if (!strcmp(opal_attr, DT_THRESHOLD_ATTR_SUFFIX)) { + if (type == TEMP) + attr_name = "max"; + else if (type == FAN) + attr_name = "min"; + } + + return attr_name; +} + /* * This function translates the DT node name into the 'hwmon' attribute name. * IBMPOWERNV device node appear like cooling-fan#2-data, amb-temp#1-thrs etc. * which need to be mapped as fan2_input, temp1_max respectively before * populating them inside hwmon device class. */ -static int create_hwmon_attr_name(struct device *dev, enum sensors type, - const char *node_name, - char *hwmon_attr_name) +static const char *parse_opal_node_name(const char *node_name, + enum sensors type, u32 *index) { char attr_suffix[MAX_ATTR_LEN]; - char *attr_name; - u32 index; + const char *attr_name; int err; - err = get_sensor_index_attr(node_name, &index, attr_suffix); - if (err) { - dev_err(dev, "Sensor device node name '%s' is invalid\n", - node_name); - return err; - } + err = get_sensor_index_attr(node_name, index, attr_suffix); + if (err) + return ERR_PTR(err); - if (!strcmp(attr_suffix, DT_FAULT_ATTR_SUFFIX)) { - attr_name = "fault"; - } else if (!strcmp(attr_suffix, DT_DATA_ATTR_SUFFIX)) { - attr_name = "input"; - } else if (!strcmp(attr_suffix, DT_THRESHOLD_ATTR_SUFFIX)) { - if (type == AMBIENT_TEMP) - attr_name = "max"; - else if (type == FAN) - attr_name = "min"; - else - return -ENOENT; - } else { - return -ENOENT; + attr_name = convert_opal_attr_name(type, attr_suffix); + if (!attr_name) + return ERR_PTR(-ENOENT); + + return attr_name; +} + +static int get_sensor_type(struct device_node *np) +{ + enum sensors type; + const char *str; + + for (type = 0; type < MAX_SENSOR_TYPE; type++) { + if (of_device_is_compatible(np, sensor_groups[type].compatible)) + return type; } - snprintf(hwmon_attr_name, MAX_ATTR_LEN, "%s%d_%s", - sensor_groups[type].name, index, attr_name); - return 0; + /* + * Let's check if we have a newer device tree + */ + if (!of_device_is_compatible(np, "ibm,opal-sensor")) + return MAX_SENSOR_TYPE; + + if (of_property_read_string(np, "sensor-type", &str)) + return MAX_SENSOR_TYPE; + + for (type = 0; type < MAX_SENSOR_TYPE; type++) + if (!strcmp(str, sensor_groups[type].name)) + return type; + + return MAX_SENSOR_TYPE; +} + +static u32 get_sensor_hwmon_index(struct sensor_data *sdata, + struct sensor_data *sdata_table, int count) +{ + int i; + + /* + * We don't use the OPAL index on newer device trees + */ + if (sdata->opal_index != INVALID_INDEX) { + for (i = 0; i < count; i++) + if (sdata_table[i].opal_index == sdata->opal_index && + sdata_table[i].type == sdata->type) + return sdata_table[i].hwmon_index; + } + return ++sensor_groups[sdata->type].hwmon_index; } static int populate_attr_groups(struct platform_device *pdev) @@ -178,15 +288,22 @@ static int populate_attr_groups(struct platform_device *pdev) opal = of_find_node_by_path("/ibm,opal/sensors"); for_each_child_of_node(opal, np) { + const char *label; + if (np->name == NULL) continue; - for (type = 0; type < MAX_SENSOR_TYPE; type++) - if (of_device_is_compatible(np, - sensor_groups[type].compatible)) { - sensor_groups[type].attr_count++; - break; - } + type = get_sensor_type(np); + if (type == MAX_SENSOR_TYPE) + continue; + + sensor_groups[type].attr_count++; + + /* + * add a new attribute for labels + */ + if (!of_property_read_string(np, "label", &label)) + sensor_groups[type].attr_count++; } of_node_put(opal); @@ -207,6 +324,21 @@ static int populate_attr_groups(struct platform_device *pdev) return 0; } +static void create_hwmon_attr(struct sensor_data *sdata, const char *attr_name, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf)) +{ + snprintf(sdata->name, MAX_ATTR_LEN, "%s%d_%s", + sensor_groups[sdata->type].name, sdata->hwmon_index, + attr_name); + + sysfs_attr_init(&sdata->dev_attr.attr); + sdata->dev_attr.attr.name = sdata->name; + sdata->dev_attr.attr.mode = S_IRUGO; + sdata->dev_attr.show = show; +} + /* * Iterate through the device tree for each child of 'sensors' node, create * a sysfs attribute file, the file is named by translating the DT node name @@ -233,18 +365,23 @@ static int create_device_attrs(struct platform_device *pdev) } for_each_child_of_node(opal, np) { + const char *attr_name; + u32 opal_index; + const char *label; + if (np->name == NULL) continue; - for (type = 0; type < MAX_SENSOR_TYPE; type++) - if (of_device_is_compatible(np, - sensor_groups[type].compatible)) - break; - + type = get_sensor_type(np); if (type == MAX_SENSOR_TYPE) continue; - if (of_property_read_u32(np, "sensor-id", &sensor_id)) { + /* + * Newer device trees use a "sensor-data" property + * name for input. + */ + if (of_property_read_u32(np, "sensor-id", &sensor_id) && + of_property_read_u32(np, "sensor-data", &sensor_id)) { dev_info(&pdev->dev, "'sensor-id' missing in the node '%s'\n", np->name); @@ -253,18 +390,46 @@ static int create_device_attrs(struct platform_device *pdev) sdata[count].id = sensor_id; sdata[count].type = type; - err = create_hwmon_attr_name(&pdev->dev, type, np->name, - sdata[count].name); - if (err) - goto exit_put_node; - sysfs_attr_init(&sdata[count].dev_attr.attr); - sdata[count].dev_attr.attr.name = sdata[count].name; - sdata[count].dev_attr.attr.mode = S_IRUGO; - sdata[count].dev_attr.show = show_sensor; + /* + * If we can not parse the node name, it means we are + * running on a newer device tree. We can just forget + * about the OPAL index and use a defaut value for the + * hwmon attribute name + */ + attr_name = parse_opal_node_name(np->name, type, &opal_index); + if (IS_ERR(attr_name)) { + attr_name = "input"; + opal_index = INVALID_INDEX; + } + + sdata[count].opal_index = opal_index; + sdata[count].hwmon_index = + get_sensor_hwmon_index(&sdata[count], sdata, count); + + create_hwmon_attr(&sdata[count], attr_name, show_sensor); pgroups[type]->attrs[sensor_groups[type].attr_count++] = &sdata[count++].dev_attr.attr; + + if (!of_property_read_string(np, "label", &label)) { + /* + * For the label attribute, we can reuse the + * "properties" of the previous "input" + * attribute. They are related to the same + * sensor. + */ + sdata[count].type = type; + sdata[count].opal_index = sdata[count - 1].opal_index; + sdata[count].hwmon_index = sdata[count - 1].hwmon_index; + + make_sensor_label(np, &sdata[count], label); + + create_hwmon_attr(&sdata[count], "label", show_label); + + pgroups[type]->attrs[sensor_groups[type].attr_count++] = + &sdata[count++].dev_attr.attr; + } } exit_put_node: diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 409116c..d0ee556 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -11,6 +11,7 @@ * similar parts. The other devices are supported by different drivers. * * Supports: IT8603E Super I/O chip w/LPC interface + * IT8620E Super I/O chip w/LPC interface * IT8623E Super I/O chip w/LPC interface * IT8705F Super I/O chip w/LPC interface * IT8712F Super I/O chip w/LPC interface @@ -23,8 +24,11 @@ * IT8758E Super I/O chip w/LPC interface * IT8771E Super I/O chip w/LPC interface * IT8772E Super I/O chip w/LPC interface + * IT8781F Super I/O chip w/LPC interface * IT8782F Super I/O chip w/LPC interface * IT8783E/F Super I/O chip w/LPC interface + * IT8786E Super I/O chip w/LPC interface + * IT8790E Super I/O chip w/LPC interface * Sis950 A clone of the IT8705F * * Copyright (C) 2001 Chris Gauthron @@ -66,7 +70,7 @@ #define DRVNAME "it87" enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8771, - it8772, it8782, it8783, it8603 }; + it8772, it8781, it8782, it8783, it8786, it8790, it8603, it8620 }; static unsigned short force_id; module_param(force_id, ushort, 0); @@ -146,15 +150,20 @@ static inline void superio_exit(void) #define IT8728F_DEVID 0x8728 #define IT8771E_DEVID 0x8771 #define IT8772E_DEVID 0x8772 +#define IT8781F_DEVID 0x8781 #define IT8782F_DEVID 0x8782 #define IT8783E_DEVID 0x8783 +#define IT8786E_DEVID 0x8786 +#define IT8790E_DEVID 0x8790 #define IT8603E_DEVID 0x8603 +#define IT8620E_DEVID 0x8620 #define IT8623E_DEVID 0x8623 #define IT87_ACT_REG 0x30 #define IT87_BASE_REG 0x60 /* Logical device 7 registers (IT8712F and later) */ #define IT87_SIO_GPIO1_REG 0x25 +#define IT87_SIO_GPIO2_REG 0x26 #define IT87_SIO_GPIO3_REG 0x27 #define IT87_SIO_GPIO5_REG 0x29 #define IT87_SIO_PINX1_REG 0x2a /* Pin selection */ @@ -207,11 +216,11 @@ static bool fix_pwm_polarity; /* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */ -static const u8 IT87_REG_FAN[] = { 0x0d, 0x0e, 0x0f, 0x80, 0x82 }; -static const u8 IT87_REG_FAN_MIN[] = { 0x10, 0x11, 0x12, 0x84, 0x86 }; -static const u8 IT87_REG_FANX[] = { 0x18, 0x19, 0x1a, 0x81, 0x83 }; -static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 }; -static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 }; +static const u8 IT87_REG_FAN[] = { 0x0d, 0x0e, 0x0f, 0x80, 0x82, 0x4c }; +static const u8 IT87_REG_FAN_MIN[] = { 0x10, 0x11, 0x12, 0x84, 0x86, 0x4e }; +static const u8 IT87_REG_FANX[] = { 0x18, 0x19, 0x1a, 0x81, 0x83, 0x4d }; +static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87, 0x4f }; +static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 }; #define IT87_REG_FAN_MAIN_CTRL 0x13 #define IT87_REG_FAN_CTL 0x14 @@ -238,6 +247,7 @@ static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 }; struct it87_devices { const char *name; + const char * const suffix; u16 features; u8 peci_mask; u8 old_peci_mask; @@ -250,79 +260,131 @@ struct it87_devices { #define FEAT_TEMP_OFFSET (1 << 4) #define FEAT_TEMP_PECI (1 << 5) #define FEAT_TEMP_OLD_PECI (1 << 6) +#define FEAT_FAN16_CONFIG (1 << 7) /* Need to enable 16-bit fans */ +#define FEAT_FIVE_FANS (1 << 8) /* Supports five fans */ +#define FEAT_VID (1 << 9) /* Set if chip supports VID */ +#define FEAT_IN7_INTERNAL (1 << 10) /* Set if in7 is internal */ +#define FEAT_SIX_FANS (1 << 11) /* Supports six fans */ static const struct it87_devices it87_devices[] = { [it87] = { .name = "it87", + .suffix = "F", .features = FEAT_OLD_AUTOPWM, /* may need to overwrite */ }, [it8712] = { .name = "it8712", - .features = FEAT_OLD_AUTOPWM, /* may need to overwrite */ + .suffix = "F", + .features = FEAT_OLD_AUTOPWM | FEAT_VID, + /* may need to overwrite */ }, [it8716] = { .name = "it8716", - .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET, + .suffix = "F", + .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID + | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS, }, [it8718] = { .name = "it8718", - .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET - | FEAT_TEMP_OLD_PECI, + .suffix = "F", + .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID + | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS, .old_peci_mask = 0x4, }, [it8720] = { .name = "it8720", - .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET - | FEAT_TEMP_OLD_PECI, + .suffix = "F", + .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID + | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS, .old_peci_mask = 0x4, }, [it8721] = { .name = "it8721", + .suffix = "F", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS - | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI, + | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI + | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_IN7_INTERNAL, .peci_mask = 0x05, .old_peci_mask = 0x02, /* Actually reports PCH */ }, [it8728] = { .name = "it8728", + .suffix = "F", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS - | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI, + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS + | FEAT_IN7_INTERNAL, .peci_mask = 0x07, }, [it8771] = { .name = "it8771", + .suffix = "E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS - | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI, - /* PECI: guesswork */ - /* 12mV ADC (OHM) */ - /* 16 bit fans (OHM) */ + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL, + /* PECI: guesswork */ + /* 12mV ADC (OHM) */ + /* 16 bit fans (OHM) */ + /* three fans, always 16 bit (guesswork) */ .peci_mask = 0x07, }, [it8772] = { .name = "it8772", + .suffix = "E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS - | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI, - /* PECI (coreboot) */ - /* 12mV ADC (HWSensors4, OHM) */ - /* 16 bit fans (HWSensors4, OHM) */ + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL, + /* PECI (coreboot) */ + /* 12mV ADC (HWSensors4, OHM) */ + /* 16 bit fans (HWSensors4, OHM) */ + /* three fans, always 16 bit (datasheet) */ .peci_mask = 0x07, }, + [it8781] = { + .name = "it8781", + .suffix = "F", + .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET + | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG, + .old_peci_mask = 0x4, + }, [it8782] = { .name = "it8782", + .suffix = "F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET - | FEAT_TEMP_OLD_PECI, + | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG, .old_peci_mask = 0x4, }, [it8783] = { .name = "it8783", + .suffix = "E/F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET - | FEAT_TEMP_OLD_PECI, + | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG, .old_peci_mask = 0x4, }, + [it8786] = { + .name = "it8786", + .suffix = "E", + .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL, + .peci_mask = 0x07, + }, + [it8790] = { + .name = "it8790", + .suffix = "E", + .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL, + .peci_mask = 0x07, + }, [it8603] = { .name = "it8603", + .suffix = "E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS - | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI, + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL, + .peci_mask = 0x07, + }, + [it8620] = { + .name = "it8620", + .suffix = "E", + .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS + | FEAT_IN7_INTERNAL, .peci_mask = 0x07, }, }; @@ -337,6 +399,12 @@ static const struct it87_devices it87_devices[] = { #define has_temp_old_peci(data, nr) \ (((data)->features & FEAT_TEMP_OLD_PECI) && \ ((data)->old_peci_mask & (1 << nr))) +#define has_fan16_config(data) ((data)->features & FEAT_FAN16_CONFIG) +#define has_five_fans(data) ((data)->features & (FEAT_FIVE_FANS | \ + FEAT_SIX_FANS)) +#define has_vid(data) ((data)->features & FEAT_VID) +#define has_in7_internal(data) ((data)->features & FEAT_IN7_INTERNAL) +#define has_six_fans(data) ((data)->features & FEAT_SIX_FANS) struct it87_sio_data { enum chips type; @@ -373,7 +441,7 @@ struct it87_data { u16 in_scaled; /* Internal voltage sensors are scaled */ u8 in[10][3]; /* [nr][0]=in, [1]=min, [2]=max */ u8 has_fan; /* Bitfield, fans enabled */ - u16 fan[5][2]; /* Register values, [nr][0]=fan, [1]=min */ + u16 fan[6][2]; /* Register values, [nr][0]=fan, [1]=min */ u8 has_temp; /* Bitfield, temp sensors enabled */ s8 temp[3][4]; /* [nr][0]=temp, [1]=min, [2]=max, [3]=offset */ u8 sensor; /* Register value (IT87_REG_TEMP_ENABLE) */ @@ -475,15 +543,25 @@ static int DIV_TO_REG(int val) } #define DIV_FROM_REG(val) (1 << (val)) +/* + * PWM base frequencies. The frequency has to be divided by either 128 or 256, + * depending on the chip type, to calculate the actual PWM frequency. + * + * Some of the chip datasheets suggest a base frequency of 51 kHz instead + * of 750 kHz for the slowest base frequency, resulting in a PWM frequency + * of 200 Hz. Sometimes both PWM frequency select registers are affected, + * sometimes just one. It is unknown if this is a datasheet error or real, + * so this is ignored for now. + */ static const unsigned int pwm_freq[8] = { - 48000000 / 128, - 24000000 / 128, - 12000000 / 128, - 8000000 / 128, - 6000000 / 128, - 3000000 / 128, - 1500000 / 128, - 750000 / 128, + 48000000, + 24000000, + 12000000, + 8000000, + 6000000, + 3000000, + 1500000, + 750000, }; static int it87_probe(struct platform_device *pdev); @@ -801,8 +879,11 @@ static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr, { struct it87_data *data = it87_update_device(dev); int index = (data->fan_ctl >> 4) & 0x07; + unsigned int freq; - return sprintf(buf, "%u\n", pwm_freq[index]); + freq = pwm_freq[index] / (has_newer_autopwm(data) ? 256 : 128); + + return sprintf(buf, "%u\n", freq); } static ssize_t set_fan(struct device *dev, struct device_attribute *attr, @@ -1024,6 +1105,9 @@ static ssize_t set_pwm_freq(struct device *dev, if (kstrtoul(buf, 10, &val) < 0) return -EINVAL; + val = clamp_val(val, 0, 1000000); + val *= has_newer_autopwm(data) ? 256 : 128; + /* Search for the nearest available frequency */ for (i = 0; i < 7; i++) { if (val > (pwm_freq[i] + pwm_freq[i+1]) / 2) @@ -1196,6 +1280,10 @@ static SENSOR_DEVICE_ATTR_2(fan5_input, S_IRUGO, show_fan, NULL, 4, 0); static SENSOR_DEVICE_ATTR_2(fan5_min, S_IRUGO | S_IWUSR, show_fan, set_fan, 4, 1); +static SENSOR_DEVICE_ATTR_2(fan6_input, S_IRUGO, show_fan, NULL, 5, 0); +static SENSOR_DEVICE_ATTR_2(fan6_min, S_IRUGO | S_IWUSR, show_fan, set_fan, + 5, 1); + static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 0); static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 0); @@ -1326,6 +1414,7 @@ static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 1); static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 2); static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 3); static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_alarm, NULL, 7); static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 16); static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 17); static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 18); @@ -1376,6 +1465,7 @@ static SENSOR_DEVICE_ATTR(fan2_beep, S_IRUGO, show_beep, set_beep, 0); static SENSOR_DEVICE_ATTR(fan3_beep, S_IRUGO, show_beep, set_beep, 0); static SENSOR_DEVICE_ATTR(fan4_beep, S_IRUGO, show_beep, set_beep, 0); static SENSOR_DEVICE_ATTR(fan5_beep, S_IRUGO, show_beep, set_beep, 0); +static SENSOR_DEVICE_ATTR(fan6_beep, S_IRUGO, show_beep, set_beep, 0); static SENSOR_DEVICE_ATTR(temp1_beep, S_IRUGO | S_IWUSR, show_beep, set_beep, 2); static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO, show_beep, NULL, 2); @@ -1579,7 +1669,7 @@ static struct attribute *it87_attributes_temp_beep[] = { &sensor_dev_attr_temp3_beep.dev_attr.attr, }; -static struct attribute *it87_attributes_fan[5][3+1] = { { +static struct attribute *it87_attributes_fan[6][3+1] = { { &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, &sensor_dev_attr_fan1_alarm.dev_attr.attr, @@ -1604,14 +1694,20 @@ static struct attribute *it87_attributes_fan[5][3+1] = { { &sensor_dev_attr_fan5_min.dev_attr.attr, &sensor_dev_attr_fan5_alarm.dev_attr.attr, NULL +}, { + &sensor_dev_attr_fan6_input.dev_attr.attr, + &sensor_dev_attr_fan6_min.dev_attr.attr, + &sensor_dev_attr_fan6_alarm.dev_attr.attr, + NULL } }; -static const struct attribute_group it87_group_fan[5] = { +static const struct attribute_group it87_group_fan[6] = { { .attrs = it87_attributes_fan[0] }, { .attrs = it87_attributes_fan[1] }, { .attrs = it87_attributes_fan[2] }, { .attrs = it87_attributes_fan[3] }, { .attrs = it87_attributes_fan[4] }, + { .attrs = it87_attributes_fan[5] }, }; static const struct attribute *it87_attributes_fan_div[] = { @@ -1693,6 +1789,7 @@ static struct attribute *it87_attributes_fan_beep[] = { &sensor_dev_attr_fan3_beep.dev_attr.attr, &sensor_dev_attr_fan4_beep.dev_attr.attr, &sensor_dev_attr_fan5_beep.dev_attr.attr, + &sensor_dev_attr_fan6_beep.dev_attr.attr, }; static struct attribute *it87_attributes_vid[] = { @@ -1724,6 +1821,7 @@ static int __init it87_find(unsigned short *address, int err; u16 chip_type; const char *board_vendor, *board_name; + const struct it87_devices *config; err = superio_enter(); if (err) @@ -1761,16 +1859,28 @@ static int __init it87_find(unsigned short *address, case IT8772E_DEVID: sio_data->type = it8772; break; + case IT8781F_DEVID: + sio_data->type = it8781; + break; case IT8782F_DEVID: sio_data->type = it8782; break; case IT8783E_DEVID: sio_data->type = it8783; break; + case IT8786E_DEVID: + sio_data->type = it8786; + break; + case IT8790E_DEVID: + sio_data->type = it8790; + break; case IT8603E_DEVID: case IT8623E_DEVID: sio_data->type = it8603; break; + case IT8620E_DEVID: + sio_data->type = it8620; + break; case 0xffff: /* No device at all */ goto exit; default: @@ -1792,30 +1902,34 @@ static int __init it87_find(unsigned short *address, err = 0; sio_data->revision = superio_inb(DEVREV) & 0x0f; - pr_info("Found IT%04x%c chip at 0x%x, revision %d\n", chip_type, - chip_type == 0x8771 || chip_type == 0x8772 || - chip_type == 0x8603 ? 'E' : 'F', *address, - sio_data->revision); + pr_info("Found IT%04x%s chip at 0x%x, revision %d\n", chip_type, + it87_devices[sio_data->type].suffix, + *address, sio_data->revision); + + config = &it87_devices[sio_data->type]; + + /* in7 (VSB or VCCH5V) is always internal on some chips */ + if (has_in7_internal(config)) + sio_data->internal |= (1 << 1); /* in8 (Vbat) is always internal */ - sio_data->internal = (1 << 2); + sio_data->internal |= (1 << 2); + /* Only the IT8603E has in9 */ if (sio_data->type != it8603) sio_data->skip_in |= (1 << 9); - /* Read GPIO config and VID value from LDN 7 (GPIO) */ - if (sio_data->type == it87) { - /* The IT8705F doesn't have VID pins at all */ + if (!has_vid(config)) sio_data->skip_vid = 1; + /* Read GPIO config and VID value from LDN 7 (GPIO) */ + if (sio_data->type == it87) { /* The IT8705F has a different LD number for GPIO */ superio_select(5); sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; } else if (sio_data->type == it8783) { int reg25, reg27, reg2a, reg2c, regef; - sio_data->skip_vid = 1; /* No VID */ - superio_select(GPIO); reg25 = superio_inb(IT87_SIO_GPIO1_REG); @@ -1881,7 +1995,6 @@ static int __init it87_find(unsigned short *address, } else if (sio_data->type == it8603) { int reg27, reg29; - sio_data->skip_vid = 1; /* No VID */ superio_select(GPIO); reg27 = superio_inb(IT87_SIO_GPIO3_REG); @@ -1902,14 +2015,36 @@ static int __init it87_find(unsigned short *address, sio_data->skip_in |= (1 << 5); /* No VIN5 */ sio_data->skip_in |= (1 << 6); /* No VIN6 */ - /* no fan4 */ - sio_data->skip_pwm |= (1 << 3); - sio_data->skip_fan |= (1 << 3); - - sio_data->internal |= (1 << 1); /* in7 is VSB */ sio_data->internal |= (1 << 3); /* in9 is AVCC */ sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; + } else if (sio_data->type == it8620) { + int reg; + + superio_select(GPIO); + + /* Check for fan4, fan5 */ + reg = superio_inb(IT87_SIO_GPIO2_REG); + if (!(reg & (1 << 5))) + sio_data->skip_fan |= (1 << 3); + if (!(reg & (1 << 4))) + sio_data->skip_fan |= (1 << 4); + + /* Check for pwm3, fan3 */ + reg = superio_inb(IT87_SIO_GPIO3_REG); + if (reg & (1 << 6)) + sio_data->skip_pwm |= (1 << 2); + if (reg & (1 << 7)) + sio_data->skip_fan |= (1 << 2); + + /* Check for pwm2, fan2 */ + reg = superio_inb(IT87_SIO_GPIO5_REG); + if (reg & (1 << 1)) + sio_data->skip_pwm |= (1 << 1); + if (reg & (1 << 2)) + sio_data->skip_fan |= (1 << 1); + + sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; } else { int reg; bool uart6; @@ -1917,15 +2052,7 @@ static int __init it87_find(unsigned short *address, superio_select(GPIO); reg = superio_inb(IT87_SIO_GPIO3_REG); - if (sio_data->type == it8721 || sio_data->type == it8728 || - sio_data->type == it8771 || sio_data->type == it8772 || - sio_data->type == it8782) { - /* - * IT8721F/IT8758E, and IT8782F don't have VID pins - * at all, not sure about the IT8728F and compatibles. - */ - sio_data->skip_vid = 1; - } else { + if (!sio_data->skip_vid) { /* We need at least 4 VID pins */ if (reg & 0x0f) { pr_info("VID is disabled (pins used for GPIO)\n"); @@ -1975,10 +2102,7 @@ static int __init it87_find(unsigned short *address, } if (reg & (1 << 0)) sio_data->internal |= (1 << 0); - if ((reg & (1 << 1)) || sio_data->type == it8721 || - sio_data->type == it8728 || - sio_data->type == it8771 || - sio_data->type == it8772) + if (reg & (1 << 1)) sio_data->internal |= (1 << 1); /* @@ -2050,7 +2174,7 @@ static void it87_remove_files(struct device *dev) sysfs_remove_file(&dev->kobj, it87_attributes_temp_beep[i]); } - for (i = 0; i < 5; i++) { + for (i = 0; i < 6; i++) { if (!(data->has_fan & (1 << i))) continue; sysfs_remove_group(&dev->kobj, &it87_group_fan[i]); @@ -2062,7 +2186,7 @@ static void it87_remove_files(struct device *dev) it87_attributes_fan_div[i]); } for (i = 0; i < 3; i++) { - if (sio_data->skip_pwm & (1 << 0)) + if (sio_data->skip_pwm & (1 << i)) continue; sysfs_remove_group(&dev->kobj, &it87_group_pwm[i]); if (has_old_autopwm(data)) @@ -2112,13 +2236,14 @@ static int it87_probe(struct platform_device *pdev) case it87: if (sio_data->revision >= 0x03) { data->features &= ~FEAT_OLD_AUTOPWM; - data->features |= FEAT_16BIT_FANS; + data->features |= FEAT_FAN16_CONFIG | FEAT_16BIT_FANS; } break; case it8712: if (sio_data->revision >= 0x08) { data->features &= ~FEAT_OLD_AUTOPWM; - data->features |= FEAT_16BIT_FANS; + data->features |= FEAT_FAN16_CONFIG | FEAT_16BIT_FANS | + FEAT_FIVE_FANS; } break; default: @@ -2147,7 +2272,8 @@ static int it87_probe(struct platform_device *pdev) data->in_scaled |= (1 << 8); /* in8 is Vbat */ if (sio_data->internal & (1 << 3)) data->in_scaled |= (1 << 9); /* in9 is AVCC */ - } else if (sio_data->type == it8782 || sio_data->type == it8783) { + } else if (sio_data->type == it8781 || sio_data->type == it8782 || + sio_data->type == it8783) { if (sio_data->internal & (1 << 0)) data->in_scaled |= (1 << 3); /* in3 is VCC5V */ if (sio_data->internal & (1 << 1)) @@ -2205,7 +2331,7 @@ static int it87_probe(struct platform_device *pdev) /* Do not create fan files for disabled fans */ fan_beep_need_rw = 1; - for (i = 0; i < 5; i++) { + for (i = 0; i < 6; i++) { if (!(data->has_fan & (1 << i))) continue; err = sysfs_create_group(&dev->kobj, &it87_group_fan[i]); @@ -2450,24 +2576,26 @@ static void it87_init_device(struct platform_device *pdev) } data->has_fan = (data->fan_main_ctrl >> 4) & 0x07; - /* Set tachometers to 16-bit mode if needed, IT8603E (and IT8728F?) - * has it by default */ - if (has_16bit_fans(data) && data->type != it8603) { - tmp = it87_read_value(data, IT87_REG_FAN_16BIT); + tmp = it87_read_value(data, IT87_REG_FAN_16BIT); + + /* Set tachometers to 16-bit mode if needed */ + if (has_fan16_config(data)) { if (~tmp & 0x07 & data->has_fan) { dev_dbg(&pdev->dev, "Setting fan1-3 to 16-bit mode\n"); it87_write_value(data, IT87_REG_FAN_16BIT, tmp | 0x07); } - /* IT8705F, IT8782F, and IT8783E/F only support three fans. */ - if (data->type != it87 && data->type != it8782 && - data->type != it8783) { - if (tmp & (1 << 4)) - data->has_fan |= (1 << 3); /* fan4 enabled */ - if (tmp & (1 << 5)) - data->has_fan |= (1 << 4); /* fan5 enabled */ - } + } + + /* Check for additional fans */ + if (has_five_fans(data)) { + if (tmp & (1 << 4)) + data->has_fan |= (1 << 3); /* fan4 enabled */ + if (tmp & (1 << 5)) + data->has_fan |= (1 << 4); /* fan5 enabled */ + if (has_six_fans(data) && (tmp & (1 << 2))) + data->has_fan |= (1 << 5); /* fan6 enabled */ } /* Fan input pins may be used for alternative functions */ @@ -2535,7 +2663,7 @@ static struct it87_data *it87_update_device(struct device *dev) if (data->type == it8603) data->in[9][0] = it87_read_value(data, 0x2f); - for (i = 0; i < 5; i++) { + for (i = 0; i < 6; i++) { /* Skip disabled fans */ if (!(data->has_fan & (1 << i))) continue; diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 996bdfd..9887d32 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -87,11 +87,14 @@ static const unsigned short normal_i2c[] = { #define AT30TSE004_DEVID_MASK 0xffff /* IDT */ -#define TS3000B3_DEVID 0x2903 /* Also matches TSE2002B3 */ -#define TS3000B3_DEVID_MASK 0xffff +#define TSE2004_DEVID 0x2200 +#define TSE2004_DEVID_MASK 0xff00 -#define TS3000GB2_DEVID 0x2912 /* Also matches TSE2002GB2 */ -#define TS3000GB2_DEVID_MASK 0xffff +#define TS3000_DEVID 0x2900 /* Also matches TSE2002 */ +#define TS3000_DEVID_MASK 0xff00 + +#define TS3001_DEVID 0x3000 +#define TS3001_DEVID_MASK 0xff00 /* Maxim */ #define MAX6604_DEVID 0x3e00 @@ -152,8 +155,9 @@ static struct jc42_chips jc42_chips[] = { { ADT_MANID, ADT7408_DEVID, ADT7408_DEVID_MASK }, { ATMEL_MANID, AT30TS00_DEVID, AT30TS00_DEVID_MASK }, { ATMEL_MANID2, AT30TSE004_DEVID, AT30TSE004_DEVID_MASK }, - { IDT_MANID, TS3000B3_DEVID, TS3000B3_DEVID_MASK }, - { IDT_MANID, TS3000GB2_DEVID, TS3000GB2_DEVID_MASK }, + { IDT_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK }, + { IDT_MANID, TS3000_DEVID, TS3000_DEVID_MASK }, + { IDT_MANID, TS3001_DEVID, TS3001_DEVID_MASK }, { MAX_MANID, MAX6604_DEVID, MAX6604_DEVID_MASK }, { MCP_MANID, MCP9804_DEVID, MCP9804_DEVID_MASK }, { MCP_MANID, MCP98242_DEVID, MCP98242_DEVID_MASK }, diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 1be4117..4fcb481 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -57,6 +57,7 @@ #include <linux/err.h> #include <linux/mutex.h> #include <linux/acpi.h> +#include <linux/dmi.h> #include <linux/io.h> #include "lm75.h" @@ -880,12 +881,12 @@ struct nct6775_data { u16 have_temp; u16 have_temp_fixed; u16 have_in; -#ifdef CONFIG_PM + /* Remember extra register values over suspend/resume */ u8 vbat; u8 fandiv1; u8 fandiv2; -#endif + u8 sio_reg_enable; }; struct nct6775_sio_data { @@ -3178,6 +3179,10 @@ nct6775_check_fan_inputs(struct nct6775_data *data) int sioreg = data->sioreg; int regval; + /* Store SIO_REG_ENABLE for use during resume */ + superio_select(sioreg, NCT6775_LD_HWM); + data->sio_reg_enable = superio_inb(sioreg, SIO_REG_ENABLE); + /* fan4 and fan5 share some pins with the GPIO and serial flash */ if (data->kind == nct6775) { regval = superio_inb(sioreg, 0x2c); @@ -3195,21 +3200,38 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm6pin = false; } else if (data->kind == nct6776) { bool gpok = superio_inb(sioreg, 0x27) & 0x80; + const char *board_vendor, *board_name; - superio_select(sioreg, NCT6775_LD_HWM); - regval = superio_inb(sioreg, SIO_REG_ENABLE); + board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); + board_name = dmi_get_system_info(DMI_BOARD_NAME); + + if (board_name && board_vendor && + !strcmp(board_vendor, "ASRock")) { + /* + * Auxiliary fan monitoring is not enabled on ASRock + * Z77 Pro4-M if booted in UEFI Ultra-FastBoot mode. + * Observed with BIOS version 2.00. + */ + if (!strcmp(board_name, "Z77 Pro4-M")) { + if ((data->sio_reg_enable & 0xe0) != 0xe0) { + data->sio_reg_enable |= 0xe0; + superio_outb(sioreg, SIO_REG_ENABLE, + data->sio_reg_enable); + } + } + } - if (regval & 0x80) + if (data->sio_reg_enable & 0x80) fan3pin = gpok; else fan3pin = !(superio_inb(sioreg, 0x24) & 0x40); - if (regval & 0x40) + if (data->sio_reg_enable & 0x40) fan4pin = gpok; else fan4pin = superio_inb(sioreg, 0x1C) & 0x01; - if (regval & 0x20) + if (data->sio_reg_enable & 0x20) fan5pin = gpok; else fan5pin = superio_inb(sioreg, 0x1C) & 0x02; @@ -3989,8 +4011,7 @@ static void nct6791_enable_io_mapping(int sioaddr) } } -#ifdef CONFIG_PM -static int nct6775_suspend(struct device *dev) +static int __maybe_unused nct6775_suspend(struct device *dev) { struct nct6775_data *data = nct6775_update_device(dev); @@ -4005,22 +4026,29 @@ static int nct6775_suspend(struct device *dev) return 0; } -static int nct6775_resume(struct device *dev) +static int __maybe_unused nct6775_resume(struct device *dev) { struct nct6775_data *data = dev_get_drvdata(dev); + int sioreg = data->sioreg; int i, j, err = 0; + u8 reg; mutex_lock(&data->update_lock); data->bank = 0xff; /* Force initial bank selection */ - if (data->kind == nct6791 || data->kind == nct6792) { - err = superio_enter(data->sioreg); - if (err) - goto abort; + err = superio_enter(sioreg); + if (err) + goto abort; - nct6791_enable_io_mapping(data->sioreg); - superio_exit(data->sioreg); - } + superio_select(sioreg, NCT6775_LD_HWM); + reg = superio_inb(sioreg, SIO_REG_ENABLE); + if (reg != data->sio_reg_enable) + superio_outb(sioreg, SIO_REG_ENABLE, data->sio_reg_enable); + + if (data->kind == nct6791 || data->kind == nct6792) + nct6791_enable_io_mapping(sioreg); + + superio_exit(sioreg); /* Restore limits */ for (i = 0; i < data->in_num; i++) { @@ -4066,22 +4094,12 @@ abort: return err; } -static const struct dev_pm_ops nct6775_dev_pm_ops = { - .suspend = nct6775_suspend, - .resume = nct6775_resume, - .freeze = nct6775_suspend, - .restore = nct6775_resume, -}; - -#define NCT6775_DEV_PM_OPS (&nct6775_dev_pm_ops) -#else -#define NCT6775_DEV_PM_OPS NULL -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume); static struct platform_driver nct6775_driver = { .driver = { .name = DRVNAME, - .pm = NCT6775_DEV_PM_OPS, + .pm = &nct6775_dev_pm_ops, }, .probe = nct6775_probe, }; diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c new file mode 100644 index 0000000..b77b82f --- /dev/null +++ b/drivers/hwmon/nct7904.c @@ -0,0 +1,593 @@ +/* + * nct7904.c - driver for Nuvoton NCT7904D. + * + * Copyright (c) 2015 Kontron + * Author: Vadim V. Vlasov <vvlasov@dev.rtsoft.ru> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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/module.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +#define VENDOR_ID_REG 0x7A /* Any bank */ +#define NUVOTON_ID 0x50 +#define CHIP_ID_REG 0x7B /* Any bank */ +#define NCT7904_ID 0xC5 +#define DEVICE_ID_REG 0x7C /* Any bank */ + +#define BANK_SEL_REG 0xFF +#define BANK_0 0x00 +#define BANK_1 0x01 +#define BANK_2 0x02 +#define BANK_3 0x03 +#define BANK_4 0x04 +#define BANK_MAX 0x04 + +#define FANIN_MAX 12 /* Counted from 1 */ +#define VSEN_MAX 21 /* VSEN1..14, 3VDD, VBAT, V3VSB, + LTD (not a voltage), VSEN17..19 */ +#define FANCTL_MAX 4 /* Counted from 1 */ +#define TCPU_MAX 8 /* Counted from 1 */ +#define TEMP_MAX 4 /* Counted from 1 */ + +#define VT_ADC_CTRL0_REG 0x20 /* Bank 0 */ +#define VT_ADC_CTRL1_REG 0x21 /* Bank 0 */ +#define VT_ADC_CTRL2_REG 0x22 /* Bank 0 */ +#define FANIN_CTRL0_REG 0x24 +#define FANIN_CTRL1_REG 0x25 +#define DTS_T_CTRL0_REG 0x26 +#define DTS_T_CTRL1_REG 0x27 +#define VT_ADC_MD_REG 0x2E + +#define VSEN1_HV_REG 0x40 /* Bank 0; 2 regs (HV/LV) per sensor */ +#define TEMP_CH1_HV_REG 0x42 /* Bank 0; same as VSEN2_HV */ +#define LTD_HV_REG 0x62 /* Bank 0; 2 regs in VSEN range */ +#define FANIN1_HV_REG 0x80 /* Bank 0; 2 regs (HV/LV) per sensor */ +#define T_CPU1_HV_REG 0xA0 /* Bank 0; 2 regs (HV/LV) per sensor */ + +#define PRTS_REG 0x03 /* Bank 2 */ +#define FANCTL1_FMR_REG 0x00 /* Bank 3; 1 reg per channel */ +#define FANCTL1_OUT_REG 0x10 /* Bank 3; 1 reg per channel */ + +static const unsigned short normal_i2c[] = { + 0x2d, 0x2e, I2C_CLIENT_END +}; + +struct nct7904_data { + struct i2c_client *client; + struct mutex bank_lock; + int bank_sel; + u32 fanin_mask; + u32 vsen_mask; + u32 tcpu_mask; + u8 fan_mode[FANCTL_MAX]; +}; + +/* Access functions */ +static int nct7904_bank_lock(struct nct7904_data *data, unsigned bank) +{ + int ret; + + mutex_lock(&data->bank_lock); + if (data->bank_sel == bank) + return 0; + ret = i2c_smbus_write_byte_data(data->client, BANK_SEL_REG, bank); + if (ret == 0) + data->bank_sel = bank; + else + data->bank_sel = -1; + return ret; +} + +static inline void nct7904_bank_release(struct nct7904_data *data) +{ + mutex_unlock(&data->bank_lock); +} + +/* Read 1-byte register. Returns unsigned reg or -ERRNO on error. */ +static int nct7904_read_reg(struct nct7904_data *data, + unsigned bank, unsigned reg) +{ + struct i2c_client *client = data->client; + int ret; + + ret = nct7904_bank_lock(data, bank); + if (ret == 0) + ret = i2c_smbus_read_byte_data(client, reg); + + nct7904_bank_release(data); + return ret; +} + +/* + * Read 2-byte register. Returns register in big-endian format or + * -ERRNO on error. + */ +static int nct7904_read_reg16(struct nct7904_data *data, + unsigned bank, unsigned reg) +{ + struct i2c_client *client = data->client; + int ret, hi; + + ret = nct7904_bank_lock(data, bank); + if (ret == 0) { + ret = i2c_smbus_read_byte_data(client, reg); + if (ret >= 0) { + hi = ret; + ret = i2c_smbus_read_byte_data(client, reg + 1); + if (ret >= 0) + ret |= hi << 8; + } + } + + nct7904_bank_release(data); + return ret; +} + +/* Write 1-byte register. Returns 0 or -ERRNO on error. */ +static int nct7904_write_reg(struct nct7904_data *data, + unsigned bank, unsigned reg, u8 val) +{ + struct i2c_client *client = data->client; + int ret; + + ret = nct7904_bank_lock(data, bank); + if (ret == 0) + ret = i2c_smbus_write_byte_data(client, reg, val); + + nct7904_bank_release(data); + return ret; +} + +/* FANIN ATTR */ +static ssize_t show_fan(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct nct7904_data *data = dev_get_drvdata(dev); + int ret; + unsigned cnt, rpm; + + ret = nct7904_read_reg16(data, BANK_0, FANIN1_HV_REG + index * 2); + if (ret < 0) + return ret; + cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f); + if (cnt == 0x1fff) + rpm = 0; + else + rpm = 1350000 / cnt; + return sprintf(buf, "%u\n", rpm); +} + +static umode_t nct7904_fanin_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7904_data *data = dev_get_drvdata(dev); + + if (data->fanin_mask & (1 << n)) + return a->mode; + return 0; +} + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 5); +static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan, NULL, 7); +static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, show_fan, NULL, 8); +static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, show_fan, NULL, 9); +static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, show_fan, NULL, 10); +static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, show_fan, NULL, 11); + +static struct attribute *nct7904_fanin_attrs[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan5_input.dev_attr.attr, + &sensor_dev_attr_fan6_input.dev_attr.attr, + &sensor_dev_attr_fan7_input.dev_attr.attr, + &sensor_dev_attr_fan8_input.dev_attr.attr, + &sensor_dev_attr_fan9_input.dev_attr.attr, + &sensor_dev_attr_fan10_input.dev_attr.attr, + &sensor_dev_attr_fan11_input.dev_attr.attr, + &sensor_dev_attr_fan12_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group nct7904_fanin_group = { + .attrs = nct7904_fanin_attrs, + .is_visible = nct7904_fanin_is_visible, +}; + +/* VSEN ATTR */ +static ssize_t show_voltage(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct nct7904_data *data = dev_get_drvdata(dev); + int ret; + int volt; + + ret = nct7904_read_reg16(data, BANK_0, VSEN1_HV_REG + index * 2); + if (ret < 0) + return ret; + volt = ((ret & 0xff00) >> 5) | (ret & 0x7); + if (index < 14) + volt *= 2; /* 0.002V scale */ + else + volt *= 6; /* 0.006V scale */ + + return sprintf(buf, "%d\n", volt); +} + +static ssize_t show_ltemp(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct nct7904_data *data = dev_get_drvdata(dev); + int ret; + int temp; + + ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG); + if (ret < 0) + return ret; + temp = ((ret & 0xff00) >> 5) | (ret & 0x7); + temp = sign_extend32(temp, 10) * 125; + + return sprintf(buf, "%d\n", temp); +} + +static umode_t nct7904_vsen_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7904_data *data = dev_get_drvdata(dev); + + if (data->vsen_mask & (1 << n)) + return a->mode; + return 0; +} + +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 0); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 1); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_voltage, NULL, 2); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_voltage, NULL, 3); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 4); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 5); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 6); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_voltage, NULL, 7); +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_voltage, NULL, 8); +static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_voltage, NULL, 9); +static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, show_voltage, NULL, 10); +static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, show_voltage, NULL, 11); +static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, show_voltage, NULL, 12); +static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, show_voltage, NULL, 13); +/* + * Next 3 voltage sensors have specific names in the Nuvoton doc + * (3VDD, VBAT, 3VSB) but we use vacant numbers for them. + */ +static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, show_voltage, NULL, 14); +static SENSOR_DEVICE_ATTR(in16_input, S_IRUGO, show_voltage, NULL, 15); +static SENSOR_DEVICE_ATTR(in20_input, S_IRUGO, show_voltage, NULL, 16); +/* This is not a voltage, but a local temperature sensor. */ +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_ltemp, NULL, 0); +static SENSOR_DEVICE_ATTR(in17_input, S_IRUGO, show_voltage, NULL, 18); +static SENSOR_DEVICE_ATTR(in18_input, S_IRUGO, show_voltage, NULL, 19); +static SENSOR_DEVICE_ATTR(in19_input, S_IRUGO, show_voltage, NULL, 20); + +static struct attribute *nct7904_vsen_attrs[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_in10_input.dev_attr.attr, + &sensor_dev_attr_in11_input.dev_attr.attr, + &sensor_dev_attr_in12_input.dev_attr.attr, + &sensor_dev_attr_in13_input.dev_attr.attr, + &sensor_dev_attr_in14_input.dev_attr.attr, + &sensor_dev_attr_in15_input.dev_attr.attr, + &sensor_dev_attr_in16_input.dev_attr.attr, + &sensor_dev_attr_in20_input.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_in17_input.dev_attr.attr, + &sensor_dev_attr_in18_input.dev_attr.attr, + &sensor_dev_attr_in19_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group nct7904_vsen_group = { + .attrs = nct7904_vsen_attrs, + .is_visible = nct7904_vsen_is_visible, +}; + +/* CPU_TEMP ATTR */ +static ssize_t show_tcpu(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct nct7904_data *data = dev_get_drvdata(dev); + int ret; + int temp; + + ret = nct7904_read_reg16(data, BANK_0, T_CPU1_HV_REG + index * 2); + if (ret < 0) + return ret; + + temp = ((ret & 0xff00) >> 5) | (ret & 0x7); + temp = sign_extend32(temp, 10) * 125; + return sprintf(buf, "%d\n", temp); +} + +static umode_t nct7904_tcpu_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7904_data *data = dev_get_drvdata(dev); + + if (data->tcpu_mask & (1 << n)) + return a->mode; + return 0; +} + +/* "temp1_input" reserved for local temp */ +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_tcpu, NULL, 0); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_tcpu, NULL, 1); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_tcpu, NULL, 2); +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_tcpu, NULL, 3); +static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_tcpu, NULL, 4); +static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_tcpu, NULL, 5); +static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_tcpu, NULL, 6); +static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, show_tcpu, NULL, 7); + +static struct attribute *nct7904_tcpu_attrs[] = { + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp7_input.dev_attr.attr, + &sensor_dev_attr_temp8_input.dev_attr.attr, + &sensor_dev_attr_temp9_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group nct7904_tcpu_group = { + .attrs = nct7904_tcpu_attrs, + .is_visible = nct7904_tcpu_is_visible, +}; + +/* PWM ATTR */ +static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct nct7904_data *data = dev_get_drvdata(dev); + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + if (val > 255) + return -EINVAL; + + ret = nct7904_write_reg(data, BANK_3, FANCTL1_OUT_REG + index, val); + + return ret ? ret : count; +} + +static ssize_t show_pwm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct nct7904_data *data = dev_get_drvdata(dev); + int val; + + val = nct7904_read_reg(data, BANK_3, FANCTL1_OUT_REG + index); + if (val < 0) + return val; + + return sprintf(buf, "%d\n", val); +} + +static ssize_t store_mode(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct nct7904_data *data = dev_get_drvdata(dev); + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + if (val > 1 || (val && !data->fan_mode[index])) + return -EINVAL; + + ret = nct7904_write_reg(data, BANK_3, FANCTL1_FMR_REG + index, + val ? data->fan_mode[index] : 0); + + return ret ? ret : count; +} + +/* Return 0 for manual mode or 1 for SmartFan mode */ +static ssize_t show_mode(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct nct7904_data *data = dev_get_drvdata(dev); + int val; + + val = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + index); + if (val < 0) + return val; + + return sprintf(buf, "%d\n", val ? 1 : 0); +} + +/* 2 attributes per channel: pwm and mode */ +static SENSOR_DEVICE_ATTR(fan1_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0); +static SENSOR_DEVICE_ATTR(fan1_mode, S_IRUGO | S_IWUSR, + show_mode, store_mode, 0); +static SENSOR_DEVICE_ATTR(fan2_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 1); +static SENSOR_DEVICE_ATTR(fan2_mode, S_IRUGO | S_IWUSR, + show_mode, store_mode, 1); +static SENSOR_DEVICE_ATTR(fan3_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 2); +static SENSOR_DEVICE_ATTR(fan3_mode, S_IRUGO | S_IWUSR, + show_mode, store_mode, 2); +static SENSOR_DEVICE_ATTR(fan4_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 3); +static SENSOR_DEVICE_ATTR(fan4_mode, S_IRUGO | S_IWUSR, + show_mode, store_mode, 3); + +static struct attribute *nct7904_fanctl_attrs[] = { + &sensor_dev_attr_fan1_pwm.dev_attr.attr, + &sensor_dev_attr_fan1_mode.dev_attr.attr, + &sensor_dev_attr_fan2_pwm.dev_attr.attr, + &sensor_dev_attr_fan2_mode.dev_attr.attr, + &sensor_dev_attr_fan3_pwm.dev_attr.attr, + &sensor_dev_attr_fan3_mode.dev_attr.attr, + &sensor_dev_attr_fan4_pwm.dev_attr.attr, + &sensor_dev_attr_fan4_mode.dev_attr.attr, + NULL +}; + +static const struct attribute_group nct7904_fanctl_group = { + .attrs = nct7904_fanctl_attrs, +}; + +static const struct attribute_group *nct7904_groups[] = { + &nct7904_fanin_group, + &nct7904_vsen_group, + &nct7904_tcpu_group, + &nct7904_fanctl_group, + NULL +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int nct7904_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + if (!i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_BYTE | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + /* Determine the chip type. */ + if (i2c_smbus_read_byte_data(client, VENDOR_ID_REG) != NUVOTON_ID || + i2c_smbus_read_byte_data(client, CHIP_ID_REG) != NCT7904_ID || + (i2c_smbus_read_byte_data(client, DEVICE_ID_REG) & 0xf0) != 0x50 || + (i2c_smbus_read_byte_data(client, BANK_SEL_REG) & 0xf8) != 0x00) + return -ENODEV; + + strlcpy(info->type, "nct7904", I2C_NAME_SIZE); + + return 0; +} + +static int nct7904_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct nct7904_data *data; + struct device *hwmon_dev; + struct device *dev = &client->dev; + int ret, i; + u32 mask; + + data = devm_kzalloc(dev, sizeof(struct nct7904_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->bank_lock); + data->bank_sel = -1; + + /* Setup sensor groups. */ + /* FANIN attributes */ + ret = nct7904_read_reg16(data, BANK_0, FANIN_CTRL0_REG); + if (ret < 0) + return ret; + data->fanin_mask = (ret >> 8) | ((ret & 0xff) << 8); + + /* + * VSEN attributes + * + * Note: voltage sensors overlap with external temperature + * sensors. So, if we ever decide to support the latter + * we will have to adjust 'vsen_mask' accordingly. + */ + mask = 0; + ret = nct7904_read_reg16(data, BANK_0, VT_ADC_CTRL0_REG); + if (ret >= 0) + mask = (ret >> 8) | ((ret & 0xff) << 8); + ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG); + if (ret >= 0) + mask |= (ret << 16); + data->vsen_mask = mask; + + /* CPU_TEMP attributes */ + ret = nct7904_read_reg16(data, BANK_0, DTS_T_CTRL0_REG); + if (ret < 0) + return ret; + data->tcpu_mask = ((ret >> 8) & 0xf) | ((ret & 0xf) << 4); + + for (i = 0; i < FANCTL_MAX; i++) { + ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + i); + if (ret < 0) + return ret; + data->fan_mode[i] = ret; + } + + hwmon_dev = + devm_hwmon_device_register_with_groups(dev, client->name, data, + nct7904_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id nct7904_id[] = { + {"nct7904", 0}, + {} +}; + +static struct i2c_driver nct7904_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "nct7904", + }, + .probe = nct7904_probe, + .id_table = nct7904_id, + .detect = nct7904_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(nct7904_driver); + +MODULE_AUTHOR("Vadim V. Vlasov <vvlasov@dev.rtsoft.ru>"); +MODULE_DESCRIPTION("Hwmon driver for NUVOTON NCT7904"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 1991d903..2d9a712 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -24,55 +24,78 @@ #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/sysfs.h> +#include <linux/thermal.h> #define MAX_PWM 255 struct pwm_fan_ctx { struct mutex lock; struct pwm_device *pwm; - unsigned char pwm_value; + unsigned int pwm_value; + unsigned int pwm_fan_state; + unsigned int pwm_fan_max_state; + unsigned int *pwm_fan_cooling_levels; + struct thermal_cooling_device *cdev; }; -static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) { - struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); - unsigned long pwm, duty; - ssize_t ret; - - if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM) - return -EINVAL; + unsigned long duty; + int ret = 0; mutex_lock(&ctx->lock); - if (ctx->pwm_value == pwm) - goto exit_set_pwm_no_change; - - if (pwm == 0) { - pwm_disable(ctx->pwm); - goto exit_set_pwm; - } + goto exit_set_pwm_err; duty = DIV_ROUND_UP(pwm * (ctx->pwm->period - 1), MAX_PWM); ret = pwm_config(ctx->pwm, duty, ctx->pwm->period); if (ret) goto exit_set_pwm_err; + if (pwm == 0) + pwm_disable(ctx->pwm); + if (ctx->pwm_value == 0) { ret = pwm_enable(ctx->pwm); if (ret) goto exit_set_pwm_err; } -exit_set_pwm: ctx->pwm_value = pwm; -exit_set_pwm_no_change: - ret = count; exit_set_pwm_err: mutex_unlock(&ctx->lock); return ret; } +static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm) +{ + int i; + + for (i = 0; i < ctx->pwm_fan_max_state; ++i) + if (pwm < ctx->pwm_fan_cooling_levels[i + 1]) + break; + + ctx->pwm_fan_state = i; +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); + unsigned long pwm; + int ret; + + if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM) + return -EINVAL; + + ret = __set_pwm(ctx, pwm); + if (ret) + return ret; + + pwm_fan_update_state(ctx, pwm); + return count; +} + static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, char *buf) { @@ -91,10 +114,108 @@ static struct attribute *pwm_fan_attrs[] = { ATTRIBUTE_GROUPS(pwm_fan); +/* thermal cooling device callbacks */ +static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct pwm_fan_ctx *ctx = cdev->devdata; + + if (!ctx) + return -EINVAL; + + *state = ctx->pwm_fan_max_state; + + return 0; +} + +static int pwm_fan_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct pwm_fan_ctx *ctx = cdev->devdata; + + if (!ctx) + return -EINVAL; + + *state = ctx->pwm_fan_state; + + return 0; +} + +static int +pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) +{ + struct pwm_fan_ctx *ctx = cdev->devdata; + int ret; + + if (!ctx || (state > ctx->pwm_fan_max_state)) + return -EINVAL; + + if (state == ctx->pwm_fan_state) + return 0; + + ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]); + if (ret) { + dev_err(&cdev->device, "Cannot set pwm!\n"); + return ret; + } + + ctx->pwm_fan_state = state; + + return ret; +} + +static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = { + .get_max_state = pwm_fan_get_max_state, + .get_cur_state = pwm_fan_get_cur_state, + .set_cur_state = pwm_fan_set_cur_state, +}; + +static int pwm_fan_of_get_cooling_data(struct device *dev, + struct pwm_fan_ctx *ctx) +{ + struct device_node *np = dev->of_node; + int num, i, ret; + + if (!of_find_property(np, "cooling-levels", NULL)) + return 0; + + ret = of_property_count_u32_elems(np, "cooling-levels"); + if (ret <= 0) { + dev_err(dev, "Wrong data!\n"); + return ret ? : -EINVAL; + } + + num = ret; + ctx->pwm_fan_cooling_levels = devm_kzalloc(dev, num * sizeof(u32), + GFP_KERNEL); + if (!ctx->pwm_fan_cooling_levels) + return -ENOMEM; + + ret = of_property_read_u32_array(np, "cooling-levels", + ctx->pwm_fan_cooling_levels, num); + if (ret) { + dev_err(dev, "Property 'cooling-levels' cannot be read!\n"); + return ret; + } + + for (i = 0; i < num; i++) { + if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) { + dev_err(dev, "PWM fan state[%d]:%d > %d\n", i, + ctx->pwm_fan_cooling_levels[i], MAX_PWM); + return -EINVAL; + } + } + + ctx->pwm_fan_max_state = num - 1; + + return 0; +} + static int pwm_fan_probe(struct platform_device *pdev) { - struct device *hwmon; + struct thermal_cooling_device *cdev; struct pwm_fan_ctx *ctx; + struct device *hwmon; int duty_cycle; int ret; @@ -136,6 +257,26 @@ static int pwm_fan_probe(struct platform_device *pdev) pwm_disable(ctx->pwm); return PTR_ERR(hwmon); } + + ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx); + if (ret) + return ret; + + ctx->pwm_fan_state = ctx->pwm_fan_max_state; + if (IS_ENABLED(CONFIG_THERMAL)) { + cdev = thermal_of_cooling_device_register(pdev->dev.of_node, + "pwm-fan", ctx, + &pwm_fan_cooling_ops); + if (IS_ERR(cdev)) { + dev_err(&pdev->dev, + "Failed to register pwm-fan as cooling device"); + pwm_disable(ctx->pwm); + return PTR_ERR(cdev); + } + ctx->cdev = cdev; + thermal_cdev_update(cdev); + } + return 0; } @@ -143,6 +284,7 @@ static int pwm_fan_remove(struct platform_device *pdev) { struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev); + thermal_cooling_device_unregister(ctx->cdev); if (ctx->pwm_value) pwm_disable(ctx->pwm); return 0; @@ -177,7 +319,7 @@ static int pwm_fan_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pwm_fan_pm, pwm_fan_suspend, pwm_fan_resume); -static struct of_device_id of_pwm_fan_match[] = { +static const struct of_device_id of_pwm_fan_match[] = { { .compatible = "pwm-fan", }, {}, }; diff --git a/drivers/hwmon/vexpress.c b/drivers/hwmon/vexpress.c index cf1848b..8ba419d 100644 --- a/drivers/hwmon/vexpress.c +++ b/drivers/hwmon/vexpress.c @@ -193,7 +193,7 @@ static struct vexpress_hwmon_type vexpress_hwmon_energy = { }, }; -static struct of_device_id vexpress_hwmon_of_match[] = { +static const struct of_device_id vexpress_hwmon_of_match[] = { #if !defined(CONFIG_REGULATOR_VEXPRESS) { .compatible = "arm,vexpress-volt", diff --git a/drivers/i2c/busses/i2c-designware-baytrail.c b/drivers/i2c/busses/i2c-designware-baytrail.c index 5f1ff4c..7d7ae97 100644 --- a/drivers/i2c/busses/i2c-designware-baytrail.c +++ b/drivers/i2c/busses/i2c-designware-baytrail.c @@ -17,27 +17,31 @@ #include <linux/acpi.h> #include <linux/i2c.h> #include <linux/interrupt.h> + #include <asm/iosf_mbi.h> + #include "i2c-designware-core.h" #define SEMAPHORE_TIMEOUT 100 #define PUNIT_SEMAPHORE 0x7 +#define PUNIT_SEMAPHORE_BIT BIT(0) +#define PUNIT_SEMAPHORE_ACQUIRE BIT(1) static unsigned long acquired; static int get_sem(struct device *dev, u32 *sem) { - u32 reg_val; + u32 data; int ret; ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, PUNIT_SEMAPHORE, - ®_val); + &data); if (ret) { dev_err(dev, "iosf failed to read punit semaphore\n"); return ret; } - *sem = reg_val & 0x1; + *sem = data & PUNIT_SEMAPHORE_BIT; return 0; } @@ -52,27 +56,29 @@ static void reset_semaphore(struct device *dev) return; } - data = data & 0xfffffffe; + data &= ~PUNIT_SEMAPHORE_BIT; if (iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, - PUNIT_SEMAPHORE, data)) + PUNIT_SEMAPHORE, data)) dev_err(dev, "iosf failed to reset punit semaphore during write\n"); } -int baytrail_i2c_acquire(struct dw_i2c_dev *dev) +static int baytrail_i2c_acquire(struct dw_i2c_dev *dev) { - u32 sem = 0; + u32 sem; int ret; unsigned long start, end; + might_sleep(); + if (!dev || !dev->dev) return -ENODEV; - if (!dev->acquire_lock) + if (!dev->release_lock) return 0; - /* host driver writes 0x2 to side band semaphore register */ + /* host driver writes to side band semaphore register */ ret = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE, - PUNIT_SEMAPHORE, 0x2); + PUNIT_SEMAPHORE, PUNIT_SEMAPHORE_ACQUIRE); if (ret) { dev_err(dev->dev, "iosf punit semaphore request failed\n"); return ret; @@ -81,7 +87,7 @@ int baytrail_i2c_acquire(struct dw_i2c_dev *dev) /* host driver waits for bit 0 to be set in semaphore register */ start = jiffies; end = start + msecs_to_jiffies(SEMAPHORE_TIMEOUT); - while (!time_after(jiffies, end)) { + do { ret = get_sem(dev->dev, &sem); if (!ret && sem) { acquired = jiffies; @@ -91,14 +97,14 @@ int baytrail_i2c_acquire(struct dw_i2c_dev *dev) } usleep_range(1000, 2000); - } + } while (time_before(jiffies, end)); dev_err(dev->dev, "punit semaphore timed out, resetting\n"); reset_semaphore(dev->dev); ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, - PUNIT_SEMAPHORE, &sem); - if (!ret) + PUNIT_SEMAPHORE, &sem); + if (ret) dev_err(dev->dev, "iosf failed to read punit semaphore\n"); else dev_err(dev->dev, "PUNIT SEM: %d\n", sem); @@ -107,9 +113,8 @@ int baytrail_i2c_acquire(struct dw_i2c_dev *dev) return -ETIMEDOUT; } -EXPORT_SYMBOL(baytrail_i2c_acquire); -void baytrail_i2c_release(struct dw_i2c_dev *dev) +static void baytrail_i2c_release(struct dw_i2c_dev *dev) { if (!dev || !dev->dev) return; @@ -121,7 +126,6 @@ void baytrail_i2c_release(struct dw_i2c_dev *dev) dev_dbg(dev->dev, "punit semaphore held for %ums\n", jiffies_to_msecs(jiffies - acquired)); } -EXPORT_SYMBOL(baytrail_i2c_release); int i2c_dw_eval_lock_support(struct dw_i2c_dev *dev) { @@ -137,7 +141,6 @@ int i2c_dw_eval_lock_support(struct dw_i2c_dev *dev) return 0; status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host); - if (ACPI_FAILURE(status)) return 0; @@ -153,7 +156,6 @@ int i2c_dw_eval_lock_support(struct dw_i2c_dev *dev) return 0; } -EXPORT_SYMBOL(i2c_dw_eval_lock_support); MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); MODULE_DESCRIPTION("Baytrail I2C Semaphore driver"); diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 210cf48..edf274c 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -679,9 +679,6 @@ static int i2c_device_remove(struct device *dev) status = driver->remove(client); } - if (dev->of_node) - irq_dispose_mapping(client->irq); - dev_pm_domain_detach(&client->dev, true); return status; } diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 1793aea..6eb738c 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1793,11 +1793,11 @@ static void idetape_setup(ide_drive_t *drive, idetape_tape_t *tape, int minor) tape->best_dsc_rw_freq = clamp_t(unsigned long, t, IDETAPE_DSC_RW_MIN, IDETAPE_DSC_RW_MAX); printk(KERN_INFO "ide-tape: %s <-> %s: %dKBps, %d*%dkB buffer, " - "%lums tDSC%s\n", + "%ums tDSC%s\n", drive->name, tape->name, *(u16 *)&tape->caps[14], (*(u16 *)&tape->caps[16] * 512) / tape->buffer_size, tape->buffer_size / 1024, - tape->best_dsc_rw_freq * 1000 / HZ, + jiffies_to_msecs(tape->best_dsc_rw_freq), (drive->dev_flags & IDE_DFLAG_USING_DMA) ? ", DMA" : ""); ide_proc_register_driver(drive, tape->driver); diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index b0e5852..5c979d0 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -55,7 +55,7 @@ #include <linux/kernel.h> #include <linux/cpuidle.h> -#include <linux/clockchips.h> +#include <linux/tick.h> #include <trace/events/power.h> #include <linux/sched.h> #include <linux/notifier.h> @@ -638,12 +638,12 @@ static int intel_idle(struct cpuidle_device *dev, leave_mm(cpu); if (!(lapic_timer_reliable_states & (1 << (cstate)))) - clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu); + tick_broadcast_enter(); mwait_idle_with_hints(eax, ecx); if (!(lapic_timer_reliable_states & (1 << (cstate)))) - clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu); + tick_broadcast_exit(); return index; } @@ -665,13 +665,12 @@ static void intel_idle_freeze(struct cpuidle_device *dev, static void __setup_broadcast_timer(void *arg) { - unsigned long reason = (unsigned long)arg; - int cpu = smp_processor_id(); - - reason = reason ? - CLOCK_EVT_NOTIFY_BROADCAST_ON : CLOCK_EVT_NOTIFY_BROADCAST_OFF; + unsigned long on = (unsigned long)arg; - clockevents_notify(reason, &cpu); + if (on) + tick_broadcast_enable(); + else + tick_broadcast_disable(); } static int cpu_hotplug_notify(struct notifier_block *n, diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 1096da3..75c6d21 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -659,7 +659,7 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p) mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->buffer->scan_mask, + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { ret = bma180_get_data_reg(data, bit); if (ret < 0) { diff --git a/drivers/iio/accel/bmc150-accel.c b/drivers/iio/accel/bmc150-accel.c index 066d0c0..75567fd 100644 --- a/drivers/iio/accel/bmc150-accel.c +++ b/drivers/iio/accel/bmc150-accel.c @@ -168,14 +168,14 @@ static const struct { int val; int val2; u8 bw_bits; -} bmc150_accel_samp_freq_table[] = { {7, 810000, 0x08}, - {15, 630000, 0x09}, - {31, 250000, 0x0A}, - {62, 500000, 0x0B}, - {125, 0, 0x0C}, - {250, 0, 0x0D}, - {500, 0, 0x0E}, - {1000, 0, 0x0F} }; +} bmc150_accel_samp_freq_table[] = { {15, 620000, 0x08}, + {31, 260000, 0x09}, + {62, 500000, 0x0A}, + {125, 0, 0x0B}, + {250, 0, 0x0C}, + {500, 0, 0x0D}, + {1000, 0, 0x0E}, + {2000, 0, 0x0F} }; static const struct { int bw_bits; @@ -840,7 +840,7 @@ static int bmc150_accel_validate_trigger(struct iio_dev *indio_dev, } static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( - "7.810000 15.630000 31.250000 62.500000 125 250 500 1000"); + "15.620000 31.260000 62.50000 125 250 500 1000 2000"); static struct attribute *bmc150_accel_attributes[] = { &iio_const_attr_sampling_frequency_available.dev_attr.attr, @@ -986,7 +986,7 @@ static irqreturn_t bmc150_accel_trigger_handler(int irq, void *p) int bit, ret, i = 0; mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->buffer->scan_mask, + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { ret = i2c_smbus_read_word_data(data->client, BMC150_ACCEL_AXIS_TO_REG(bit)); diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 567de26..1a63795 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -956,7 +956,7 @@ static irqreturn_t kxcjk1013_trigger_handler(int irq, void *p) mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->buffer->scan_mask, + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { ret = kxcjk1013_get_acc_reg(data, bit); if (ret < 0) { diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 202daf8..d82af14 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -135,9 +135,19 @@ config AXP288_ADC device. Depending on platform configuration, this general purpose ADC can be used for sampling sensors such as thermal resistors. +config DA9150_GPADC + tristate "Dialog DA9150 GPADC driver support" + depends on MFD_DA9150 + help + Say yes here to build support for Dialog DA9150 GPADC. + + This driver can also be built as a module. If chosen, the module name + will be da9150-gpadc. + config CC10001_ADC tristate "Cosmic Circuits 10001 ADC driver" - depends on HAS_IOMEM || HAVE_CLK || REGULATOR + depends on HAVE_CLK || REGULATOR + depends on HAS_IOMEM select IIO_BUFFER select IIO_TRIGGERED_BUFFER help diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 0315af6..3930e63 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AD799X) += ad799x.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_AXP288_ADC) += axp288_adc.o +obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index ff61ae5..8a0eb4a 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -544,7 +544,6 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) { struct iio_dev *idev = iio_trigger_get_drvdata(trig); struct at91_adc_state *st = iio_priv(idev); - struct iio_buffer *buffer = idev->buffer; struct at91_adc_reg_desc *reg = st->registers; u32 status = at91_adc_readl(st, reg->trigger_register); int value; @@ -564,7 +563,7 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) at91_adc_writel(st, reg->trigger_register, status | value); - for_each_set_bit(bit, buffer->scan_mask, + for_each_set_bit(bit, idev->active_scan_mask, st->num_channels) { struct iio_chan_spec const *chan = idev->channels + bit; at91_adc_writel(st, AT91_ADC_CHER, @@ -579,7 +578,7 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) at91_adc_writel(st, reg->trigger_register, status & ~value); - for_each_set_bit(bit, buffer->scan_mask, + for_each_set_bit(bit, idev->active_scan_mask, st->num_channels) { struct iio_chan_spec const *chan = idev->channels + bit; at91_adc_writel(st, AT91_ADC_CHDR, diff --git a/drivers/iio/adc/da9150-gpadc.c b/drivers/iio/adc/da9150-gpadc.c new file mode 100644 index 0000000..3445107 --- /dev/null +++ b/drivers/iio/adc/da9150-gpadc.c @@ -0,0 +1,407 @@ +/* + * DA9150 GPADC Driver + * + * Copyright (c) 2014 Dialog Semiconductor + * + * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/completion.h> +#include <linux/iio/iio.h> +#include <linux/iio/machine.h> +#include <linux/iio/driver.h> +#include <linux/mfd/da9150/core.h> +#include <linux/mfd/da9150/registers.h> + +/* Channels */ +enum da9150_gpadc_hw_channel { + DA9150_GPADC_HW_CHAN_GPIOA_2V = 0, + DA9150_GPADC_HW_CHAN_GPIOA_2V_, + DA9150_GPADC_HW_CHAN_GPIOB_2V, + DA9150_GPADC_HW_CHAN_GPIOB_2V_, + DA9150_GPADC_HW_CHAN_GPIOC_2V, + DA9150_GPADC_HW_CHAN_GPIOC_2V_, + DA9150_GPADC_HW_CHAN_GPIOD_2V, + DA9150_GPADC_HW_CHAN_GPIOD_2V_, + DA9150_GPADC_HW_CHAN_IBUS_SENSE, + DA9150_GPADC_HW_CHAN_IBUS_SENSE_, + DA9150_GPADC_HW_CHAN_VBUS_DIV, + DA9150_GPADC_HW_CHAN_VBUS_DIV_, + DA9150_GPADC_HW_CHAN_ID, + DA9150_GPADC_HW_CHAN_ID_, + DA9150_GPADC_HW_CHAN_VSYS, + DA9150_GPADC_HW_CHAN_VSYS_, + DA9150_GPADC_HW_CHAN_GPIOA_6V, + DA9150_GPADC_HW_CHAN_GPIOA_6V_, + DA9150_GPADC_HW_CHAN_GPIOB_6V, + DA9150_GPADC_HW_CHAN_GPIOB_6V_, + DA9150_GPADC_HW_CHAN_GPIOC_6V, + DA9150_GPADC_HW_CHAN_GPIOC_6V_, + DA9150_GPADC_HW_CHAN_GPIOD_6V, + DA9150_GPADC_HW_CHAN_GPIOD_6V_, + DA9150_GPADC_HW_CHAN_VBAT, + DA9150_GPADC_HW_CHAN_VBAT_, + DA9150_GPADC_HW_CHAN_TBAT, + DA9150_GPADC_HW_CHAN_TBAT_, + DA9150_GPADC_HW_CHAN_TJUNC_CORE, + DA9150_GPADC_HW_CHAN_TJUNC_CORE_, + DA9150_GPADC_HW_CHAN_TJUNC_OVP, + DA9150_GPADC_HW_CHAN_TJUNC_OVP_, +}; + +enum da9150_gpadc_channel { + DA9150_GPADC_CHAN_GPIOA = 0, + DA9150_GPADC_CHAN_GPIOB, + DA9150_GPADC_CHAN_GPIOC, + DA9150_GPADC_CHAN_GPIOD, + DA9150_GPADC_CHAN_IBUS, + DA9150_GPADC_CHAN_VBUS, + DA9150_GPADC_CHAN_VSYS, + DA9150_GPADC_CHAN_VBAT, + DA9150_GPADC_CHAN_TBAT, + DA9150_GPADC_CHAN_TJUNC_CORE, + DA9150_GPADC_CHAN_TJUNC_OVP, +}; + +/* Private data */ +struct da9150_gpadc { + struct da9150 *da9150; + struct device *dev; + + struct mutex lock; + struct completion complete; +}; + + +static irqreturn_t da9150_gpadc_irq(int irq, void *data) +{ + + struct da9150_gpadc *gpadc = data; + + complete(&gpadc->complete); + + return IRQ_HANDLED; +} + +static int da9150_gpadc_read_adc(struct da9150_gpadc *gpadc, int hw_chan) +{ + u8 result_regs[2]; + int result; + + mutex_lock(&gpadc->lock); + + /* Set channel & enable measurement */ + da9150_reg_write(gpadc->da9150, DA9150_GPADC_MAN, + (DA9150_GPADC_EN_MASK | + hw_chan << DA9150_GPADC_MUX_SHIFT)); + + /* Consume left-over completion from a previous timeout */ + try_wait_for_completion(&gpadc->complete); + + /* Check for actual completion */ + wait_for_completion_timeout(&gpadc->complete, msecs_to_jiffies(5)); + + /* Read result and status from device */ + da9150_bulk_read(gpadc->da9150, DA9150_GPADC_RES_A, 2, result_regs); + + mutex_unlock(&gpadc->lock); + + /* Check to make sure device really has completed reading */ + if (result_regs[1] & DA9150_GPADC_RUN_MASK) { + dev_err(gpadc->dev, "Timeout on channel %d of GPADC\n", + hw_chan); + return -ETIMEDOUT; + } + + /* LSBs - 2 bits */ + result = (result_regs[1] & DA9150_GPADC_RES_L_MASK) >> + DA9150_GPADC_RES_L_SHIFT; + /* MSBs - 8 bits */ + result |= result_regs[0] << DA9150_GPADC_RES_L_BITS; + + return result; +} + +static inline int da9150_gpadc_gpio_6v_voltage_now(int raw_val) +{ + /* Convert to mV */ + return (6 * ((raw_val * 1000) + 500)) / 1024; +} + +static inline int da9150_gpadc_ibus_current_avg(int raw_val) +{ + /* Convert to mA */ + return (4 * ((raw_val * 1000) + 500)) / 2048; +} + +static inline int da9150_gpadc_vbus_21v_voltage_now(int raw_val) +{ + /* Convert to mV */ + return (21 * ((raw_val * 1000) + 500)) / 1024; +} + +static inline int da9150_gpadc_vsys_6v_voltage_now(int raw_val) +{ + /* Convert to mV */ + return (3 * ((raw_val * 1000) + 500)) / 512; +} + +static int da9150_gpadc_read_processed(struct da9150_gpadc *gpadc, int channel, + int hw_chan, int *val) +{ + int raw_val; + + raw_val = da9150_gpadc_read_adc(gpadc, hw_chan); + if (raw_val < 0) + return raw_val; + + switch (channel) { + case DA9150_GPADC_CHAN_GPIOA: + case DA9150_GPADC_CHAN_GPIOB: + case DA9150_GPADC_CHAN_GPIOC: + case DA9150_GPADC_CHAN_GPIOD: + *val = da9150_gpadc_gpio_6v_voltage_now(raw_val); + break; + case DA9150_GPADC_CHAN_IBUS: + *val = da9150_gpadc_ibus_current_avg(raw_val); + break; + case DA9150_GPADC_CHAN_VBUS: + *val = da9150_gpadc_vbus_21v_voltage_now(raw_val); + break; + case DA9150_GPADC_CHAN_VSYS: + *val = da9150_gpadc_vsys_6v_voltage_now(raw_val); + break; + default: + /* No processing for other channels so return raw value */ + *val = raw_val; + break; + } + + return IIO_VAL_INT; +} + +static int da9150_gpadc_read_scale(int channel, int *val, int *val2) +{ + switch (channel) { + case DA9150_GPADC_CHAN_VBAT: + *val = 2932; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + case DA9150_GPADC_CHAN_TJUNC_CORE: + case DA9150_GPADC_CHAN_TJUNC_OVP: + *val = 1000000; + *val2 = 4420; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static int da9150_gpadc_read_offset(int channel, int *val) +{ + switch (channel) { + case DA9150_GPADC_CHAN_VBAT: + *val = 1500000 / 2932; + return IIO_VAL_INT; + case DA9150_GPADC_CHAN_TJUNC_CORE: + case DA9150_GPADC_CHAN_TJUNC_OVP: + *val = -144; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int da9150_gpadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct da9150_gpadc *gpadc = iio_priv(indio_dev); + + if ((chan->channel < DA9150_GPADC_CHAN_GPIOA) || + (chan->channel > DA9150_GPADC_CHAN_TJUNC_OVP)) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: + return da9150_gpadc_read_processed(gpadc, chan->channel, + chan->address, val); + case IIO_CHAN_INFO_SCALE: + return da9150_gpadc_read_scale(chan->channel, val, val2); + case IIO_CHAN_INFO_OFFSET: + return da9150_gpadc_read_offset(chan->channel, val); + default: + return -EINVAL; + } +} + +static const struct iio_info da9150_gpadc_info = { + .read_raw = &da9150_gpadc_read_raw, + .driver_module = THIS_MODULE, +}; + +#define DA9150_GPADC_CHANNEL(_id, _hw_id, _type, chan_info, \ + _ext_name) { \ + .type = _type, \ + .indexed = 1, \ + .channel = DA9150_GPADC_CHAN_##_id, \ + .address = DA9150_GPADC_HW_CHAN_##_hw_id, \ + .info_mask_separate = chan_info, \ + .extend_name = _ext_name, \ + .datasheet_name = #_id, \ +} + +#define DA9150_GPADC_CHANNEL_RAW(_id, _hw_id, _type, _ext_name) \ + DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \ + BIT(IIO_CHAN_INFO_RAW), _ext_name) + +#define DA9150_GPADC_CHANNEL_SCALED(_id, _hw_id, _type, _ext_name) \ + DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + _ext_name) + +#define DA9150_GPADC_CHANNEL_PROCESSED(_id, _hw_id, _type, _ext_name) \ + DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \ + BIT(IIO_CHAN_INFO_PROCESSED), _ext_name) + +/* Supported channels */ +static const struct iio_chan_spec da9150_gpadc_channels[] = { + DA9150_GPADC_CHANNEL_PROCESSED(GPIOA, GPIOA_6V, IIO_VOLTAGE, NULL), + DA9150_GPADC_CHANNEL_PROCESSED(GPIOB, GPIOB_6V, IIO_VOLTAGE, NULL), + DA9150_GPADC_CHANNEL_PROCESSED(GPIOC, GPIOC_6V, IIO_VOLTAGE, NULL), + DA9150_GPADC_CHANNEL_PROCESSED(GPIOD, GPIOD_6V, IIO_VOLTAGE, NULL), + DA9150_GPADC_CHANNEL_PROCESSED(IBUS, IBUS_SENSE, IIO_CURRENT, "ibus"), + DA9150_GPADC_CHANNEL_PROCESSED(VBUS, VBUS_DIV_, IIO_VOLTAGE, "vbus"), + DA9150_GPADC_CHANNEL_PROCESSED(VSYS, VSYS, IIO_VOLTAGE, "vsys"), + DA9150_GPADC_CHANNEL_SCALED(VBAT, VBAT, IIO_VOLTAGE, "vbat"), + DA9150_GPADC_CHANNEL_RAW(TBAT, TBAT, IIO_VOLTAGE, "tbat"), + DA9150_GPADC_CHANNEL_SCALED(TJUNC_CORE, TJUNC_CORE, IIO_TEMP, + "tjunc_core"), + DA9150_GPADC_CHANNEL_SCALED(TJUNC_OVP, TJUNC_OVP, IIO_TEMP, + "tjunc_ovp"), +}; + +/* Default maps used by da9150-charger */ +static struct iio_map da9150_gpadc_default_maps[] = { + { + .consumer_dev_name = "da9150-charger", + .consumer_channel = "CHAN_IBUS", + .adc_channel_label = "IBUS", + }, + { + .consumer_dev_name = "da9150-charger", + .consumer_channel = "CHAN_VBUS", + .adc_channel_label = "VBUS", + }, + { + .consumer_dev_name = "da9150-charger", + .consumer_channel = "CHAN_TJUNC", + .adc_channel_label = "TJUNC_CORE", + }, + { + .consumer_dev_name = "da9150-charger", + .consumer_channel = "CHAN_VBAT", + .adc_channel_label = "VBAT", + }, + {}, +}; + +static int da9150_gpadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct da9150 *da9150 = dev_get_drvdata(dev->parent); + struct da9150_gpadc *gpadc; + struct iio_dev *indio_dev; + int irq, ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc)); + if (!indio_dev) { + dev_err(&pdev->dev, "Failed to allocate IIO device\n"); + return -ENOMEM; + } + gpadc = iio_priv(indio_dev); + + platform_set_drvdata(pdev, indio_dev); + gpadc->da9150 = da9150; + gpadc->dev = dev; + mutex_init(&gpadc->lock); + init_completion(&gpadc->complete); + + irq = platform_get_irq_byname(pdev, "GPADC"); + if (irq < 0) { + dev_err(dev, "Failed to get IRQ: %d\n", irq); + return irq; + } + + ret = devm_request_threaded_irq(dev, irq, NULL, da9150_gpadc_irq, + IRQF_ONESHOT, "GPADC", gpadc); + if (ret) { + dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret); + return ret; + } + + ret = iio_map_array_register(indio_dev, da9150_gpadc_default_maps); + if (ret) { + dev_err(dev, "Failed to register IIO maps: %d\n", ret); + return ret; + } + + indio_dev->name = dev_name(dev); + indio_dev->dev.parent = dev; + indio_dev->dev.of_node = pdev->dev.of_node; + indio_dev->info = &da9150_gpadc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = da9150_gpadc_channels; + indio_dev->num_channels = ARRAY_SIZE(da9150_gpadc_channels); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "Failed to register IIO device: %d\n", ret); + goto iio_map_unreg; + } + + return 0; + +iio_map_unreg: + iio_map_array_unregister(indio_dev); + + return ret; +} + +static int da9150_gpadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + iio_map_array_unregister(indio_dev); + + return 0; +} + +static struct platform_driver da9150_gpadc_driver = { + .driver = { + .name = "da9150-gpadc", + }, + .probe = da9150_gpadc_probe, + .remove = da9150_gpadc_remove, +}; + +module_platform_driver(da9150_gpadc_driver); + +MODULE_DESCRIPTION("GPADC Driver for DA9150"); +MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c index 5167225..b96c636 100644 --- a/drivers/iio/adc/mcp3422.c +++ b/drivers/iio/adc/mcp3422.c @@ -58,20 +58,11 @@ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ } -/* LSB is in nV to eliminate floating point */ -static const u32 rates_to_lsb[] = {1000000, 250000, 62500, 15625}; - -/* - * scales calculated as: - * rates_to_lsb[sample_rate] / (1 << pga); - * pga is 1 for 0, 2 - */ - static const int mcp3422_scales[4][4] = { - { 1000000, 250000, 62500, 15625 }, - { 500000 , 125000, 31250, 7812 }, - { 250000 , 62500 , 15625, 3906 }, - { 125000 , 31250 , 7812 , 1953 } }; + { 1000000, 500000, 250000, 125000 }, + { 250000 , 125000, 62500 , 31250 }, + { 62500 , 31250 , 15625 , 7812 }, + { 15625 , 7812 , 3906 , 1953 } }; /* Constant msleep times for data acquisitions */ static const int mcp3422_read_times[4] = { diff --git a/drivers/iio/adc/qcom-spmi-iadc.c b/drivers/iio/adc/qcom-spmi-iadc.c index b9666f2..fabd24e 100644 --- a/drivers/iio/adc/qcom-spmi-iadc.c +++ b/drivers/iio/adc/qcom-spmi-iadc.c @@ -296,7 +296,8 @@ static int iadc_do_conversion(struct iadc_chip *iadc, int chan, u16 *data) if (iadc->poll_eoc) { ret = iadc_poll_wait_eoc(iadc, wait); } else { - ret = wait_for_completion_timeout(&iadc->complete, wait); + ret = wait_for_completion_timeout(&iadc->complete, + usecs_to_jiffies(wait)); if (!ret) ret = -ETIMEDOUT; else diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 2e5cc44..a0e7161 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -188,12 +188,11 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev) static int tiadc_buffer_postenable(struct iio_dev *indio_dev) { struct tiadc_device *adc_dev = iio_priv(indio_dev); - struct iio_buffer *buffer = indio_dev->buffer; unsigned int enb = 0; u8 bit; tiadc_step_config(indio_dev); - for_each_set_bit(bit, buffer->scan_mask, adc_dev->channels) + for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) enb |= (get_adc_step_bit(adc_dev, bit) << 1); adc_dev->buffer_en_ch_steps = enb; diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 8ec353c..e63b8e7 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -141,9 +141,13 @@ struct vf610_adc { struct regulator *vref; struct vf610_adc_feature adc_feature; + u32 sample_freq_avail[5]; + struct completion completion; }; +static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 }; + #define VF610_ADC_CHAN(_idx, _chan_type) { \ .type = (_chan_type), \ .indexed = 1, \ @@ -180,35 +184,47 @@ static const struct iio_chan_spec vf610_adc_iio_channels[] = { /* sentinel */ }; -/* - * ADC sample frequency, unit is ADCK cycles. - * ADC clk source is ipg clock, which is the same as bus clock. - * - * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder) - * SFCAdder: fixed to 6 ADCK cycles - * AverageNum: 1, 4, 8, 16, 32 samples for hardware average. - * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode - * LSTAdder(Long Sample Time): fixed to 3 ADCK cycles - * - * By default, enable 12 bit resolution mode, clock source - * set to ipg clock, So get below frequency group: - */ -static const u32 vf610_sample_freq_avail[5] = -{1941176, 559332, 286957, 145374, 73171}; +static inline void vf610_adc_calculate_rates(struct vf610_adc *info) +{ + unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk); + int i; + + /* + * Calculate ADC sample frequencies + * Sample time unit is ADCK cycles. ADCK clk source is ipg clock, + * which is the same as bus clock. + * + * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder) + * SFCAdder: fixed to 6 ADCK cycles + * AverageNum: 1, 4, 8, 16, 32 samples for hardware average. + * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode + * LSTAdder(Long Sample Time): fixed to 3 ADCK cycles + */ + adck_rate = ipg_rate / info->adc_feature.clk_div; + for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++) + info->sample_freq_avail[i] = + adck_rate / (6 + vf610_hw_avgs[i] * (25 + 3)); +} static inline void vf610_adc_cfg_init(struct vf610_adc *info) { + struct vf610_adc_feature *adc_feature = &info->adc_feature; + /* set default Configuration for ADC controller */ - info->adc_feature.clk_sel = VF610_ADCIOC_BUSCLK_SET; - info->adc_feature.vol_ref = VF610_ADCIOC_VR_VREF_SET; + adc_feature->clk_sel = VF610_ADCIOC_BUSCLK_SET; + adc_feature->vol_ref = VF610_ADCIOC_VR_VREF_SET; + + adc_feature->calibration = true; + adc_feature->ovwren = true; + + adc_feature->res_mode = 12; + adc_feature->sample_rate = 1; + adc_feature->lpm = true; - info->adc_feature.calibration = true; - info->adc_feature.ovwren = true; + /* Use a save ADCK which is below 20MHz on all devices */ + adc_feature->clk_div = 8; - info->adc_feature.clk_div = 1; - info->adc_feature.res_mode = 12; - info->adc_feature.sample_rate = 1; - info->adc_feature.lpm = true; + vf610_adc_calculate_rates(info); } static void vf610_adc_cfg_post_set(struct vf610_adc *info) @@ -290,12 +306,10 @@ static void vf610_adc_cfg_set(struct vf610_adc *info) cfg_data = readl(info->regs + VF610_REG_ADC_CFG); - /* low power configuration */ cfg_data &= ~VF610_ADC_ADLPC_EN; if (adc_feature->lpm) cfg_data |= VF610_ADC_ADLPC_EN; - /* disable high speed */ cfg_data &= ~VF610_ADC_ADHSC_EN; writel(cfg_data, info->regs + VF610_REG_ADC_CFG); @@ -435,10 +449,27 @@ static irqreturn_t vf610_adc_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1941176, 559332, 286957, 145374, 73171"); +static ssize_t vf610_show_samp_freq_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vf610_adc *info = iio_priv(dev_to_iio_dev(dev)); + size_t len = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(info->sample_freq_avail); i++) + len += scnprintf(buf + len, PAGE_SIZE - len, + "%u ", info->sample_freq_avail[i]); + + /* replace trailing space by newline */ + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(vf610_show_samp_freq_avail); static struct attribute *vf610_attributes[] = { - &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, NULL }; @@ -502,7 +533,7 @@ static int vf610_read_raw(struct iio_dev *indio_dev, return IIO_VAL_FRACTIONAL_LOG2; case IIO_CHAN_INFO_SAMP_FREQ: - *val = vf610_sample_freq_avail[info->adc_feature.sample_rate]; + *val = info->sample_freq_avail[info->adc_feature.sample_rate]; *val2 = 0; return IIO_VAL_INT; @@ -525,9 +556,9 @@ static int vf610_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: for (i = 0; - i < ARRAY_SIZE(vf610_sample_freq_avail); + i < ARRAY_SIZE(info->sample_freq_avail); i++) - if (val == vf610_sample_freq_avail[i]) { + if (val == info->sample_freq_avail[i]) { info->adc_feature.sample_rate = i; vf610_adc_sample_set(info); return 0; diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c index 52d7043..55a90082 100644 --- a/drivers/iio/common/ssp_sensors/ssp_dev.c +++ b/drivers/iio/common/ssp_sensors/ssp_dev.c @@ -640,6 +640,7 @@ static int ssp_remove(struct spi_device *spi) return 0; } +#ifdef CONFIG_PM_SLEEP static int ssp_suspend(struct device *dev) { int ret; @@ -688,6 +689,7 @@ static int ssp_resume(struct device *dev) return 0; } +#endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops ssp_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(ssp_suspend, ssp_resume) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index f57562a..15c73e2 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -322,7 +322,7 @@ static int ad5686_probe(struct spi_device *spi) st = iio_priv(indio_dev); spi_set_drvdata(spi, indio_dev); - st->reg = devm_regulator_get(&spi->dev, "vcc"); + st->reg = devm_regulator_get_optional(&spi->dev, "vcc"); if (!IS_ERR(st->reg)) { ret = regulator_enable(st->reg); if (ret) diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c index 60451b3..ccf3ea7 100644 --- a/drivers/iio/gyro/bmg160.c +++ b/drivers/iio/gyro/bmg160.c @@ -822,7 +822,7 @@ static irqreturn_t bmg160_trigger_handler(int irq, void *p) int bit, ret, i = 0; mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->buffer->scan_mask, + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { ret = i2c_smbus_read_word_data(data->client, BMG160_AXIS_TO_REG(bit)); diff --git a/drivers/iio/humidity/dht11.c b/drivers/iio/humidity/dht11.c index 623c145..7d79a1a 100644 --- a/drivers/iio/humidity/dht11.c +++ b/drivers/iio/humidity/dht11.c @@ -29,6 +29,7 @@ #include <linux/wait.h> #include <linux/bitops.h> #include <linux/completion.h> +#include <linux/mutex.h> #include <linux/delay.h> #include <linux/gpio.h> #include <linux/of_gpio.h> @@ -39,8 +40,12 @@ #define DHT11_DATA_VALID_TIME 2000000000 /* 2s in ns */ -#define DHT11_EDGES_PREAMBLE 4 +#define DHT11_EDGES_PREAMBLE 2 #define DHT11_BITS_PER_READ 40 +/* + * Note that when reading the sensor actually 84 edges are detected, but + * since the last edge is not significant, we only store 83: + */ #define DHT11_EDGES_PER_READ (2*DHT11_BITS_PER_READ + DHT11_EDGES_PREAMBLE + 1) /* Data transmission timing (nano seconds) */ @@ -57,6 +62,7 @@ struct dht11 { int irq; struct completion completion; + struct mutex lock; s64 timestamp; int temperature; @@ -88,7 +94,7 @@ static int dht11_decode(struct dht11 *dht11, int offset) unsigned char temp_int, temp_dec, hum_int, hum_dec, checksum; /* Calculate timestamp resolution */ - for (i = 0; i < dht11->num_edges; ++i) { + for (i = 1; i < dht11->num_edges; ++i) { t = dht11->edges[i].ts - dht11->edges[i-1].ts; if (t > 0 && t < timeres) timeres = t; @@ -138,6 +144,27 @@ static int dht11_decode(struct dht11 *dht11, int offset) return 0; } +/* + * IRQ handler called on GPIO edges + */ +static irqreturn_t dht11_handle_irq(int irq, void *data) +{ + struct iio_dev *iio = data; + struct dht11 *dht11 = iio_priv(iio); + + /* TODO: Consider making the handler safe for IRQ sharing */ + if (dht11->num_edges < DHT11_EDGES_PER_READ && dht11->num_edges >= 0) { + dht11->edges[dht11->num_edges].ts = iio_get_time_ns(); + dht11->edges[dht11->num_edges++].value = + gpio_get_value(dht11->gpio); + + if (dht11->num_edges >= DHT11_EDGES_PER_READ) + complete(&dht11->completion); + } + + return IRQ_HANDLED; +} + static int dht11_read_raw(struct iio_dev *iio_dev, const struct iio_chan_spec *chan, int *val, int *val2, long m) @@ -145,6 +172,7 @@ static int dht11_read_raw(struct iio_dev *iio_dev, struct dht11 *dht11 = iio_priv(iio_dev); int ret; + mutex_lock(&dht11->lock); if (dht11->timestamp + DHT11_DATA_VALID_TIME < iio_get_time_ns()) { reinit_completion(&dht11->completion); @@ -157,8 +185,17 @@ static int dht11_read_raw(struct iio_dev *iio_dev, if (ret) goto err; + ret = request_irq(dht11->irq, dht11_handle_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + iio_dev->name, iio_dev); + if (ret) + goto err; + ret = wait_for_completion_killable_timeout(&dht11->completion, HZ); + + free_irq(dht11->irq, iio_dev); + if (ret == 0 && dht11->num_edges < DHT11_EDGES_PER_READ - 1) { dev_err(&iio_dev->dev, "Only %d signal edges detected\n", @@ -185,6 +222,7 @@ static int dht11_read_raw(struct iio_dev *iio_dev, ret = -EINVAL; err: dht11->num_edges = -1; + mutex_unlock(&dht11->lock); return ret; } @@ -193,27 +231,6 @@ static const struct iio_info dht11_iio_info = { .read_raw = dht11_read_raw, }; -/* - * IRQ handler called on GPIO edges -*/ -static irqreturn_t dht11_handle_irq(int irq, void *data) -{ - struct iio_dev *iio = data; - struct dht11 *dht11 = iio_priv(iio); - - /* TODO: Consider making the handler safe for IRQ sharing */ - if (dht11->num_edges < DHT11_EDGES_PER_READ && dht11->num_edges >= 0) { - dht11->edges[dht11->num_edges].ts = iio_get_time_ns(); - dht11->edges[dht11->num_edges++].value = - gpio_get_value(dht11->gpio); - - if (dht11->num_edges >= DHT11_EDGES_PER_READ) - complete(&dht11->completion); - } - - return IRQ_HANDLED; -} - static const struct iio_chan_spec dht11_chan_spec[] = { { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), }, @@ -256,11 +273,6 @@ static int dht11_probe(struct platform_device *pdev) dev_err(dev, "GPIO %d has no interrupt\n", dht11->gpio); return -EINVAL; } - ret = devm_request_irq(dev, dht11->irq, dht11_handle_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - pdev->name, iio); - if (ret) - return ret; dht11->timestamp = iio_get_time_ns() - DHT11_DATA_VALID_TIME - 1; dht11->num_edges = -1; @@ -268,6 +280,7 @@ static int dht11_probe(struct platform_device *pdev) platform_set_drvdata(pdev, iio); init_completion(&dht11->completion); + mutex_init(&dht11->lock); iio->name = pdev->name; iio->dev.parent = &pdev->dev; iio->info = &dht11_iio_info; diff --git a/drivers/iio/humidity/si7020.c b/drivers/iio/humidity/si7020.c index b541646..fa3b809 100644 --- a/drivers/iio/humidity/si7020.c +++ b/drivers/iio/humidity/si7020.c @@ -45,12 +45,12 @@ static int si7020_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - struct i2c_client *client = iio_priv(indio_dev); + struct i2c_client **client = iio_priv(indio_dev); int ret; switch (mask) { case IIO_CHAN_INFO_RAW: - ret = i2c_smbus_read_word_data(client, + ret = i2c_smbus_read_word_data(*client, chan->type == IIO_TEMP ? SI7020CMD_TEMP_HOLD : SI7020CMD_RH_HOLD); @@ -126,7 +126,7 @@ static int si7020_probe(struct i2c_client *client, /* Wait the maximum power-up time after software reset. */ msleep(15); - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*client)); + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu/adis16400_core.c index b70873d..fa795dc 100644 --- a/drivers/iio/imu/adis16400_core.c +++ b/drivers/iio/imu/adis16400_core.c @@ -26,6 +26,7 @@ #include <linux/list.h> #include <linux/module.h> #include <linux/debugfs.h> +#include <linux/bitops.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -414,7 +415,7 @@ static int adis16400_read_raw(struct iio_dev *indio_dev, mutex_unlock(&indio_dev->mlock); if (ret) return ret; - val16 = ((val16 & 0xFFF) << 4) >> 4; + val16 = sign_extend32(val16, 11); *val = val16; return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c index e0017c2..f53e9a8 100644 --- a/drivers/iio/imu/adis_trigger.c +++ b/drivers/iio/imu/adis_trigger.c @@ -60,7 +60,7 @@ int adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev) iio_trigger_set_drvdata(adis->trig, adis); ret = iio_trigger_register(adis->trig); - indio_dev->trig = adis->trig; + indio_dev->trig = iio_trigger_get(adis->trig); if (ret) goto error_free_irq; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index f73e60b..ef76afe 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -410,42 +410,46 @@ error_read_raw: } } -static int inv_mpu6050_write_fsr(struct inv_mpu6050_state *st, int fsr) +static int inv_mpu6050_write_gyro_scale(struct inv_mpu6050_state *st, int val) { - int result; + int result, i; u8 d; - if (fsr < 0 || fsr > INV_MPU6050_MAX_GYRO_FS_PARAM) - return -EINVAL; - if (fsr == st->chip_config.fsr) - return 0; + for (i = 0; i < ARRAY_SIZE(gyro_scale_6050); ++i) { + if (gyro_scale_6050[i] == val) { + d = (i << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT); + result = inv_mpu6050_write_reg(st, + st->reg->gyro_config, d); + if (result) + return result; - d = (fsr << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT); - result = inv_mpu6050_write_reg(st, st->reg->gyro_config, d); - if (result) - return result; - st->chip_config.fsr = fsr; + st->chip_config.fsr = i; + return 0; + } + } - return 0; + return -EINVAL; } -static int inv_mpu6050_write_accel_fs(struct inv_mpu6050_state *st, int fs) +static int inv_mpu6050_write_accel_scale(struct inv_mpu6050_state *st, int val) { - int result; + int result, i; u8 d; - if (fs < 0 || fs > INV_MPU6050_MAX_ACCL_FS_PARAM) - return -EINVAL; - if (fs == st->chip_config.accl_fs) - return 0; + for (i = 0; i < ARRAY_SIZE(accel_scale); ++i) { + if (accel_scale[i] == val) { + d = (i << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT); + result = inv_mpu6050_write_reg(st, + st->reg->accl_config, d); + if (result) + return result; - d = (fs << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT); - result = inv_mpu6050_write_reg(st, st->reg->accl_config, d); - if (result) - return result; - st->chip_config.accl_fs = fs; + st->chip_config.accl_fs = i; + return 0; + } + } - return 0; + return -EINVAL; } static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, @@ -471,10 +475,10 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ANGL_VEL: - result = inv_mpu6050_write_fsr(st, val); + result = inv_mpu6050_write_gyro_scale(st, val2); break; case IIO_ACCEL: - result = inv_mpu6050_write_accel_fs(st, val); + result = inv_mpu6050_write_accel_scale(st, val2); break; default: result = -EINVAL; @@ -780,7 +784,11 @@ static int inv_mpu_probe(struct i2c_client *client, i2c_set_clientdata(client, indio_dev); indio_dev->dev.parent = &client->dev; - indio_dev->name = id->name; + /* id will be NULL when enumerated via ACPI */ + if (id) + indio_dev->name = (char *)id->name; + else + indio_dev->name = (char *)dev_name(&client->dev); indio_dev->channels = inv_mpu_channels; indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index 0cd306a..ba27e27 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -24,6 +24,16 @@ #include <linux/poll.h> #include "inv_mpu_iio.h" +static void inv_clear_kfifo(struct inv_mpu6050_state *st) +{ + unsigned long flags; + + /* take the spin lock sem to avoid interrupt kick in */ + spin_lock_irqsave(&st->time_stamp_lock, flags); + kfifo_reset(&st->timestamps); + spin_unlock_irqrestore(&st->time_stamp_lock, flags); +} + int inv_reset_fifo(struct iio_dev *indio_dev) { int result; @@ -50,6 +60,10 @@ int inv_reset_fifo(struct iio_dev *indio_dev) INV_MPU6050_BIT_FIFO_RST); if (result) goto reset_fifo_fail; + + /* clear timestamps fifo */ + inv_clear_kfifo(st); + /* enable interrupt */ if (st->chip_config.accl_fifo_enable || st->chip_config.gyro_fifo_enable) { @@ -83,16 +97,6 @@ reset_fifo_fail: return result; } -static void inv_clear_kfifo(struct inv_mpu6050_state *st) -{ - unsigned long flags; - - /* take the spin lock sem to avoid interrupt kick in */ - spin_lock_irqsave(&st->time_stamp_lock, flags); - kfifo_reset(&st->timestamps); - spin_unlock_irqrestore(&st->time_stamp_lock, flags); -} - /** * inv_mpu6050_irq_handler() - Cache a timestamp at each data ready interrupt. */ @@ -184,7 +188,6 @@ end_session: flush_fifo: /* Flush HW and SW FIFOs. */ inv_reset_fifo(indio_dev); - inv_clear_kfifo(st); mutex_unlock(&indio_dev->mlock); iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index 5cc3692..b3a3637 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -1227,7 +1227,7 @@ static irqreturn_t kmx61_trigger_handler(int irq, void *p) base = KMX61_MAG_XOUT_L; mutex_lock(&data->lock); - for_each_set_bit(bit, indio_dev->buffer->scan_mask, + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { ret = kmx61_read_measurement(data, base, bit); if (ret < 0) { diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index aaba9d3..4df97f6 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -847,8 +847,7 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, * @attr_list: List of IIO device attributes * * This function frees the memory allocated for each of the IIO device - * attributes in the list. Note: if you want to reuse the list after calling - * this function you have to reinitialize it using INIT_LIST_HEAD(). + * attributes in the list. */ void iio_free_chan_devattr_list(struct list_head *attr_list) { @@ -856,6 +855,7 @@ void iio_free_chan_devattr_list(struct list_head *attr_list) list_for_each_entry_safe(p, n, attr_list, l) { kfree(p->dev_attr.attr.name); + list_del(&p->l); kfree(p); } } @@ -936,6 +936,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev) iio_free_chan_devattr_list(&indio_dev->channel_attr_list); kfree(indio_dev->chan_attr_group.attrs); + indio_dev->chan_attr_group.attrs = NULL; } static void iio_dev_release(struct device *device) diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index a4b3970..a99692b 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -500,6 +500,7 @@ int iio_device_register_eventset(struct iio_dev *indio_dev) error_free_setup_event_lines: iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list); kfree(indio_dev->event_interface); + indio_dev->event_interface = NULL; return ret; } diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index ae68c64..a224afd 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -73,6 +73,7 @@ config CM36651 config GP2AP020A00F tristate "Sharp GP2AP020A00F Proximity/ALS sensor" depends on I2C + select REGMAP_I2C select IIO_BUFFER select IIO_TRIGGERED_BUFFER select IRQ_WORK @@ -126,6 +127,7 @@ config HID_SENSOR_PROX config JSA1212 tristate "JSA1212 ALS and proximity sensor driver" depends on I2C + select REGMAP_I2C help Say Y here if you want to build a IIO driver for JSA1212 proximity & ALS sensor device. diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 4c7a4c5..a5d6de7 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -18,6 +18,8 @@ config AK8975 config AK09911 tristate "Asahi Kasei AK09911 3-axis Compass" + depends on I2C + depends on GPIOLIB select AK8975 help Deprecated: AK09911 is now supported by AK8975 driver. diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 74dff4e..89fca3a 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -494,7 +494,7 @@ static irqreturn_t sx9500_trigger_handler(int irq, void *private) mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->buffer->scan_mask, + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { ret = sx9500_read_proximity(data, &indio_dev->channels[bit], &val); diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index aec7a6a..8c014b5 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -99,6 +99,14 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, if (dmasync) dma_set_attr(DMA_ATTR_WRITE_BARRIER, &attrs); + /* + * If the combination of the addr and size requested for this memory + * region causes an integer overflow, return error. + */ + if ((PAGE_ALIGN(addr + size) <= size) || + (PAGE_ALIGN(addr + size) <= addr)) + return ERR_PTR(-EINVAL); + if (!can_do_mlock()) return ERR_PTR(-EPERM); diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c index c761971..5904026 100644 --- a/drivers/infiniband/hw/mlx4/mad.c +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -64,6 +64,14 @@ enum { #define GUID_TBL_BLK_NUM_ENTRIES 8 #define GUID_TBL_BLK_SIZE (GUID_TBL_ENTRY_SIZE * GUID_TBL_BLK_NUM_ENTRIES) +/* Counters should be saturate once they reach their maximum value */ +#define ASSIGN_32BIT_COUNTER(counter, value) do {\ + if ((value) > U32_MAX) \ + counter = cpu_to_be32(U32_MAX); \ + else \ + counter = cpu_to_be32(value); \ +} while (0) + struct mlx4_mad_rcv_buf { struct ib_grh grh; u8 payload[256]; @@ -806,10 +814,14 @@ static int ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num, static void edit_counter(struct mlx4_counter *cnt, struct ib_pma_portcounters *pma_cnt) { - pma_cnt->port_xmit_data = cpu_to_be32((be64_to_cpu(cnt->tx_bytes)>>2)); - pma_cnt->port_rcv_data = cpu_to_be32((be64_to_cpu(cnt->rx_bytes)>>2)); - pma_cnt->port_xmit_packets = cpu_to_be32(be64_to_cpu(cnt->tx_frames)); - pma_cnt->port_rcv_packets = cpu_to_be32(be64_to_cpu(cnt->rx_frames)); + ASSIGN_32BIT_COUNTER(pma_cnt->port_xmit_data, + (be64_to_cpu(cnt->tx_bytes) >> 2)); + ASSIGN_32BIT_COUNTER(pma_cnt->port_rcv_data, + (be64_to_cpu(cnt->rx_bytes) >> 2)); + ASSIGN_32BIT_COUNTER(pma_cnt->port_xmit_packets, + be64_to_cpu(cnt->tx_frames)); + ASSIGN_32BIT_COUNTER(pma_cnt->port_rcv_packets, + be64_to_cpu(cnt->rx_frames)); } static int iboe_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num, diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index ac6e2b7..b972c0b 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -2697,8 +2697,12 @@ static void handle_bonded_port_state_event(struct work_struct *work) spin_lock_bh(&ibdev->iboe.lock); for (i = 0; i < MLX4_MAX_PORTS; ++i) { struct net_device *curr_netdev = ibdev->iboe.netdevs[i]; + enum ib_port_state curr_port_state; - enum ib_port_state curr_port_state = + if (!curr_netdev) + continue; + + curr_port_state = (netif_running(curr_netdev) && netif_carrier_ok(curr_netdev)) ? IB_PORT_ACTIVE : IB_PORT_DOWN; diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c index 8ff612d..5639325 100644 --- a/drivers/input/keyboard/tc3589x-keypad.c +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -411,9 +411,9 @@ static int tc3589x_keypad_probe(struct platform_device *pdev) input_set_drvdata(input, keypad); - error = request_threaded_irq(irq, NULL, - tc3589x_keypad_irq, plat->irqtype, - "tc3589x-keypad", keypad); + error = request_threaded_irq(irq, NULL, tc3589x_keypad_irq, + plat->irqtype | IRQF_ONESHOT, + "tc3589x-keypad", keypad); if (error < 0) { dev_err(&pdev->dev, "Could not allocate irq %d,error %d\n", diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c index 59d4dcd..9822877 100644 --- a/drivers/input/misc/mma8450.c +++ b/drivers/input/misc/mma8450.c @@ -187,6 +187,7 @@ static int mma8450_probe(struct i2c_client *c, idev->private = m; idev->input->name = MMA8450_DRV_NAME; idev->input->id.bustype = BUS_I2C; + idev->input->dev.parent = &c->dev; idev->poll = mma8450_poll; idev->poll_interval = POLL_INTERVAL; idev->poll_interval_max = POLL_INTERVAL_MAX; diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index d28726a..27bcdbc 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1154,10 +1154,28 @@ out: mutex_unlock(&alps_mutex); } -static void alps_report_bare_ps2_packet(struct input_dev *dev, +static void alps_report_bare_ps2_packet(struct psmouse *psmouse, unsigned char packet[], bool report_buttons) { + struct alps_data *priv = psmouse->private; + struct input_dev *dev; + + /* Figure out which device to use to report the bare packet */ + if (priv->proto_version == ALPS_PROTO_V2 && + (priv->flags & ALPS_DUALPOINT)) { + /* On V2 devices the DualPoint Stick reports bare packets */ + dev = priv->dev2; + } else if (unlikely(IS_ERR_OR_NULL(priv->dev3))) { + /* Register dev3 mouse if we received PS/2 packet first time */ + if (!IS_ERR(priv->dev3)) + psmouse_queue_work(psmouse, &priv->dev3_register_work, + 0); + return; + } else { + dev = priv->dev3; + } + if (report_buttons) alps_report_buttons(dev, NULL, packet[0] & 1, packet[0] & 2, packet[0] & 4); @@ -1232,8 +1250,8 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse) * de-synchronization. */ - alps_report_bare_ps2_packet(priv->dev2, - &psmouse->packet[3], false); + alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3], + false); /* * Continue with the standard ALPS protocol handling, @@ -1289,18 +1307,9 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) * properly we only do this if the device is fully synchronized. */ if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) { - - /* Register dev3 mouse if we received PS/2 packet first time */ - if (unlikely(!priv->dev3)) - psmouse_queue_work(psmouse, - &priv->dev3_register_work, 0); - if (psmouse->pktcnt == 3) { - /* Once dev3 mouse device is registered report data */ - if (likely(!IS_ERR_OR_NULL(priv->dev3))) - alps_report_bare_ps2_packet(priv->dev3, - psmouse->packet, - true); + alps_report_bare_ps2_packet(psmouse, psmouse->packet, + true); return PSMOUSE_FULL_PACKET; } return PSMOUSE_GOOD_DATA; @@ -2281,10 +2290,12 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->set_abs_params = alps_set_abs_params_mt; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; - priv->x_max = 1360; - priv->y_max = 660; priv->x_bits = 23; priv->y_bits = 12; + + if (alps_dolphin_get_device_area(psmouse, priv)) + return -EIO; + break; case ALPS_PROTO_V6: @@ -2303,9 +2314,8 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->set_abs_params = alps_set_abs_params_mt; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; - - if (alps_dolphin_get_device_area(psmouse, priv)) - return -EIO; + priv->x_max = 0xfff; + priv->y_max = 0x7ff; if (priv->fw_ver[1] != 0xba) priv->flags |= ALPS_BUTTONPAD; @@ -2605,8 +2615,10 @@ int alps_detect(struct psmouse *psmouse, bool set_properties) return -ENOMEM; error = alps_identify(psmouse, priv); - if (error) + if (error) { + kfree(priv); return error; + } if (set_properties) { psmouse->vendor = "ALPS"; diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c index 77e9d70..1e2291c 100644 --- a/drivers/input/mouse/cyapa_gen3.c +++ b/drivers/input/mouse/cyapa_gen3.c @@ -20,7 +20,7 @@ #include <linux/input/mt.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/unaligned/access_ok.h> +#include <asm/unaligned.h> #include "cyapa.h" diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c index ddf5393..5b611dd 100644 --- a/drivers/input/mouse/cyapa_gen5.c +++ b/drivers/input/mouse/cyapa_gen5.c @@ -17,7 +17,7 @@ #include <linux/mutex.h> #include <linux/completion.h> #include <linux/slab.h> -#include <linux/unaligned/access_ok.h> +#include <asm/unaligned.h> #include <linux/crc-itu-t.h> #include "cyapa.h" @@ -1926,7 +1926,7 @@ static int cyapa_gen5_read_idac_data(struct cyapa *cyapa, electrodes_tx = cyapa->electrodes_x; max_element_cnt = ((cyapa->aligned_electrodes_rx + 7) & ~7u) * electrodes_tx; - } else if (idac_data_type == GEN5_RETRIEVE_SELF_CAP_PWC_DATA) { + } else { offset = 2; max_element_cnt = cyapa->electrodes_x + cyapa->electrodes_y; diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c index 757f78a..23d2594 100644 --- a/drivers/input/mouse/focaltech.c +++ b/drivers/input/mouse/focaltech.c @@ -67,9 +67,6 @@ static void focaltech_reset(struct psmouse *psmouse) #define FOC_MAX_FINGERS 5 -#define FOC_MAX_X 2431 -#define FOC_MAX_Y 1663 - /* * Current state of a single finger on the touchpad. */ @@ -129,9 +126,17 @@ static void focaltech_report_state(struct psmouse *psmouse) input_mt_slot(dev, i); input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); if (active) { - input_report_abs(dev, ABS_MT_POSITION_X, finger->x); + unsigned int clamped_x, clamped_y; + /* + * The touchpad might report invalid data, so we clamp + * the resulting values so that we do not confuse + * userspace. + */ + clamped_x = clamp(finger->x, 0U, priv->x_max); + clamped_y = clamp(finger->y, 0U, priv->y_max); + input_report_abs(dev, ABS_MT_POSITION_X, clamped_x); input_report_abs(dev, ABS_MT_POSITION_Y, - FOC_MAX_Y - finger->y); + priv->y_max - clamped_y); } } input_mt_report_pointer_emulation(dev, true); @@ -180,16 +185,6 @@ static void focaltech_process_abs_packet(struct psmouse *psmouse, state->pressed = (packet[0] >> 4) & 1; - /* - * packet[5] contains some kind of tool size in the most - * significant nibble. 0xff is a special value (latching) that - * signals a large contact area. - */ - if (packet[5] == 0xff) { - state->fingers[finger].valid = false; - return; - } - state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2]; state->fingers[finger].y = (packet[3] << 8) | packet[4]; state->fingers[finger].valid = true; @@ -381,6 +376,23 @@ static int focaltech_read_size(struct psmouse *psmouse) return 0; } + +void focaltech_set_resolution(struct psmouse *psmouse, unsigned int resolution) +{ + /* not supported yet */ +} + +static void focaltech_set_rate(struct psmouse *psmouse, unsigned int rate) +{ + /* not supported yet */ +} + +static void focaltech_set_scale(struct psmouse *psmouse, + enum psmouse_scale scale) +{ + /* not supported yet */ +} + int focaltech_init(struct psmouse *psmouse) { struct focaltech_data *priv; @@ -415,6 +427,14 @@ int focaltech_init(struct psmouse *psmouse) psmouse->cleanup = focaltech_reset; /* resync is not supported yet */ psmouse->resync_time = 0; + /* + * rate/resolution/scale changes are not supported yet, and + * the generic implementations of these functions seem to + * confuse some touchpads + */ + psmouse->set_resolution = focaltech_set_resolution; + psmouse->set_rate = focaltech_set_rate; + psmouse->set_scale = focaltech_set_scale; return 0; diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 4ccd01d..8bc6123 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -454,6 +454,17 @@ static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) } /* + * Here we set the mouse scaling. + */ + +static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale) +{ + ps2_command(&psmouse->ps2dev, NULL, + scale == PSMOUSE_SCALE21 ? PSMOUSE_CMD_SETSCALE21 : + PSMOUSE_CMD_SETSCALE11); +} + +/* * psmouse_poll() - default poll handler. Everyone except for ALPS uses it. */ @@ -689,6 +700,7 @@ static void psmouse_apply_defaults(struct psmouse *psmouse) psmouse->set_rate = psmouse_set_rate; psmouse->set_resolution = psmouse_set_resolution; + psmouse->set_scale = psmouse_set_scale; psmouse->poll = psmouse_poll; psmouse->protocol_handler = psmouse_process_byte; psmouse->pktsize = 3; @@ -1160,7 +1172,7 @@ static void psmouse_initialize(struct psmouse *psmouse) if (psmouse_max_proto != PSMOUSE_PS2) { psmouse->set_rate(psmouse, psmouse->rate); psmouse->set_resolution(psmouse, psmouse->resolution); - ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); + psmouse->set_scale(psmouse, PSMOUSE_SCALE11); } } diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index c2ff137..d02e1bd 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -36,6 +36,11 @@ typedef enum { PSMOUSE_FULL_PACKET } psmouse_ret_t; +enum psmouse_scale { + PSMOUSE_SCALE11, + PSMOUSE_SCALE21 +}; + struct psmouse { void *private; struct input_dev *dev; @@ -67,6 +72,7 @@ struct psmouse { psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse); void (*set_rate)(struct psmouse *psmouse, unsigned int rate); void (*set_resolution)(struct psmouse *psmouse, unsigned int resolution); + void (*set_scale)(struct psmouse *psmouse, enum psmouse_scale scale); int (*reconnect)(struct psmouse *psmouse); void (*disconnect)(struct psmouse *psmouse); diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index f2cceb6..3b06c8a 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -67,9 +67,6 @@ #define X_MAX_POSITIVE 8176 #define Y_MAX_POSITIVE 8176 -/* maximum ABS_MT_POSITION displacement (in mm) */ -#define DMAX 10 - /***************************************************************************** * Stuff we need even when we do not want native Synaptics support ****************************************************************************/ @@ -123,32 +120,46 @@ void synaptics_reset(struct psmouse *psmouse) static bool cr48_profile_sensor; +#define ANY_BOARD_ID 0 struct min_max_quirk { const char * const *pnp_ids; + struct { + unsigned long int min, max; + } board_id; int x_min, x_max, y_min, y_max; }; static const struct min_max_quirk min_max_pnpid_table[] = { { (const char * const []){"LEN0033", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, 1024, 5052, 2258, 4832 }, { - (const char * const []){"LEN0035", "LEN0042", NULL}, + (const char * const []){"LEN0042", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, 1232, 5710, 1156, 4696 }, { (const char * const []){"LEN0034", "LEN0036", "LEN0037", "LEN0039", "LEN2002", "LEN2004", NULL}, + {ANY_BOARD_ID, 2961}, 1024, 5112, 2024, 4832 }, { (const char * const []){"LEN2001", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, 1024, 5022, 2508, 4832 }, { (const char * const []){"LEN2006", NULL}, + {2691, 2691}, + 1024, 5045, 2457, 4832 + }, + { + (const char * const []){"LEN2006", NULL}, + {ANY_BOARD_ID, ANY_BOARD_ID}, 1264, 5675, 1171, 4688 }, { } @@ -175,9 +186,7 @@ static const char * const topbuttonpad_pnp_ids[] = { "LEN0041", "LEN0042", /* Yoga */ "LEN0045", - "LEN0046", "LEN0047", - "LEN0048", "LEN0049", "LEN2000", "LEN2001", /* Edge E431 */ @@ -185,7 +194,7 @@ static const char * const topbuttonpad_pnp_ids[] = { "LEN2003", "LEN2004", /* L440 */ "LEN2005", - "LEN2006", + "LEN2006", /* Edge E440/E540 */ "LEN2007", "LEN2008", "LEN2009", @@ -235,18 +244,39 @@ static int synaptics_model_id(struct psmouse *psmouse) return 0; } +static int synaptics_more_extended_queries(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + unsigned char buf[3]; + + if (synaptics_send_cmd(psmouse, SYN_QUE_MEXT_CAPAB_10, buf)) + return -1; + + priv->ext_cap_10 = (buf[0]<<16) | (buf[1]<<8) | buf[2]; + + return 0; +} + /* - * Read the board id from the touchpad + * Read the board id and the "More Extended Queries" from the touchpad * The board id is encoded in the "QUERY MODES" response */ -static int synaptics_board_id(struct psmouse *psmouse) +static int synaptics_query_modes(struct psmouse *psmouse) { struct synaptics_data *priv = psmouse->private; unsigned char bid[3]; + /* firmwares prior 7.5 have no board_id encoded */ + if (SYN_ID_FULL(priv->identity) < 0x705) + return 0; + if (synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid)) return -1; priv->board_id = ((bid[0] & 0xfc) << 6) | bid[1]; + + if (SYN_MEXT_CAP_BIT(bid[0])) + return synaptics_more_extended_queries(psmouse); + return 0; } @@ -346,7 +376,6 @@ static int synaptics_resolution(struct psmouse *psmouse) { struct synaptics_data *priv = psmouse->private; unsigned char resp[3]; - int i; if (SYN_ID_MAJOR(priv->identity) < 4) return 0; @@ -358,17 +387,6 @@ static int synaptics_resolution(struct psmouse *psmouse) } } - for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) { - if (psmouse_matches_pnp_id(psmouse, - min_max_pnpid_table[i].pnp_ids)) { - priv->x_min = min_max_pnpid_table[i].x_min; - priv->x_max = min_max_pnpid_table[i].x_max; - priv->y_min = min_max_pnpid_table[i].y_min; - priv->y_max = min_max_pnpid_table[i].y_max; - return 0; - } - } - if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 && SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) { if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MAX_COORDS, resp)) { @@ -377,23 +395,69 @@ static int synaptics_resolution(struct psmouse *psmouse) } else { priv->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); priv->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3); + psmouse_info(psmouse, + "queried max coordinates: x [..%d], y [..%d]\n", + priv->x_max, priv->y_max); } } - if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 && - SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c)) { + if (SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c) && + (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 || + /* + * Firmware v8.1 does not report proper number of extended + * capabilities, but has been proven to report correct min + * coordinates. + */ + SYN_ID_FULL(priv->identity) == 0x801)) { if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MIN_COORDS, resp)) { psmouse_warn(psmouse, "device claims to have min coordinates query, but I'm not able to read it.\n"); } else { priv->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1); priv->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3); + psmouse_info(psmouse, + "queried min coordinates: x [%d..], y [%d..]\n", + priv->x_min, priv->y_min); } } return 0; } +/* + * Apply quirk(s) if the hardware matches + */ + +static void synaptics_apply_quirks(struct psmouse *psmouse) +{ + struct synaptics_data *priv = psmouse->private; + int i; + + for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) { + if (!psmouse_matches_pnp_id(psmouse, + min_max_pnpid_table[i].pnp_ids)) + continue; + + if (min_max_pnpid_table[i].board_id.min != ANY_BOARD_ID && + priv->board_id < min_max_pnpid_table[i].board_id.min) + continue; + + if (min_max_pnpid_table[i].board_id.max != ANY_BOARD_ID && + priv->board_id > min_max_pnpid_table[i].board_id.max) + continue; + + priv->x_min = min_max_pnpid_table[i].x_min; + priv->x_max = min_max_pnpid_table[i].x_max; + priv->y_min = min_max_pnpid_table[i].y_min; + priv->y_max = min_max_pnpid_table[i].y_max; + psmouse_info(psmouse, + "quirked min/max coordinates: x [%d..%d], y [%d..%d]\n", + priv->x_min, priv->x_max, + priv->y_min, priv->y_max); + break; + } +} + static int synaptics_query_hardware(struct psmouse *psmouse) { if (synaptics_identify(psmouse)) @@ -402,13 +466,15 @@ static int synaptics_query_hardware(struct psmouse *psmouse) return -1; if (synaptics_firmware_id(psmouse)) return -1; - if (synaptics_board_id(psmouse)) + if (synaptics_query_modes(psmouse)) return -1; if (synaptics_capability(psmouse)) return -1; if (synaptics_resolution(psmouse)) return -1; + synaptics_apply_quirks(psmouse); + return 0; } @@ -516,18 +582,22 @@ static int synaptics_is_pt_packet(unsigned char *buf) return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4; } -static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet) +static void synaptics_pass_pt_packet(struct psmouse *psmouse, + struct serio *ptport, + unsigned char *packet) { + struct synaptics_data *priv = psmouse->private; struct psmouse *child = serio_get_drvdata(ptport); if (child && child->state == PSMOUSE_ACTIVATED) { - serio_interrupt(ptport, packet[1], 0); + serio_interrupt(ptport, packet[1] | priv->pt_buttons, 0); serio_interrupt(ptport, packet[4], 0); serio_interrupt(ptport, packet[5], 0); if (child->pktsize == 4) serio_interrupt(ptport, packet[2], 0); - } else + } else { serio_interrupt(ptport, packet[1], 0); + } } static void synaptics_pt_activate(struct psmouse *psmouse) @@ -605,6 +675,18 @@ static void synaptics_parse_agm(const unsigned char buf[], } } +static void synaptics_parse_ext_buttons(const unsigned char buf[], + struct synaptics_data *priv, + struct synaptics_hw_state *hw) +{ + unsigned int ext_bits = + (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1; + unsigned int ext_mask = GENMASK(ext_bits - 1, 0); + + hw->ext_buttons = buf[4] & ext_mask; + hw->ext_buttons |= (buf[5] & ext_mask) << ext_bits; +} + static bool is_forcepad; static int synaptics_parse_hw_state(const unsigned char buf[], @@ -691,28 +773,9 @@ static int synaptics_parse_hw_state(const unsigned char buf[], hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0; } - if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) && + if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 0 && ((buf[0] ^ buf[3]) & 0x02)) { - switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) { - default: - /* - * if nExtBtn is greater than 8 it should be - * considered invalid and treated as 0 - */ - break; - case 8: - hw->ext_buttons |= ((buf[5] & 0x08)) ? 0x80 : 0; - hw->ext_buttons |= ((buf[4] & 0x08)) ? 0x40 : 0; - case 6: - hw->ext_buttons |= ((buf[5] & 0x04)) ? 0x20 : 0; - hw->ext_buttons |= ((buf[4] & 0x04)) ? 0x10 : 0; - case 4: - hw->ext_buttons |= ((buf[5] & 0x02)) ? 0x08 : 0; - hw->ext_buttons |= ((buf[4] & 0x02)) ? 0x04 : 0; - case 2: - hw->ext_buttons |= ((buf[5] & 0x01)) ? 0x02 : 0; - hw->ext_buttons |= ((buf[4] & 0x01)) ? 0x01 : 0; - } + synaptics_parse_ext_buttons(buf, priv, hw); } } else { hw->x = (((buf[1] & 0x1f) << 8) | buf[2]); @@ -774,12 +837,54 @@ static void synaptics_report_semi_mt_data(struct input_dev *dev, } } +static void synaptics_report_ext_buttons(struct psmouse *psmouse, + const struct synaptics_hw_state *hw) +{ + struct input_dev *dev = psmouse->dev; + struct synaptics_data *priv = psmouse->private; + int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1; + char buf[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + int i; + + if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) + return; + + /* Bug in FW 8.1, buttons are reported only when ExtBit is 1 */ + if (SYN_ID_FULL(priv->identity) == 0x801 && + !((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02)) + return; + + if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) { + for (i = 0; i < ext_bits; i++) { + input_report_key(dev, BTN_0 + 2 * i, + hw->ext_buttons & (1 << i)); + input_report_key(dev, BTN_1 + 2 * i, + hw->ext_buttons & (1 << (i + ext_bits))); + } + return; + } + + /* + * This generation of touchpads has the trackstick buttons + * physically wired to the touchpad. Re-route them through + * the pass-through interface. + */ + if (!priv->pt_port) + return; + + /* The trackstick expects at most 3 buttons */ + priv->pt_buttons = SYN_CAP_EXT_BUTTON_STICK_L(hw->ext_buttons) | + SYN_CAP_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 | + SYN_CAP_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2; + + synaptics_pass_pt_packet(psmouse, priv->pt_port, buf); +} + static void synaptics_report_buttons(struct psmouse *psmouse, const struct synaptics_hw_state *hw) { struct input_dev *dev = psmouse->dev; struct synaptics_data *priv = psmouse->private; - int i; input_report_key(dev, BTN_LEFT, hw->left); input_report_key(dev, BTN_RIGHT, hw->right); @@ -792,8 +897,7 @@ static void synaptics_report_buttons(struct psmouse *psmouse, input_report_key(dev, BTN_BACK, hw->down); } - for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) - input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i)); + synaptics_report_ext_buttons(psmouse, hw); } static void synaptics_report_mt_data(struct psmouse *psmouse, @@ -813,7 +917,7 @@ static void synaptics_report_mt_data(struct psmouse *psmouse, pos[i].y = synaptics_invert_y(hw[i]->y); } - input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res); + input_mt_assign_slots(dev, slot, pos, nsemi, 0); for (i = 0; i < nsemi; i++) { input_mt_slot(dev, slot[i]); @@ -1014,7 +1118,8 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse) if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) { if (priv->pt_port) - synaptics_pass_pt_packet(priv->pt_port, psmouse->packet); + synaptics_pass_pt_packet(psmouse, priv->pt_port, + psmouse->packet); } else synaptics_process_packet(psmouse); @@ -1116,8 +1221,9 @@ static void set_input_params(struct psmouse *psmouse, __set_bit(BTN_BACK, dev->keybit); } - for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) - __set_bit(BTN_0 + i, dev->keybit); + if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) + for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) + __set_bit(BTN_0 + i, dev->keybit); __clear_bit(EV_REL, dev->evbit); __clear_bit(REL_X, dev->relbit); @@ -1125,7 +1231,8 @@ static void set_input_params(struct psmouse *psmouse, if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); - if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids)) + if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) && + !SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) __set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit); /* Clickpads report only left button */ __clear_bit(BTN_RIGHT, dev->keybit); diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index aedc329..ee4bd0d1 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -22,6 +22,7 @@ #define SYN_QUE_EXT_CAPAB_0C 0x0c #define SYN_QUE_EXT_MAX_COORDS 0x0d #define SYN_QUE_EXT_MIN_COORDS 0x0f +#define SYN_QUE_MEXT_CAPAB_10 0x10 /* synatics modes */ #define SYN_BIT_ABSOLUTE_MODE (1 << 7) @@ -53,6 +54,7 @@ #define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20) #define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12) #define SYN_CAP_PRODUCT_ID(ec) (((ec) & 0xff0000) >> 16) +#define SYN_MEXT_CAP_BIT(m) ((m) & (1 << 1)) /* * The following describes response for the 0x0c query. @@ -89,6 +91,30 @@ #define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400) #define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & 0x000800) +/* + * The following descibes response for the 0x10 query. + * + * byte mask name meaning + * ---- ---- ------- ------------ + * 1 0x01 ext buttons are stick buttons exported in the extended + * capability are actually meant to be used + * by the tracktick (pass-through). + * 1 0x02 SecurePad the touchpad is a SecurePad, so it + * contains a built-in fingerprint reader. + * 1 0xe0 more ext count how many more extented queries are + * available after this one. + * 2 0xff SecurePad width the width of the SecurePad fingerprint + * reader. + * 3 0xff SecurePad height the height of the SecurePad fingerprint + * reader. + */ +#define SYN_CAP_EXT_BUTTONS_STICK(ex10) ((ex10) & 0x010000) +#define SYN_CAP_SECUREPAD(ex10) ((ex10) & 0x020000) + +#define SYN_CAP_EXT_BUTTON_STICK_L(eb) (!!((eb) & 0x01)) +#define SYN_CAP_EXT_BUTTON_STICK_M(eb) (!!((eb) & 0x02)) +#define SYN_CAP_EXT_BUTTON_STICK_R(eb) (!!((eb) & 0x04)) + /* synaptics modes query bits */ #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) #define SYN_MODE_RATE(m) ((m) & (1 << 6)) @@ -143,6 +169,7 @@ struct synaptics_data { unsigned long int capabilities; /* Capabilities */ unsigned long int ext_cap; /* Extended Capabilities */ unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */ + unsigned long int ext_cap_10; /* Ext Caps from 0x10 query */ unsigned long int identity; /* Identification */ unsigned int x_res, y_res; /* X/Y resolution in units/mm */ unsigned int x_max, y_max; /* Max coordinates (from FW) */ @@ -156,6 +183,7 @@ struct synaptics_data { bool disable_gesture; /* disable gestures */ struct serio *pt_port; /* Pass-through serio port */ + unsigned char pt_buttons; /* Pass-through buttons */ /* * Last received Advanced Gesture Mode (AGM) packet. An AGM packet diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 5891752..6261fd6 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -943,6 +943,7 @@ config TOUCHSCREEN_SUN4I tristate "Allwinner sun4i resistive touchscreen controller support" depends on ARCH_SUNXI || COMPILE_TEST depends on HWMON + depends on THERMAL || !THERMAL_OF help This selects support for the resistive touchscreen controller found on Allwinner sunxi SoCs. diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index baa0d97..1ae4e54 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -23,6 +23,7 @@ config IOMMU_IO_PGTABLE config IOMMU_IO_PGTABLE_LPAE bool "ARMv7/v8 Long Descriptor Format" select IOMMU_IO_PGTABLE + depends on ARM || ARM64 || COMPILE_TEST help Enable support for the ARM long descriptor pagetable format. This allocator supports 4K/2M/1G, 16K/32M and 64K/512M page @@ -63,6 +64,7 @@ config MSM_IOMMU bool "MSM IOMMU Support" depends on ARM depends on ARCH_MSM8X60 || ARCH_MSM8960 || COMPILE_TEST + depends on BROKEN select IOMMU_API help Support for the IOMMUs found on certain Qualcomm SOCs. diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index fc13dd5..a3adde6 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1288,10 +1288,13 @@ static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain, return 0; spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags); - if (smmu_domain->smmu->features & ARM_SMMU_FEAT_TRANS_OPS) + if (smmu_domain->smmu->features & ARM_SMMU_FEAT_TRANS_OPS && + smmu_domain->stage == ARM_SMMU_DOMAIN_S1) { ret = arm_smmu_iova_to_phys_hard(domain, iova); - else + } else { ret = ops->iova_to_phys(ops, iova); + } + spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags); return ret; @@ -1556,7 +1559,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) return -ENODEV; } - if (smmu->version == 1 || (!(id & ID0_ATOSNS) && (id & ID0_S1TS))) { + if ((id & ID0_S1TS) && ((smmu->version == 1) || (id & ID0_ATOSNS))) { smmu->features |= ARM_SMMU_FEAT_TRANS_OPS; dev_notice(smmu->dev, "\taddress translation ops\n"); } diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 7ce5273..dc14fec4 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1186,8 +1186,15 @@ static const struct iommu_ops exynos_iommu_ops = { static int __init exynos_iommu_init(void) { + struct device_node *np; int ret; + np = of_find_matching_node(NULL, sysmmu_of_match); + if (!np) + return 0; + + of_node_put(np); + lv2table_kmem_cache = kmem_cache_create("exynos-iommu-lv2table", LV2TABLE_SIZE, LV2TABLE_SIZE, 0, NULL); if (!lv2table_kmem_cache) { diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index ae4c1a8..2d1e05b 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1742,9 +1742,8 @@ static int domain_init(struct dmar_domain *domain, int guest_width) static void domain_exit(struct dmar_domain *domain) { - struct dmar_drhd_unit *drhd; - struct intel_iommu *iommu; struct page *freelist = NULL; + int i; /* Domain 0 is reserved, so dont process it */ if (!domain) @@ -1764,8 +1763,8 @@ static void domain_exit(struct dmar_domain *domain) /* clear attached or cached domains */ rcu_read_lock(); - for_each_active_iommu(iommu, drhd) - iommu_detach_domain(domain, iommu); + for_each_set_bit(i, domain->iommu_bmp, g_num_of_iommus) + iommu_detach_domain(domain, g_iommus[i]); rcu_read_unlock(); dma_free_pagelist(freelist); diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index 5a500ed..b610a8d 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -56,7 +56,8 @@ ((((d)->levels - ((l) - ARM_LPAE_START_LVL(d) + 1)) \ * (d)->bits_per_level) + (d)->pg_shift) -#define ARM_LPAE_PAGES_PER_PGD(d) ((d)->pgd_size >> (d)->pg_shift) +#define ARM_LPAE_PAGES_PER_PGD(d) \ + DIV_ROUND_UP((d)->pgd_size, 1UL << (d)->pg_shift) /* * Calculate the index at level l used to map virtual address a using the @@ -66,7 +67,7 @@ ((l) == ARM_LPAE_START_LVL(d) ? ilog2(ARM_LPAE_PAGES_PER_PGD(d)) : 0) #define ARM_LPAE_LVL_IDX(a,l,d) \ - (((a) >> ARM_LPAE_LVL_SHIFT(l,d)) & \ + (((u64)(a) >> ARM_LPAE_LVL_SHIFT(l,d)) & \ ((1 << ((d)->bits_per_level + ARM_LPAE_PGD_IDX(l,d))) - 1)) /* Calculate the block/page mapping size at level l for pagetable in d. */ diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index 10186ca..bc39bdf 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c @@ -851,6 +851,7 @@ static int ipmmu_remove(struct platform_device *pdev) static const struct of_device_id ipmmu_of_ids[] = { { .compatible = "renesas,ipmmu-vmsa", }, + { } }; static struct platform_driver ipmmu_driver = { diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index af1dc6a..43429ab 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -133,19 +133,25 @@ struct iommu_ops *of_iommu_get_ops(struct device_node *np) return ops; } -struct iommu_ops *of_iommu_configure(struct device *dev) +struct iommu_ops *of_iommu_configure(struct device *dev, + struct device_node *master_np) { struct of_phandle_args iommu_spec; struct device_node *np; struct iommu_ops *ops = NULL; int idx = 0; + if (dev_is_pci(dev)) { + dev_err(dev, "IOMMU is currently not supported for PCI\n"); + return NULL; + } + /* * We don't currently walk up the tree looking for a parent IOMMU. * See the `Notes:' section of * Documentation/devicetree/bindings/iommu/iommu.txt */ - while (!of_parse_phandle_with_args(dev->of_node, "iommus", + while (!of_parse_phandle_with_args(master_np, "iommus", "#iommu-cells", idx, &iommu_spec)) { np = iommu_spec.np; diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c index f59f857..a4ba851 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c @@ -1376,6 +1376,13 @@ static int __init omap_iommu_init(void) struct kmem_cache *p; const unsigned long flags = SLAB_HWCACHE_ALIGN; size_t align = 1 << 10; /* L2 pagetable alignement */ + struct device_node *np; + + np = of_find_matching_node(NULL, omap_iommu_of_match); + if (!np) + return 0; + + of_node_put(np); p = kmem_cache_create("iopte_cache", IOPTE_TABLE_SIZE, align, flags, iopte_cachep_ctor); diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c index 6a8b1ec..9f74fdd 100644 --- a/drivers/iommu/rockchip-iommu.c +++ b/drivers/iommu/rockchip-iommu.c @@ -1015,8 +1015,15 @@ static struct platform_driver rk_iommu_driver = { static int __init rk_iommu_init(void) { + struct device_node *np; int ret; + np = of_find_matching_node(NULL, rk_iommu_dt_ids); + if (!np) + return 0; + + of_node_put(np); + ret = bus_set_iommu(&platform_bus_type, &rk_iommu_ops); if (ret) return ret; diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index cc79d2a..c8d260e 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -110,6 +110,13 @@ config RENESAS_IRQC bool select IRQ_DOMAIN +config ST_IRQCHIP + bool + select REGMAP + select MFD_SYSCON + help + Enables SysCfg Controlled IRQs on STi based platforms. + config TB10X_IRQC bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 42965d2..552a740 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o obj-$(CONFIG_ARCH_MMP) += irq-mmp.o obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o obj-$(CONFIG_ARCH_MXS) += irq-mxs.o +obj-$(CONFIG_ARCH_TEGRA) += irq-tegra.o obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o obj-$(CONFIG_DW_APB_ICTL) += irq-dw-apb-ictl.o obj-$(CONFIG_METAG) += irq-metag-ext.o @@ -33,10 +34,12 @@ obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o +obj-$(CONFIG_ST_IRQCHIP) += irq-st.o obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o +obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 463c235..daccc8b 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -38,6 +38,8 @@ /* Interrupt Controller Registers Map */ #define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48) #define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C) +#define ARMADA_370_XP_INT_FABRIC_MASK_OFFS (0x54) +#define ARMADA_370_XP_INT_CAUSE_PERF(cpu) (1 << cpu) #define ARMADA_370_XP_INT_CONTROL (0x00) #define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30) @@ -56,6 +58,7 @@ #define ARMADA_370_XP_MAX_PER_CPU_IRQS (28) #define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5) +#define ARMADA_370_XP_FABRIC_IRQ (3) #define IPI_DOORBELL_START (0) #define IPI_DOORBELL_END (8) @@ -69,6 +72,7 @@ static void __iomem *per_cpu_int_base; static void __iomem *main_int_base; static struct irq_domain *armada_370_xp_mpic_domain; static u32 doorbell_mask_reg; +static int parent_irq; #ifdef CONFIG_PCI_MSI static struct irq_domain *armada_370_xp_msi_domain; static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); @@ -76,6 +80,17 @@ static DEFINE_MUTEX(msi_used_lock); static phys_addr_t msi_doorbell_addr; #endif +static inline bool is_percpu_irq(irq_hw_number_t irq) +{ + switch (irq) { + case ARMADA_370_XP_TIMER0_PER_CPU_IRQ: + case ARMADA_370_XP_FABRIC_IRQ: + return true; + default: + return false; + } +} + /* * In SMP mode: * For shared global interrupts, mask/unmask global enable bit @@ -85,7 +100,7 @@ static void armada_370_xp_irq_mask(struct irq_data *d) { irq_hw_number_t hwirq = irqd_to_hwirq(d); - if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) + if (!is_percpu_irq(hwirq)) writel(hwirq, main_int_base + ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); else @@ -97,7 +112,7 @@ static void armada_370_xp_irq_unmask(struct irq_data *d) { irq_hw_number_t hwirq = irqd_to_hwirq(d); - if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) + if (!is_percpu_irq(hwirq)) writel(hwirq, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS); else @@ -280,20 +295,21 @@ static struct irq_chip armada_370_xp_irq_chip = { #ifdef CONFIG_SMP .irq_set_affinity = armada_xp_set_affinity, #endif + .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, }; static int armada_370_xp_mpic_irq_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { armada_370_xp_irq_mask(irq_get_irq_data(virq)); - if (hw != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) + if (!is_percpu_irq(hw)) writel(hw, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); else writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS); irq_set_status_flags(virq, IRQ_LEVEL); - if (hw == ARMADA_370_XP_TIMER0_PER_CPU_IRQ) { + if (is_percpu_irq(hw)) { irq_set_percpu_devid(virq); irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, handle_percpu_devid_irq); @@ -307,28 +323,6 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h, return 0; } -#ifdef CONFIG_SMP -static void armada_mpic_send_doorbell(const struct cpumask *mask, - unsigned int irq) -{ - int cpu; - unsigned long map = 0; - - /* Convert our logical CPU mask into a physical one. */ - for_each_cpu(cpu, mask) - map |= 1 << cpu_logical_map(cpu); - - /* - * Ensure that stores to Normal memory are visible to the - * other CPUs before issuing the IPI. - */ - dsb(); - - /* submit softirq */ - writel((map << 8) | irq, main_int_base + - ARMADA_370_XP_SW_TRIG_INT_OFFS); -} - static void armada_xp_mpic_smp_cpu_init(void) { u32 control; @@ -351,11 +345,45 @@ static void armada_xp_mpic_smp_cpu_init(void) writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); } +static void armada_xp_mpic_perf_init(void) +{ + unsigned long cpuid = cpu_logical_map(smp_processor_id()); + + /* Enable Performance Counter Overflow interrupts */ + writel(ARMADA_370_XP_INT_CAUSE_PERF(cpuid), + per_cpu_int_base + ARMADA_370_XP_INT_FABRIC_MASK_OFFS); +} + +#ifdef CONFIG_SMP +static void armada_mpic_send_doorbell(const struct cpumask *mask, + unsigned int irq) +{ + int cpu; + unsigned long map = 0; + + /* Convert our logical CPU mask into a physical one. */ + for_each_cpu(cpu, mask) + map |= 1 << cpu_logical_map(cpu); + + /* + * Ensure that stores to Normal memory are visible to the + * other CPUs before issuing the IPI. + */ + dsb(); + + /* submit softirq */ + writel((map << 8) | irq, main_int_base + + ARMADA_370_XP_SW_TRIG_INT_OFFS); +} + static int armada_xp_mpic_secondary_init(struct notifier_block *nfb, unsigned long action, void *hcpu) { - if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) + if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) { + armada_xp_mpic_perf_init(); armada_xp_mpic_smp_cpu_init(); + } + return NOTIFY_OK; } @@ -364,6 +392,21 @@ static struct notifier_block armada_370_xp_mpic_cpu_notifier = { .priority = 100, }; +static int mpic_cascaded_secondary_init(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) { + armada_xp_mpic_perf_init(); + enable_percpu_irq(parent_irq, IRQ_TYPE_NONE); + } + + return NOTIFY_OK; +} + +static struct notifier_block mpic_cascaded_cpu_notifier = { + .notifier_call = mpic_cascaded_secondary_init, + .priority = 100, +}; #endif /* CONFIG_SMP */ static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { @@ -539,7 +582,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, struct device_node *parent) { struct resource main_int_res, per_cpu_int_res; - int parent_irq, nr_irqs, i; + int nr_irqs, i; u32 control; BUG_ON(of_address_to_resource(node, 0, &main_int_res)); @@ -572,9 +615,9 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, BUG_ON(!armada_370_xp_mpic_domain); -#ifdef CONFIG_SMP + /* Setup for the boot CPU */ + armada_xp_mpic_perf_init(); armada_xp_mpic_smp_cpu_init(); -#endif armada_370_xp_msi_init(node, main_int_res.start); @@ -587,6 +630,9 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, register_cpu_notifier(&armada_370_xp_mpic_cpu_notifier); #endif } else { +#ifdef CONFIG_SMP + register_cpu_notifier(&mpic_cascaded_cpu_notifier); +#endif irq_set_chained_handler(parent_irq, armada_370_xp_mpic_handle_cascade_irq); } diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index bbbaf5d..692fe2b 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -11,11 +11,12 @@ */ #include <linux/err.h> #include <linux/io.h> +#include <linux/irqdomain.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/slab.h> -#include <linux/irqchip/arm-gic.h> -#include <linux/irqchip/irq-crossbar.h> + +#include "irqchip.h" #define IRQ_FREE -1 #define IRQ_RESERVED -2 @@ -24,6 +25,7 @@ /** * struct crossbar_device - crossbar device description + * @lock: spinlock serializing access to @irq_map * @int_max: maximum number of supported interrupts * @safe_map: safe default value to initialize the crossbar * @max_crossbar_sources: Maximum number of crossbar sources @@ -33,6 +35,7 @@ * @write: register write function pointer */ struct crossbar_device { + raw_spinlock_t lock; uint int_max; uint safe_map; uint max_crossbar_sources; @@ -44,72 +47,101 @@ struct crossbar_device { static struct crossbar_device *cb; -static inline void crossbar_writel(int irq_no, int cb_no) +static void crossbar_writel(int irq_no, int cb_no) { writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); } -static inline void crossbar_writew(int irq_no, int cb_no) +static void crossbar_writew(int irq_no, int cb_no) { writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); } -static inline void crossbar_writeb(int irq_no, int cb_no) +static void crossbar_writeb(int irq_no, int cb_no) { writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); } -static inline int get_prev_map_irq(int cb_no) -{ - int i; - - for (i = cb->int_max - 1; i >= 0; i--) - if (cb->irq_map[i] == cb_no) - return i; - - return -ENODEV; -} +static struct irq_chip crossbar_chip = { + .name = "CBAR", + .irq_eoi = irq_chip_eoi_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_wake = irq_chip_set_wake_parent, +#ifdef CONFIG_SMP + .irq_set_affinity = irq_chip_set_affinity_parent, +#endif +}; -static inline int allocate_free_irq(int cb_no) +static int allocate_gic_irq(struct irq_domain *domain, unsigned virq, + irq_hw_number_t hwirq) { + struct of_phandle_args args; int i; + int err; + raw_spin_lock(&cb->lock); for (i = cb->int_max - 1; i >= 0; i--) { if (cb->irq_map[i] == IRQ_FREE) { - cb->irq_map[i] = cb_no; - return i; + cb->irq_map[i] = hwirq; + break; } } + raw_spin_unlock(&cb->lock); - return -ENODEV; -} + if (i < 0) + return -ENODEV; -static inline bool needs_crossbar_write(irq_hw_number_t hw) -{ - int cb_no; + args.np = domain->parent->of_node; + args.args_count = 3; + args.args[0] = 0; /* SPI */ + args.args[1] = i; + args.args[2] = IRQ_TYPE_LEVEL_HIGH; - if (hw > GIC_IRQ_START) { - cb_no = cb->irq_map[hw - GIC_IRQ_START]; - if (cb_no != IRQ_RESERVED && cb_no != IRQ_SKIP) - return true; - } + err = irq_domain_alloc_irqs_parent(domain, virq, 1, &args); + if (err) + cb->irq_map[i] = IRQ_FREE; + else + cb->write(i, hwirq); - return false; + return err; } -static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hw) +static int crossbar_domain_alloc(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs, void *data) { - if (needs_crossbar_write(hw)) - cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); + struct of_phandle_args *args = data; + irq_hw_number_t hwirq; + int i; + + if (args->args_count != 3) + return -EINVAL; /* Not GIC compliant */ + if (args->args[0] != 0) + return -EINVAL; /* No PPI should point to this domain */ + + hwirq = args->args[1]; + if ((hwirq + nr_irqs) > cb->max_crossbar_sources) + return -EINVAL; /* Can't deal with this */ + + for (i = 0; i < nr_irqs; i++) { + int err = allocate_gic_irq(d, virq + i, hwirq + i); + + if (err) + return err; + + irq_domain_set_hwirq_and_chip(d, virq + i, hwirq + i, + &crossbar_chip, NULL); + } return 0; } /** - * crossbar_domain_unmap - unmap a crossbar<->irq connection - * @d: domain of irq to unmap - * @irq: virq number + * crossbar_domain_free - unmap/free a crossbar<->irq connection + * @domain: domain of irq to unmap + * @virq: virq number + * @nr_irqs: number of irqs to free * * We do not maintain a use count of total number of map/unmap * calls for a particular irq to find out if a irq can be really @@ -117,14 +149,20 @@ static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, * after which irq is anyways unusable. So an explicit map has to be called * after that. */ -static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) +static void crossbar_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) { - irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; + int i; - if (needs_crossbar_write(hw)) { - cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; - cb->write(hw - GIC_IRQ_START, cb->safe_map); + raw_spin_lock(&cb->lock); + for (i = 0; i < nr_irqs; i++) { + struct irq_data *d = irq_domain_get_irq_data(domain, virq + i); + + irq_domain_reset_irq_data(d); + cb->irq_map[d->hwirq] = IRQ_FREE; + cb->write(d->hwirq, cb->safe_map); } + raw_spin_unlock(&cb->lock); } static int crossbar_domain_xlate(struct irq_domain *d, @@ -133,44 +171,22 @@ static int crossbar_domain_xlate(struct irq_domain *d, unsigned long *out_hwirq, unsigned int *out_type) { - int ret; - int req_num = intspec[1]; - int direct_map_num; - - if (req_num >= cb->max_crossbar_sources) { - direct_map_num = req_num - cb->max_crossbar_sources; - if (direct_map_num < cb->int_max) { - ret = cb->irq_map[direct_map_num]; - if (ret == IRQ_RESERVED || ret == IRQ_SKIP) { - /* We use the interrupt num as h/w irq num */ - ret = direct_map_num; - goto found; - } - } - - pr_err("%s: requested crossbar number %d > max %d\n", - __func__, req_num, cb->max_crossbar_sources); - return -EINVAL; - } - - ret = get_prev_map_irq(req_num); - if (ret >= 0) - goto found; - - ret = allocate_free_irq(req_num); - - if (ret < 0) - return ret; - -found: - *out_hwirq = ret + GIC_IRQ_START; + if (d->of_node != controller) + return -EINVAL; /* Shouldn't happen, really... */ + if (intsize != 3) + return -EINVAL; /* Not GIC compliant */ + if (intspec[0] != 0) + return -EINVAL; /* No PPI should point to this domain */ + + *out_hwirq = intspec[1]; + *out_type = intspec[2]; return 0; } -static const struct irq_domain_ops routable_irq_domain_ops = { - .map = crossbar_domain_map, - .unmap = crossbar_domain_unmap, - .xlate = crossbar_domain_xlate +static const struct irq_domain_ops crossbar_domain_ops = { + .alloc = crossbar_domain_alloc, + .free = crossbar_domain_free, + .xlate = crossbar_domain_xlate, }; static int __init crossbar_of_init(struct device_node *node) @@ -293,7 +309,8 @@ static int __init crossbar_of_init(struct device_node *node) cb->write(i, cb->safe_map); } - register_routable_domain_ops(&routable_irq_domain_ops); + raw_spin_lock_init(&cb->lock); + return 0; err_reg_offset: @@ -309,18 +326,37 @@ err_cb: return ret; } -static const struct of_device_id crossbar_match[] __initconst = { - { .compatible = "ti,irq-crossbar" }, - {} -}; - -int __init irqcrossbar_init(void) +static int __init irqcrossbar_init(struct device_node *node, + struct device_node *parent) { - struct device_node *np; - np = of_find_matching_node(NULL, crossbar_match); - if (!np) + struct irq_domain *parent_domain, *domain; + int err; + + if (!parent) { + pr_err("%s: no parent, giving up\n", node->full_name); return -ENODEV; + } + + parent_domain = irq_find_host(parent); + if (!parent_domain) { + pr_err("%s: unable to obtain parent domain\n", node->full_name); + return -ENXIO; + } + + err = crossbar_of_init(node); + if (err) + return err; + + domain = irq_domain_add_hierarchy(parent_domain, 0, + cb->max_crossbar_sources, + node, &crossbar_domain_ops, + NULL); + if (!domain) { + pr_err("%s: failed to allocated domain\n", node->full_name); + return -ENOMEM; + } - crossbar_of_init(np); return 0; } + +IRQCHIP_DECLARE(ti_irqcrossbar, "ti,irq-crossbar", irqcrossbar_init); diff --git a/drivers/irqchip/irq-digicolor.c b/drivers/irqchip/irq-digicolor.c index 930a2a2..3cbc658 100644 --- a/drivers/irqchip/irq-digicolor.c +++ b/drivers/irqchip/irq-digicolor.c @@ -55,8 +55,8 @@ static void __exception_irq_entry digicolor_handle_irq(struct pt_regs *regs) } while (1); } -static void digicolor_set_gc(void __iomem *reg_base, unsigned irq_base, - unsigned en_reg, unsigned ack_reg) +static void __init digicolor_set_gc(void __iomem *reg_base, unsigned irq_base, + unsigned en_reg, unsigned ack_reg) { struct irq_chip_generic *gc; diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index d8996bd..9687f8a 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -169,7 +169,7 @@ static void its_encode_cmd(struct its_cmd_block *cmd, u8 cmd_nr) static void its_encode_devid(struct its_cmd_block *cmd, u32 devid) { - cmd->raw_cmd[0] &= ~(0xffffUL << 32); + cmd->raw_cmd[0] &= BIT_ULL(32) - 1; cmd->raw_cmd[0] |= ((u64)devid) << 32; } @@ -416,13 +416,14 @@ static void its_send_single_command(struct its_node *its, { struct its_cmd_block *cmd, *sync_cmd, *next_cmd; struct its_collection *sync_col; + unsigned long flags; - raw_spin_lock(&its->lock); + raw_spin_lock_irqsave(&its->lock, flags); cmd = its_allocate_entry(its); if (!cmd) { /* We're soooooo screewed... */ pr_err_ratelimited("ITS can't allocate, dropping command\n"); - raw_spin_unlock(&its->lock); + raw_spin_unlock_irqrestore(&its->lock, flags); return; } sync_col = builder(cmd, desc); @@ -442,7 +443,7 @@ static void its_send_single_command(struct its_node *its, post: next_cmd = its_post_commands(its); - raw_spin_unlock(&its->lock); + raw_spin_unlock_irqrestore(&its->lock, flags); its_wait_for_range_completion(its, cmd, next_cmd); } @@ -799,21 +800,44 @@ static int its_alloc_tables(struct its_node *its) { int err; int i; - int psz = PAGE_SIZE; + int psz = SZ_64K; u64 shr = GITS_BASER_InnerShareable; + u64 cache = GITS_BASER_WaWb; for (i = 0; i < GITS_BASER_NR_REGS; i++) { u64 val = readq_relaxed(its->base + GITS_BASER + i * 8); u64 type = GITS_BASER_TYPE(val); u64 entry_size = GITS_BASER_ENTRY_SIZE(val); + int order = get_order(psz); + int alloc_size; u64 tmp; void *base; if (type == GITS_BASER_TYPE_NONE) continue; - /* We're lazy and only allocate a single page for now */ - base = (void *)get_zeroed_page(GFP_KERNEL); + /* + * Allocate as many entries as required to fit the + * range of device IDs that the ITS can grok... The ID + * space being incredibly sparse, this results in a + * massive waste of memory. + * + * For other tables, only allocate a single page. + */ + if (type == GITS_BASER_TYPE_DEVICE) { + u64 typer = readq_relaxed(its->base + GITS_TYPER); + u32 ids = GITS_TYPER_DEVBITS(typer); + + order = get_order((1UL << ids) * entry_size); + if (order >= MAX_ORDER) { + order = MAX_ORDER - 1; + pr_warn("%s: Device Table too large, reduce its page order to %u\n", + its->msi_chip.of_node->full_name, order); + } + } + + alloc_size = (1 << order) * PAGE_SIZE; + base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order); if (!base) { err = -ENOMEM; goto out_free; @@ -825,7 +849,7 @@ retry_baser: val = (virt_to_phys(base) | (type << GITS_BASER_TYPE_SHIFT) | ((entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) | - GITS_BASER_WaWb | + cache | shr | GITS_BASER_VALID); @@ -841,7 +865,7 @@ retry_baser: break; } - val |= (PAGE_SIZE / psz) - 1; + val |= (alloc_size / psz) - 1; writeq_relaxed(val, its->base + GITS_BASER + i * 8); tmp = readq_relaxed(its->base + GITS_BASER + i * 8); @@ -851,9 +875,12 @@ retry_baser: * Shareability didn't stick. Just use * whatever the read reported, which is likely * to be the only thing this redistributor - * supports. + * supports. If that's zero, make it + * non-cacheable as well. */ shr = tmp & GITS_BASER_SHAREABILITY_MASK; + if (!shr) + cache = GITS_BASER_nC; goto retry_baser; } @@ -882,7 +909,7 @@ retry_baser: } pr_info("ITS: allocated %d %s @%lx (psz %dK, shr %d)\n", - (int)(PAGE_SIZE / entry_size), + (int)(alloc_size / entry_size), its_base_type_string[type], (unsigned long)virt_to_phys(base), psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT); @@ -957,16 +984,39 @@ static void its_cpu_init_lpis(void) tmp = readq_relaxed(rbase + GICR_PROPBASER); if ((tmp ^ val) & GICR_PROPBASER_SHAREABILITY_MASK) { + if (!(tmp & GICR_PROPBASER_SHAREABILITY_MASK)) { + /* + * The HW reports non-shareable, we must + * remove the cacheability attributes as + * well. + */ + val &= ~(GICR_PROPBASER_SHAREABILITY_MASK | + GICR_PROPBASER_CACHEABILITY_MASK); + val |= GICR_PROPBASER_nC; + writeq_relaxed(val, rbase + GICR_PROPBASER); + } pr_info_once("GIC: using cache flushing for LPI property table\n"); gic_rdists->flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING; } /* set PENDBASE */ val = (page_to_phys(pend_page) | - GICR_PROPBASER_InnerShareable | - GICR_PROPBASER_WaWb); + GICR_PENDBASER_InnerShareable | + GICR_PENDBASER_WaWb); writeq_relaxed(val, rbase + GICR_PENDBASER); + tmp = readq_relaxed(rbase + GICR_PENDBASER); + + if (!(tmp & GICR_PENDBASER_SHAREABILITY_MASK)) { + /* + * The HW reports non-shareable, we must remove the + * cacheability attributes as well. + */ + val &= ~(GICR_PENDBASER_SHAREABILITY_MASK | + GICR_PENDBASER_CACHEABILITY_MASK); + val |= GICR_PENDBASER_nC; + writeq_relaxed(val, rbase + GICR_PENDBASER); + } /* Enable LPIs */ val = readl_relaxed(rbase + GICR_CTLR); @@ -1003,7 +1053,7 @@ static void its_cpu_init_collection(void) * This ITS wants a linear CPU number. */ target = readq_relaxed(gic_data_rdist_rd_base() + GICR_TYPER); - target = GICR_TYPER_CPU_NUMBER(target); + target = GICR_TYPER_CPU_NUMBER(target) << 16; } /* Perform collection mapping */ @@ -1020,8 +1070,9 @@ static void its_cpu_init_collection(void) static struct its_device *its_find_device(struct its_node *its, u32 dev_id) { struct its_device *its_dev = NULL, *tmp; + unsigned long flags; - raw_spin_lock(&its->lock); + raw_spin_lock_irqsave(&its->lock, flags); list_for_each_entry(tmp, &its->its_device_list, entry) { if (tmp->device_id == dev_id) { @@ -1030,7 +1081,7 @@ static struct its_device *its_find_device(struct its_node *its, u32 dev_id) } } - raw_spin_unlock(&its->lock); + raw_spin_unlock_irqrestore(&its->lock, flags); return its_dev; } @@ -1040,6 +1091,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, { struct its_device *dev; unsigned long *lpi_map; + unsigned long flags; void *itt; int lpi_base; int nr_lpis; @@ -1056,7 +1108,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, nr_ites = max(2UL, roundup_pow_of_two(nvecs)); sz = nr_ites * its->ite_size; sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1; - itt = kmalloc(sz, GFP_KERNEL); + itt = kzalloc(sz, GFP_KERNEL); lpi_map = its_lpi_alloc_chunks(nvecs, &lpi_base, &nr_lpis); if (!dev || !itt || !lpi_map) { @@ -1075,9 +1127,9 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, dev->device_id = dev_id; INIT_LIST_HEAD(&dev->entry); - raw_spin_lock(&its->lock); + raw_spin_lock_irqsave(&its->lock, flags); list_add(&dev->entry, &its->its_device_list); - raw_spin_unlock(&its->lock); + raw_spin_unlock_irqrestore(&its->lock, flags); /* Bind the device to the first possible CPU */ cpu = cpumask_first(cpu_online_mask); @@ -1091,9 +1143,11 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, static void its_free_device(struct its_device *its_dev) { - raw_spin_lock(&its_dev->its->lock); + unsigned long flags; + + raw_spin_lock_irqsave(&its_dev->its->lock, flags); list_del(&its_dev->entry); - raw_spin_unlock(&its_dev->its->lock); + raw_spin_unlock_irqrestore(&its_dev->its->lock, flags); kfree(its_dev->itt); kfree(its_dev); } @@ -1112,31 +1166,69 @@ static int its_alloc_device_irq(struct its_device *dev, irq_hw_number_t *hwirq) return 0; } +struct its_pci_alias { + struct pci_dev *pdev; + u32 dev_id; + u32 count; +}; + +static int its_pci_msi_vec_count(struct pci_dev *pdev) +{ + int msi, msix; + + msi = max(pci_msi_vec_count(pdev), 0); + msix = max(pci_msix_vec_count(pdev), 0); + + return max(msi, msix); +} + +static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data) +{ + struct its_pci_alias *dev_alias = data; + + dev_alias->dev_id = alias; + if (pdev != dev_alias->pdev) + dev_alias->count += its_pci_msi_vec_count(dev_alias->pdev); + + return 0; +} + static int its_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec, msi_alloc_info_t *info) { struct pci_dev *pdev; struct its_node *its; - u32 dev_id; struct its_device *its_dev; + struct its_pci_alias dev_alias; if (!dev_is_pci(dev)) return -EINVAL; pdev = to_pci_dev(dev); - dev_id = PCI_DEVID(pdev->bus->number, pdev->devfn); + dev_alias.pdev = pdev; + dev_alias.count = nvec; + + pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias); its = domain->parent->host_data; - its_dev = its_find_device(its, dev_id); - if (WARN_ON(its_dev)) - return -EINVAL; + its_dev = its_find_device(its, dev_alias.dev_id); + if (its_dev) { + /* + * We already have seen this ID, probably through + * another alias (PCI bridge of some sort). No need to + * create the device. + */ + dev_dbg(dev, "Reusing ITT for devID %x\n", dev_alias.dev_id); + goto out; + } - its_dev = its_create_device(its, dev_id, nvec); + its_dev = its_create_device(its, dev_alias.dev_id, dev_alias.count); if (!its_dev) return -ENOMEM; - dev_dbg(&pdev->dev, "ITT %d entries, %d bits\n", nvec, ilog2(nvec)); - + dev_dbg(&pdev->dev, "ITT %d entries, %d bits\n", + dev_alias.count, ilog2(dev_alias.count)); +out: info->scratchpad[0].ptr = its_dev; info->scratchpad[1].ptr = dev; return 0; @@ -1255,6 +1347,34 @@ static const struct irq_domain_ops its_domain_ops = { .deactivate = its_irq_domain_deactivate, }; +static int its_force_quiescent(void __iomem *base) +{ + u32 count = 1000000; /* 1s */ + u32 val; + + val = readl_relaxed(base + GITS_CTLR); + if (val & GITS_CTLR_QUIESCENT) + return 0; + + /* Disable the generation of all interrupts to this ITS */ + val &= ~GITS_CTLR_ENABLE; + writel_relaxed(val, base + GITS_CTLR); + + /* Poll GITS_CTLR and wait until ITS becomes quiescent */ + while (1) { + val = readl_relaxed(base + GITS_CTLR); + if (val & GITS_CTLR_QUIESCENT) + return 0; + + count--; + if (!count) + return -EBUSY; + + cpu_relax(); + udelay(1); + } +} + static int its_probe(struct device_node *node, struct irq_domain *parent) { struct resource res; @@ -1283,6 +1403,13 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) goto out_unmap; } + err = its_force_quiescent(its_base); + if (err) { + pr_warn("%s: failed to quiesce, giving up\n", + node->full_name); + goto out_unmap; + } + pr_info("ITS: %s\n", node->full_name); its = kzalloc(sizeof(*its), GFP_KERNEL); @@ -1322,14 +1449,26 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) writeq_relaxed(baser, its->base + GITS_CBASER); tmp = readq_relaxed(its->base + GITS_CBASER); - writeq_relaxed(0, its->base + GITS_CWRITER); - writel_relaxed(1, its->base + GITS_CTLR); - if ((tmp ^ baser) & GITS_BASER_SHAREABILITY_MASK) { + if ((tmp ^ baser) & GITS_CBASER_SHAREABILITY_MASK) { + if (!(tmp & GITS_CBASER_SHAREABILITY_MASK)) { + /* + * The HW reports non-shareable, we must + * remove the cacheability attributes as + * well. + */ + baser &= ~(GITS_CBASER_SHAREABILITY_MASK | + GITS_CBASER_CACHEABILITY_MASK); + baser |= GITS_CBASER_nC; + writeq_relaxed(baser, its->base + GITS_CBASER); + } pr_info("ITS: using cache flushing for cmd queue\n"); its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING; } + writeq_relaxed(0, its->base + GITS_CWRITER); + writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR); + if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) { its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its); if (!its->domain) { @@ -1382,12 +1521,11 @@ static bool gic_rdists_supports_plpis(void) int its_cpu_init(void) { - if (!gic_rdists_supports_plpis()) { - pr_info("CPU%d: LPIs not supported\n", smp_processor_id()); - return -ENXIO; - } - if (!list_empty(&its_nodes)) { + if (!gic_rdists_supports_plpis()) { + pr_info("CPU%d: LPIs not supported\n", smp_processor_id()); + return -ENXIO; + } its_cpu_init_lpis(); its_cpu_init_collection(); } diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 1c6dea2..4f2fb62 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -195,6 +195,19 @@ static void gic_enable_redist(bool enable) /* * Routines to disable, enable, EOI and route interrupts */ +static int gic_peek_irq(struct irq_data *d, u32 offset) +{ + u32 mask = 1 << (gic_irq(d) % 32); + void __iomem *base; + + if (gic_irq_in_rdist(d)) + base = gic_data_rdist_sgi_base(); + else + base = gic_data.dist_base; + + return !!(readl_relaxed(base + offset + (gic_irq(d) / 32) * 4) & mask); +} + static void gic_poke_irq(struct irq_data *d, u32 offset) { u32 mask = 1 << (gic_irq(d) % 32); @@ -223,6 +236,61 @@ static void gic_unmask_irq(struct irq_data *d) gic_poke_irq(d, GICD_ISENABLER); } +static int gic_irq_set_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, bool val) +{ + u32 reg; + + if (d->hwirq >= gic_data.irq_nr) /* PPI/SPI only */ + return -EINVAL; + + switch (which) { + case IRQCHIP_STATE_PENDING: + reg = val ? GICD_ISPENDR : GICD_ICPENDR; + break; + + case IRQCHIP_STATE_ACTIVE: + reg = val ? GICD_ISACTIVER : GICD_ICACTIVER; + break; + + case IRQCHIP_STATE_MASKED: + reg = val ? GICD_ICENABLER : GICD_ISENABLER; + break; + + default: + return -EINVAL; + } + + gic_poke_irq(d, reg); + return 0; +} + +static int gic_irq_get_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, bool *val) +{ + if (d->hwirq >= gic_data.irq_nr) /* PPI/SPI only */ + return -EINVAL; + + switch (which) { + case IRQCHIP_STATE_PENDING: + *val = gic_peek_irq(d, GICD_ISPENDR); + break; + + case IRQCHIP_STATE_ACTIVE: + *val = gic_peek_irq(d, GICD_ISACTIVER); + break; + + case IRQCHIP_STATE_MASKED: + *val = !gic_peek_irq(d, GICD_ISENABLER); + break; + + default: + return -EINVAL; + } + + return 0; +} + static void gic_eoi_irq(struct irq_data *d) { gic_write_eoir(gic_irq(d)); @@ -418,19 +486,6 @@ static void gic_cpu_init(void) } #ifdef CONFIG_SMP -static int gic_peek_irq(struct irq_data *d, u32 offset) -{ - u32 mask = 1 << (gic_irq(d) % 32); - void __iomem *base; - - if (gic_irq_in_rdist(d)) - base = gic_data_rdist_sgi_base(); - else - base = gic_data.dist_base; - - return !!(readl_relaxed(base + offset + (gic_irq(d) / 32) * 4) & mask); -} - static int gic_secondary_init(struct notifier_block *nfb, unsigned long action, void *hcpu) { @@ -466,7 +521,7 @@ static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask, tlist |= 1 << (mpidr & 0xf); cpu = cpumask_next(cpu, mask); - if (cpu == nr_cpu_ids) + if (cpu >= nr_cpu_ids) goto out; mpidr = cpu_logical_map(cpu); @@ -601,6 +656,8 @@ static struct irq_chip gic_chip = { .irq_eoi = gic_eoi_irq, .irq_set_type = gic_set_type, .irq_set_affinity = gic_set_affinity, + .irq_get_irqchip_state = gic_irq_get_irqchip_state, + .irq_set_irqchip_state = gic_irq_set_irqchip_state, }; #define GIC_ID_NR (1U << gic_data.rdists.id_bits) diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 4634cf7..a6ce347 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -151,26 +151,38 @@ static inline unsigned int gic_irq(struct irq_data *d) /* * Routines to acknowledge, disable and enable interrupts */ -static void gic_mask_irq(struct irq_data *d) +static void gic_poke_irq(struct irq_data *d, u32 offset) { u32 mask = 1 << (gic_irq(d) % 32); + writel_relaxed(mask, gic_dist_base(d) + offset + (gic_irq(d) / 32) * 4); +} - raw_spin_lock(&irq_controller_lock); - writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4); +static int gic_peek_irq(struct irq_data *d, u32 offset) +{ + u32 mask = 1 << (gic_irq(d) % 32); + return !!(readl_relaxed(gic_dist_base(d) + offset + (gic_irq(d) / 32) * 4) & mask); +} + +static void gic_mask_irq(struct irq_data *d) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&irq_controller_lock, flags); + gic_poke_irq(d, GIC_DIST_ENABLE_CLEAR); if (gic_arch_extn.irq_mask) gic_arch_extn.irq_mask(d); - raw_spin_unlock(&irq_controller_lock); + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } static void gic_unmask_irq(struct irq_data *d) { - u32 mask = 1 << (gic_irq(d) % 32); + unsigned long flags; - raw_spin_lock(&irq_controller_lock); + raw_spin_lock_irqsave(&irq_controller_lock, flags); if (gic_arch_extn.irq_unmask) gic_arch_extn.irq_unmask(d); - writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4); - raw_spin_unlock(&irq_controller_lock); + gic_poke_irq(d, GIC_DIST_ENABLE_SET); + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } static void gic_eoi_irq(struct irq_data *d) @@ -184,10 +196,60 @@ static void gic_eoi_irq(struct irq_data *d) writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI); } +static int gic_irq_set_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, bool val) +{ + u32 reg; + + switch (which) { + case IRQCHIP_STATE_PENDING: + reg = val ? GIC_DIST_PENDING_SET : GIC_DIST_PENDING_CLEAR; + break; + + case IRQCHIP_STATE_ACTIVE: + reg = val ? GIC_DIST_ACTIVE_SET : GIC_DIST_ACTIVE_CLEAR; + break; + + case IRQCHIP_STATE_MASKED: + reg = val ? GIC_DIST_ENABLE_CLEAR : GIC_DIST_ENABLE_SET; + break; + + default: + return -EINVAL; + } + + gic_poke_irq(d, reg); + return 0; +} + +static int gic_irq_get_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, bool *val) +{ + switch (which) { + case IRQCHIP_STATE_PENDING: + *val = gic_peek_irq(d, GIC_DIST_PENDING_SET); + break; + + case IRQCHIP_STATE_ACTIVE: + *val = gic_peek_irq(d, GIC_DIST_ACTIVE_SET); + break; + + case IRQCHIP_STATE_MASKED: + *val = !gic_peek_irq(d, GIC_DIST_ENABLE_SET); + break; + + default: + return -EINVAL; + } + + return 0; +} + static int gic_set_type(struct irq_data *d, unsigned int type) { void __iomem *base = gic_dist_base(d); unsigned int gicirq = gic_irq(d); + unsigned long flags; int ret; /* Interrupt configuration for SGIs can't be changed */ @@ -199,14 +261,14 @@ static int gic_set_type(struct irq_data *d, unsigned int type) type != IRQ_TYPE_EDGE_RISING) return -EINVAL; - raw_spin_lock(&irq_controller_lock); + raw_spin_lock_irqsave(&irq_controller_lock, flags); if (gic_arch_extn.irq_set_type) gic_arch_extn.irq_set_type(d, type); ret = gic_configure_irq(gicirq, type, base, NULL); - raw_spin_unlock(&irq_controller_lock); + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); return ret; } @@ -227,6 +289,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3); unsigned int cpu, shift = (gic_irq(d) % 4) * 8; u32 val, mask, bit; + unsigned long flags; if (!force) cpu = cpumask_any_and(mask_val, cpu_online_mask); @@ -236,12 +299,12 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids) return -EINVAL; - raw_spin_lock(&irq_controller_lock); + raw_spin_lock_irqsave(&irq_controller_lock, flags); mask = 0xff << shift; bit = gic_cpu_map[cpu] << shift; val = readl_relaxed(reg) & ~mask; writel_relaxed(val | bit, reg); - raw_spin_unlock(&irq_controller_lock); + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); return IRQ_SET_MASK_OK; } @@ -325,6 +388,8 @@ static struct irq_chip gic_chip = { .irq_set_affinity = gic_set_affinity, #endif .irq_set_wake = gic_set_wake, + .irq_get_irqchip_state = gic_irq_get_irqchip_state, + .irq_set_irqchip_state = gic_irq_set_irqchip_state, }; void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) @@ -349,7 +414,7 @@ static u8 gic_get_cpumask(struct gic_chip_data *gic) break; } - if (!mask) + if (!mask && num_possible_cpus() > 1) pr_crit("GIC CPU mask not found - kernel will fail to boot.\n"); return mask; @@ -798,15 +863,12 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, handle_fasteoi_irq, NULL, NULL); set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); - - gic_routable_irq_domain_ops->map(d, irq, hw); } return 0; } static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq) { - gic_routable_irq_domain_ops->unmap(d, irq); } static int gic_irq_domain_xlate(struct irq_domain *d, @@ -825,16 +887,8 @@ static int gic_irq_domain_xlate(struct irq_domain *d, *out_hwirq = intspec[1] + 16; /* For SPIs, we need to add 16 more to get the GIC irq ID number */ - if (!intspec[0]) { - ret = gic_routable_irq_domain_ops->xlate(d, controller, - intspec, - intsize, - out_hwirq, - out_type); - - if (IS_ERR_VALUE(ret)) - return ret; - } + if (!intspec[0]) + *out_hwirq += 16; *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; @@ -891,37 +945,11 @@ static const struct irq_domain_ops gic_irq_domain_ops = { .xlate = gic_irq_domain_xlate, }; -/* Default functions for routable irq domain */ -static int gic_routable_irq_domain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hw) -{ - return 0; -} - -static void gic_routable_irq_domain_unmap(struct irq_domain *d, - unsigned int irq) +void gic_set_irqchip_flags(unsigned long flags) { + gic_chip.flags |= flags; } -static int gic_routable_irq_domain_xlate(struct irq_domain *d, - struct device_node *controller, - const u32 *intspec, unsigned int intsize, - unsigned long *out_hwirq, - unsigned int *out_type) -{ - *out_hwirq += 16; - return 0; -} - -static const struct irq_domain_ops gic_default_routable_irq_domain_ops = { - .map = gic_routable_irq_domain_map, - .unmap = gic_routable_irq_domain_unmap, - .xlate = gic_routable_irq_domain_xlate, -}; - -const struct irq_domain_ops *gic_routable_irq_domain_ops = - &gic_default_routable_irq_domain_ops; - void __init gic_init_bases(unsigned int gic_nr, int irq_start, void __iomem *dist_base, void __iomem *cpu_base, u32 percpu_offset, struct device_node *node) @@ -929,7 +957,6 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, irq_hw_number_t hwirq_base; struct gic_chip_data *gic; int gic_irqs, irq_base, i; - int nr_routable_irqs; BUG_ON(gic_nr >= MAX_GIC_NR); @@ -985,15 +1012,9 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, gic->gic_irqs = gic_irqs; if (node) { /* DT case */ - const struct irq_domain_ops *ops = &gic_irq_domain_hierarchy_ops; - - if (!of_property_read_u32(node, "arm,routable-irqs", - &nr_routable_irqs)) { - ops = &gic_irq_domain_ops; - gic_irqs = nr_routable_irqs; - } - - gic->domain = irq_domain_add_linear(node, gic_irqs, ops, gic); + gic->domain = irq_domain_add_linear(node, gic_irqs, + &gic_irq_domain_hierarchy_ops, + gic); } else { /* Non-DT case */ /* * For primary GICs, skip over SGIs. diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index 9acdc08..f2d269b 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -166,6 +166,27 @@ cycle_t gic_read_compare(void) return (((cycle_t) hi) << 32) + lo; } + +void gic_start_count(void) +{ + u32 gicconfig; + + /* Start the counter */ + gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG)); + gicconfig &= ~(1 << GIC_SH_CONFIG_COUNTSTOP_SHF); + gic_write(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig); +} + +void gic_stop_count(void) +{ + u32 gicconfig; + + /* Stop the counter */ + gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG)); + gicconfig |= 1 << GIC_SH_CONFIG_COUNTSTOP_SHF; + gic_write(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig); +} + #endif static bool gic_local_irq_is_routable(int intr) diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c index 384e6ed..cdf80b7 100644 --- a/drivers/irqchip/irq-renesas-irqc.c +++ b/drivers/irqchip/irq-renesas-irqc.c @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <linux/clk.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/spinlock.h> @@ -29,15 +30,26 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/platform_data/irq-renesas-irqc.h> +#include <linux/pm_runtime.h> -#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */ +#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */ -#define IRQC_REQ_STS 0x00 -#define IRQC_EN_STS 0x04 -#define IRQC_EN_SET 0x08 +#define IRQC_REQ_STS 0x00 /* Interrupt Request Status Register */ +#define IRQC_EN_STS 0x04 /* Interrupt Enable Status Register */ +#define IRQC_EN_SET 0x08 /* Interrupt Enable Set Register */ #define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10)) -#define DETECT_STATUS 0x100 + /* SYS-CPU vs. RT-CPU */ +#define DETECT_STATUS 0x100 /* IRQn Detect Status Register */ +#define MONITOR 0x104 /* IRQn Signal Level Monitor Register */ +#define HLVL_STS 0x108 /* IRQn High Level Detect Status Register */ +#define LLVL_STS 0x10c /* IRQn Low Level Detect Status Register */ +#define S_R_EDGE_STS 0x110 /* IRQn Sync Rising Edge Detect Status Reg. */ +#define S_F_EDGE_STS 0x114 /* IRQn Sync Falling Edge Detect Status Reg. */ +#define A_R_EDGE_STS 0x118 /* IRQn Async Rising Edge Detect Status Reg. */ +#define A_F_EDGE_STS 0x11c /* IRQn Async Falling Edge Detect Status Reg. */ +#define CHTEN_STS 0x120 /* Chattering Reduction Status Register */ #define IRQC_CONFIG(n) (0x180 + ((n) * 0x04)) + /* IRQn Configuration Register */ struct irqc_irq { int hw_irq; @@ -55,6 +67,7 @@ struct irqc_priv { struct platform_device *pdev; struct irq_chip irq_chip; struct irq_domain *irq_domain; + struct clk *clk; }; static void irqc_dbg(struct irqc_irq *i, char *str) @@ -94,7 +107,7 @@ static int irqc_irq_set_type(struct irq_data *d, unsigned int type) struct irqc_priv *p = irq_data_get_irq_chip_data(d); int hw_irq = irqd_to_hwirq(d); unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK]; - unsigned long tmp; + u32 tmp; irqc_dbg(&p->irq[hw_irq], "sense"); @@ -108,11 +121,26 @@ static int irqc_irq_set_type(struct irq_data *d, unsigned int type) return 0; } +static int irqc_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct irqc_priv *p = irq_data_get_irq_chip_data(d); + + if (!p->clk) + return 0; + + if (on) + clk_enable(p->clk); + else + clk_disable(p->clk); + + return 0; +} + static irqreturn_t irqc_irq_handler(int irq, void *dev_id) { struct irqc_irq *i = dev_id; struct irqc_priv *p = i->p; - unsigned long bit = BIT(i->hw_irq); + u32 bit = BIT(i->hw_irq); irqc_dbg(i, "demux1"); @@ -170,6 +198,15 @@ static int irqc_probe(struct platform_device *pdev) p->pdev = pdev; platform_set_drvdata(pdev, p); + p->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(p->clk)) { + dev_warn(&pdev->dev, "unable to get clock\n"); + p->clk = NULL; + } + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + /* get hold of manadatory IOMEM */ io = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!io) { @@ -210,7 +247,8 @@ static int irqc_probe(struct platform_device *pdev) irq_chip->irq_mask = irqc_irq_disable; irq_chip->irq_unmask = irqc_irq_enable; irq_chip->irq_set_type = irqc_irq_set_type; - irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; + irq_chip->irq_set_wake = irqc_irq_set_wake; + irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND; p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, p->number_of_irqs, @@ -250,6 +288,8 @@ err3: err2: iounmap(p->iomem); err1: + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); kfree(p); err0: return ret; @@ -265,6 +305,8 @@ static int irqc_remove(struct platform_device *pdev) irq_domain_remove(p->irq_domain); iounmap(p->iomem); + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); kfree(p); return 0; } diff --git a/drivers/irqchip/irq-st.c b/drivers/irqchip/irq-st.c new file mode 100644 index 0000000..9af48a8 --- /dev/null +++ b/drivers/irqchip/irq-st.c @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2014 STMicroelectronics – All Rights Reserved + * + * Author: Lee Jones <lee.jones@linaro.org> + * + * This is a re-write of Christophe Kerello's PMU driver. + * + * 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 <dt-bindings/interrupt-controller/irq-st.h> +#include <linux/err.h> +#include <linux/mfd/syscon.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define STIH415_SYSCFG_642 0x0a8 +#define STIH416_SYSCFG_7543 0x87c +#define STIH407_SYSCFG_5102 0x198 +#define STID127_SYSCFG_734 0x088 + +#define ST_A9_IRQ_MASK 0x001FFFFF +#define ST_A9_IRQ_MAX_CHANS 2 + +#define ST_A9_IRQ_EN_CTI_0 BIT(0) +#define ST_A9_IRQ_EN_CTI_1 BIT(1) +#define ST_A9_IRQ_EN_PMU_0 BIT(2) +#define ST_A9_IRQ_EN_PMU_1 BIT(3) +#define ST_A9_IRQ_EN_PL310_L2 BIT(4) +#define ST_A9_IRQ_EN_EXT_0 BIT(5) +#define ST_A9_IRQ_EN_EXT_1 BIT(6) +#define ST_A9_IRQ_EN_EXT_2 BIT(7) + +#define ST_A9_FIQ_N_SEL(dev, chan) (dev << (8 + (chan * 3))) +#define ST_A9_IRQ_N_SEL(dev, chan) (dev << (14 + (chan * 3))) +#define ST_A9_EXTIRQ_INV_SEL(dev) (dev << 20) + +struct st_irq_syscfg { + struct regmap *regmap; + unsigned int syscfg; + unsigned int config; + bool ext_inverted; +}; + +static const struct of_device_id st_irq_syscfg_match[] = { + { + .compatible = "st,stih415-irq-syscfg", + .data = (void *)STIH415_SYSCFG_642, + }, + { + .compatible = "st,stih416-irq-syscfg", + .data = (void *)STIH416_SYSCFG_7543, + }, + { + .compatible = "st,stih407-irq-syscfg", + .data = (void *)STIH407_SYSCFG_5102, + }, + { + .compatible = "st,stid127-irq-syscfg", + .data = (void *)STID127_SYSCFG_734, + }, + {} +}; + +static int st_irq_xlate(struct platform_device *pdev, + int device, int channel, bool irq) +{ + struct st_irq_syscfg *ddata = dev_get_drvdata(&pdev->dev); + + /* Set the device enable bit. */ + switch (device) { + case ST_IRQ_SYSCFG_EXT_0: + ddata->config |= ST_A9_IRQ_EN_EXT_0; + break; + case ST_IRQ_SYSCFG_EXT_1: + ddata->config |= ST_A9_IRQ_EN_EXT_1; + break; + case ST_IRQ_SYSCFG_EXT_2: + ddata->config |= ST_A9_IRQ_EN_EXT_2; + break; + case ST_IRQ_SYSCFG_CTI_0: + ddata->config |= ST_A9_IRQ_EN_CTI_0; + break; + case ST_IRQ_SYSCFG_CTI_1: + ddata->config |= ST_A9_IRQ_EN_CTI_1; + break; + case ST_IRQ_SYSCFG_PMU_0: + ddata->config |= ST_A9_IRQ_EN_PMU_0; + break; + case ST_IRQ_SYSCFG_PMU_1: + ddata->config |= ST_A9_IRQ_EN_PMU_1; + break; + case ST_IRQ_SYSCFG_pl310_L2: + ddata->config |= ST_A9_IRQ_EN_PL310_L2; + break; + case ST_IRQ_SYSCFG_DISABLED: + return 0; + default: + dev_err(&pdev->dev, "Unrecognised device %d\n", device); + return -EINVAL; + } + + /* Select IRQ/FIQ channel for device. */ + ddata->config |= irq ? + ST_A9_IRQ_N_SEL(device, channel) : + ST_A9_FIQ_N_SEL(device, channel); + + return 0; +} + +static int st_irq_syscfg_enable(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct st_irq_syscfg *ddata = dev_get_drvdata(&pdev->dev); + int channels, ret, i; + u32 device, invert; + + channels = of_property_count_u32_elems(np, "st,irq-device"); + if (channels != ST_A9_IRQ_MAX_CHANS) { + dev_err(&pdev->dev, "st,enable-irq-device must have 2 elems\n"); + return -EINVAL; + } + + channels = of_property_count_u32_elems(np, "st,fiq-device"); + if (channels != ST_A9_IRQ_MAX_CHANS) { + dev_err(&pdev->dev, "st,enable-fiq-device must have 2 elems\n"); + return -EINVAL; + } + + for (i = 0; i < ST_A9_IRQ_MAX_CHANS; i++) { + of_property_read_u32_index(np, "st,irq-device", i, &device); + + ret = st_irq_xlate(pdev, device, i, true); + if (ret) + return ret; + + of_property_read_u32_index(np, "st,fiq-device", i, &device); + + ret = st_irq_xlate(pdev, device, i, false); + if (ret) + return ret; + } + + /* External IRQs may be inverted. */ + of_property_read_u32(np, "st,invert-ext", &invert); + ddata->config |= ST_A9_EXTIRQ_INV_SEL(invert); + + return regmap_update_bits(ddata->regmap, ddata->syscfg, + ST_A9_IRQ_MASK, ddata->config); +} + +static int st_irq_syscfg_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; + struct st_irq_syscfg *ddata; + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + match = of_match_device(st_irq_syscfg_match, &pdev->dev); + if (!match) + return -ENODEV; + + ddata->syscfg = (unsigned int)match->data; + + ddata->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(ddata->regmap)) { + dev_err(&pdev->dev, "syscfg phandle missing\n"); + return PTR_ERR(ddata->regmap); + } + + dev_set_drvdata(&pdev->dev, ddata); + + return st_irq_syscfg_enable(pdev); +} + +static int st_irq_syscfg_resume(struct device *dev) +{ + struct st_irq_syscfg *ddata = dev_get_drvdata(dev); + + return regmap_update_bits(ddata->regmap, ddata->syscfg, + ST_A9_IRQ_MASK, ddata->config); +} + +static SIMPLE_DEV_PM_OPS(st_irq_syscfg_pm_ops, NULL, st_irq_syscfg_resume); + +static struct platform_driver st_irq_syscfg_driver = { + .driver = { + .name = "st_irq_syscfg", + .pm = &st_irq_syscfg_pm_ops, + .of_match_table = st_irq_syscfg_match, + }, + .probe = st_irq_syscfg_probe, +}; + +static int __init st_irq_syscfg_init(void) +{ + return platform_driver_register(&st_irq_syscfg_driver); +} +core_initcall(st_irq_syscfg_init); diff --git a/drivers/irqchip/irq-tegra.c b/drivers/irqchip/irq-tegra.c new file mode 100644 index 0000000..51c485d --- /dev/null +++ b/drivers/irqchip/irq-tegra.c @@ -0,0 +1,377 @@ +/* + * Driver code for Tegra's Legacy Interrupt Controller + * + * Author: Marc Zyngier <marc.zyngier@arm.com> + * + * Heavily based on the original arch/arm/mach-tegra/irq.c code: + * Copyright (C) 2011 Google, Inc. + * + * Author: + * Colin Cross <ccross@android.com> + * + * Copyright (C) 2010,2013, NVIDIA Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/io.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/syscore_ops.h> + +#include <dt-bindings/interrupt-controller/arm-gic.h> + +#include "irqchip.h" + +#define ICTLR_CPU_IEP_VFIQ 0x08 +#define ICTLR_CPU_IEP_FIR 0x14 +#define ICTLR_CPU_IEP_FIR_SET 0x18 +#define ICTLR_CPU_IEP_FIR_CLR 0x1c + +#define ICTLR_CPU_IER 0x20 +#define ICTLR_CPU_IER_SET 0x24 +#define ICTLR_CPU_IER_CLR 0x28 +#define ICTLR_CPU_IEP_CLASS 0x2C + +#define ICTLR_COP_IER 0x30 +#define ICTLR_COP_IER_SET 0x34 +#define ICTLR_COP_IER_CLR 0x38 +#define ICTLR_COP_IEP_CLASS 0x3c + +#define TEGRA_MAX_NUM_ICTLRS 6 + +static unsigned int num_ictlrs; + +struct tegra_ictlr_soc { + unsigned int num_ictlrs; +}; + +static const struct tegra_ictlr_soc tegra20_ictlr_soc = { + .num_ictlrs = 4, +}; + +static const struct tegra_ictlr_soc tegra30_ictlr_soc = { + .num_ictlrs = 5, +}; + +static const struct tegra_ictlr_soc tegra210_ictlr_soc = { + .num_ictlrs = 6, +}; + +static const struct of_device_id ictlr_matches[] = { + { .compatible = "nvidia,tegra210-ictlr", .data = &tegra210_ictlr_soc }, + { .compatible = "nvidia,tegra30-ictlr", .data = &tegra30_ictlr_soc }, + { .compatible = "nvidia,tegra20-ictlr", .data = &tegra20_ictlr_soc }, + { } +}; + +struct tegra_ictlr_info { + void __iomem *base[TEGRA_MAX_NUM_ICTLRS]; +#ifdef CONFIG_PM_SLEEP + u32 cop_ier[TEGRA_MAX_NUM_ICTLRS]; + u32 cop_iep[TEGRA_MAX_NUM_ICTLRS]; + u32 cpu_ier[TEGRA_MAX_NUM_ICTLRS]; + u32 cpu_iep[TEGRA_MAX_NUM_ICTLRS]; + + u32 ictlr_wake_mask[TEGRA_MAX_NUM_ICTLRS]; +#endif +}; + +static struct tegra_ictlr_info *lic; + +static inline void tegra_ictlr_write_mask(struct irq_data *d, unsigned long reg) +{ + void __iomem *base = d->chip_data; + u32 mask; + + mask = BIT(d->hwirq % 32); + writel_relaxed(mask, base + reg); +} + +static void tegra_mask(struct irq_data *d) +{ + tegra_ictlr_write_mask(d, ICTLR_CPU_IER_CLR); + irq_chip_mask_parent(d); +} + +static void tegra_unmask(struct irq_data *d) +{ + tegra_ictlr_write_mask(d, ICTLR_CPU_IER_SET); + irq_chip_unmask_parent(d); +} + +static void tegra_eoi(struct irq_data *d) +{ + tegra_ictlr_write_mask(d, ICTLR_CPU_IEP_FIR_CLR); + irq_chip_eoi_parent(d); +} + +static int tegra_retrigger(struct irq_data *d) +{ + tegra_ictlr_write_mask(d, ICTLR_CPU_IEP_FIR_SET); + return irq_chip_retrigger_hierarchy(d); +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_set_wake(struct irq_data *d, unsigned int enable) +{ + u32 irq = d->hwirq; + u32 index, mask; + + index = (irq / 32); + mask = BIT(irq % 32); + if (enable) + lic->ictlr_wake_mask[index] |= mask; + else + lic->ictlr_wake_mask[index] &= ~mask; + + /* + * Do *not* call into the parent, as the GIC doesn't have any + * wake-up facility... + */ + return 0; +} + +static int tegra_ictlr_suspend(void) +{ + unsigned long flags; + unsigned int i; + + local_irq_save(flags); + for (i = 0; i < num_ictlrs; i++) { + void __iomem *ictlr = lic->base[i]; + + /* Save interrupt state */ + lic->cpu_ier[i] = readl_relaxed(ictlr + ICTLR_CPU_IER); + lic->cpu_iep[i] = readl_relaxed(ictlr + ICTLR_CPU_IEP_CLASS); + lic->cop_ier[i] = readl_relaxed(ictlr + ICTLR_COP_IER); + lic->cop_iep[i] = readl_relaxed(ictlr + ICTLR_COP_IEP_CLASS); + + /* Disable COP interrupts */ + writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR); + + /* Disable CPU interrupts */ + writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR); + + /* Enable the wakeup sources of ictlr */ + writel_relaxed(lic->ictlr_wake_mask[i], ictlr + ICTLR_CPU_IER_SET); + } + local_irq_restore(flags); + + return 0; +} + +static void tegra_ictlr_resume(void) +{ + unsigned long flags; + unsigned int i; + + local_irq_save(flags); + for (i = 0; i < num_ictlrs; i++) { + void __iomem *ictlr = lic->base[i]; + + writel_relaxed(lic->cpu_iep[i], + ictlr + ICTLR_CPU_IEP_CLASS); + writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR); + writel_relaxed(lic->cpu_ier[i], + ictlr + ICTLR_CPU_IER_SET); + writel_relaxed(lic->cop_iep[i], + ictlr + ICTLR_COP_IEP_CLASS); + writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR); + writel_relaxed(lic->cop_ier[i], + ictlr + ICTLR_COP_IER_SET); + } + local_irq_restore(flags); +} + +static struct syscore_ops tegra_ictlr_syscore_ops = { + .suspend = tegra_ictlr_suspend, + .resume = tegra_ictlr_resume, +}; + +static void tegra_ictlr_syscore_init(void) +{ + register_syscore_ops(&tegra_ictlr_syscore_ops); +} +#else +#define tegra_set_wake NULL +static inline void tegra_ictlr_syscore_init(void) {} +#endif + +static struct irq_chip tegra_ictlr_chip = { + .name = "LIC", + .irq_eoi = tegra_eoi, + .irq_mask = tegra_mask, + .irq_unmask = tegra_unmask, + .irq_retrigger = tegra_retrigger, + .irq_set_wake = tegra_set_wake, + .flags = IRQCHIP_MASK_ON_SUSPEND, +#ifdef CONFIG_SMP + .irq_set_affinity = irq_chip_set_affinity_parent, +#endif +}; + +static int tegra_ictlr_domain_xlate(struct irq_domain *domain, + struct device_node *controller, + const u32 *intspec, + unsigned int intsize, + unsigned long *out_hwirq, + unsigned int *out_type) +{ + if (domain->of_node != controller) + return -EINVAL; /* Shouldn't happen, really... */ + if (intsize != 3) + return -EINVAL; /* Not GIC compliant */ + if (intspec[0] != GIC_SPI) + return -EINVAL; /* No PPI should point to this domain */ + + *out_hwirq = intspec[1]; + *out_type = intspec[2]; + return 0; +} + +static int tegra_ictlr_domain_alloc(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct of_phandle_args *args = data; + struct of_phandle_args parent_args; + struct tegra_ictlr_info *info = domain->host_data; + irq_hw_number_t hwirq; + unsigned int i; + + if (args->args_count != 3) + return -EINVAL; /* Not GIC compliant */ + if (args->args[0] != GIC_SPI) + return -EINVAL; /* No PPI should point to this domain */ + + hwirq = args->args[1]; + if (hwirq >= (num_ictlrs * 32)) + return -EINVAL; + + for (i = 0; i < nr_irqs; i++) { + int ictlr = (hwirq + i) / 32; + + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &tegra_ictlr_chip, + &info->base[ictlr]); + } + + parent_args = *args; + parent_args.np = domain->parent->of_node; + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args); +} + +static void tegra_ictlr_domain_free(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs) +{ + unsigned int i; + + for (i = 0; i < nr_irqs; i++) { + struct irq_data *d = irq_domain_get_irq_data(domain, virq + i); + irq_domain_reset_irq_data(d); + } +} + +static const struct irq_domain_ops tegra_ictlr_domain_ops = { + .xlate = tegra_ictlr_domain_xlate, + .alloc = tegra_ictlr_domain_alloc, + .free = tegra_ictlr_domain_free, +}; + +static int __init tegra_ictlr_init(struct device_node *node, + struct device_node *parent) +{ + struct irq_domain *parent_domain, *domain; + const struct of_device_id *match; + const struct tegra_ictlr_soc *soc; + unsigned int i; + int err; + + if (!parent) { + pr_err("%s: no parent, giving up\n", node->full_name); + return -ENODEV; + } + + parent_domain = irq_find_host(parent); + if (!parent_domain) { + pr_err("%s: unable to obtain parent domain\n", node->full_name); + return -ENXIO; + } + + match = of_match_node(ictlr_matches, node); + if (!match) /* Should never happen... */ + return -ENODEV; + + soc = match->data; + + lic = kzalloc(sizeof(*lic), GFP_KERNEL); + if (!lic) + return -ENOMEM; + + for (i = 0; i < TEGRA_MAX_NUM_ICTLRS; i++) { + void __iomem *base; + + base = of_iomap(node, i); + if (!base) + break; + + lic->base[i] = base; + + /* Disable all interrupts */ + writel_relaxed(~0UL, base + ICTLR_CPU_IER_CLR); + /* All interrupts target IRQ */ + writel_relaxed(0, base + ICTLR_CPU_IEP_CLASS); + + num_ictlrs++; + } + + if (!num_ictlrs) { + pr_err("%s: no valid regions, giving up\n", node->full_name); + err = -ENOMEM; + goto out_free; + } + + WARN(num_ictlrs != soc->num_ictlrs, + "%s: Found %u interrupt controllers in DT; expected %u.\n", + node->full_name, num_ictlrs, soc->num_ictlrs); + + + domain = irq_domain_add_hierarchy(parent_domain, 0, num_ictlrs * 32, + node, &tegra_ictlr_domain_ops, + lic); + if (!domain) { + pr_err("%s: failed to allocated domain\n", node->full_name); + err = -ENOMEM; + goto out_unmap; + } + + tegra_ictlr_syscore_init(); + + pr_info("%s: %d interrupts forwarded to %s\n", + node->full_name, num_ictlrs * 32, parent->full_name); + + return 0; + +out_unmap: + for (i = 0; i < num_ictlrs; i++) + iounmap(lic->base[i]); +out_free: + kfree(lic); + return err; +} + +IRQCHIP_DECLARE(tegra20_ictlr, "nvidia,tegra20-ictlr", tegra_ictlr_init); +IRQCHIP_DECLARE(tegra30_ictlr, "nvidia,tegra30-ictlr", tegra_ictlr_init); +IRQCHIP_DECLARE(tegra210_ictlr, "nvidia,tegra210-ictlr", tegra_ictlr_init); diff --git a/drivers/irqchip/irq-vf610-mscm-ir.c b/drivers/irqchip/irq-vf610-mscm-ir.c new file mode 100644 index 0000000..9521057 --- /dev/null +++ b/drivers/irqchip/irq-vf610-mscm-ir.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2014-2015 Toradex AG + * Author: Stefan Agner <stefan@agner.ch> + * + * 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. + * + * + * IRQ chip driver for MSCM interrupt router available on Vybrid SoC's. + * The interrupt router is between the CPU's interrupt controller and the + * peripheral. The router allows to route the peripheral interrupts to + * one of the two available CPU's on Vybrid VF6xx SoC's (Cortex-A5 or + * Cortex-M4). The router will be configured transparently on a IRQ + * request. + * + * o All peripheral interrupts of the Vybrid SoC can be routed to + * CPU 0, CPU 1 or both. The routing is useful for dual-core + * variants of Vybrid SoC such as VF6xx. This driver routes the + * requested interrupt to the CPU currently running on. + * + * o It is required to setup the interrupt router even on single-core + * variants of Vybrid. + */ + +#include <linux/cpu_pm.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/mfd/syscon.h> +#include <dt-bindings/interrupt-controller/arm-gic.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/regmap.h> + +#include "irqchip.h" + +#define MSCM_CPxNUM 0x4 + +#define MSCM_IRSPRC(n) (0x80 + 2 * (n)) +#define MSCM_IRSPRC_CPEN_MASK 0x3 + +#define MSCM_IRSPRC_NUM 112 + +struct vf610_mscm_ir_chip_data { + void __iomem *mscm_ir_base; + u16 cpu_mask; + u16 saved_irsprc[MSCM_IRSPRC_NUM]; +}; + +static struct vf610_mscm_ir_chip_data *mscm_ir_data; + +static inline void vf610_mscm_ir_save(struct vf610_mscm_ir_chip_data *data) +{ + int i; + + for (i = 0; i < MSCM_IRSPRC_NUM; i++) + data->saved_irsprc[i] = readw_relaxed(data->mscm_ir_base + MSCM_IRSPRC(i)); +} + +static inline void vf610_mscm_ir_restore(struct vf610_mscm_ir_chip_data *data) +{ + int i; + + for (i = 0; i < MSCM_IRSPRC_NUM; i++) + writew_relaxed(data->saved_irsprc[i], data->mscm_ir_base + MSCM_IRSPRC(i)); +} + +static int vf610_mscm_ir_notifier(struct notifier_block *self, + unsigned long cmd, void *v) +{ + switch (cmd) { + case CPU_CLUSTER_PM_ENTER: + vf610_mscm_ir_save(mscm_ir_data); + break; + case CPU_CLUSTER_PM_ENTER_FAILED: + case CPU_CLUSTER_PM_EXIT: + vf610_mscm_ir_restore(mscm_ir_data); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block mscm_ir_notifier_block = { + .notifier_call = vf610_mscm_ir_notifier, +}; + +static void vf610_mscm_ir_enable(struct irq_data *data) +{ + irq_hw_number_t hwirq = data->hwirq; + struct vf610_mscm_ir_chip_data *chip_data = data->chip_data; + u16 irsprc; + + irsprc = readw_relaxed(chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq)); + irsprc &= MSCM_IRSPRC_CPEN_MASK; + + WARN_ON(irsprc & ~chip_data->cpu_mask); + + writew_relaxed(chip_data->cpu_mask, + chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq)); + + irq_chip_unmask_parent(data); +} + +static void vf610_mscm_ir_disable(struct irq_data *data) +{ + irq_hw_number_t hwirq = data->hwirq; + struct vf610_mscm_ir_chip_data *chip_data = data->chip_data; + + writew_relaxed(0x0, chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq)); + + irq_chip_mask_parent(data); +} + +static struct irq_chip vf610_mscm_ir_irq_chip = { + .name = "mscm-ir", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_enable = vf610_mscm_ir_enable, + .irq_disable = vf610_mscm_ir_disable, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_affinity = irq_chip_set_affinity_parent, +}; + +static int vf610_mscm_ir_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + int i; + irq_hw_number_t hwirq; + struct of_phandle_args *irq_data = arg; + struct of_phandle_args gic_data; + + if (irq_data->args_count != 2) + return -EINVAL; + + hwirq = irq_data->args[0]; + for (i = 0; i < nr_irqs; i++) + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &vf610_mscm_ir_irq_chip, + domain->host_data); + + gic_data.np = domain->parent->of_node; + gic_data.args_count = 3; + gic_data.args[0] = GIC_SPI; + gic_data.args[1] = irq_data->args[0]; + gic_data.args[2] = irq_data->args[1]; + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data); +} + +static const struct irq_domain_ops mscm_irq_domain_ops = { + .xlate = irq_domain_xlate_twocell, + .alloc = vf610_mscm_ir_domain_alloc, + .free = irq_domain_free_irqs_common, +}; + +static int __init vf610_mscm_ir_of_init(struct device_node *node, + struct device_node *parent) +{ + struct irq_domain *domain, *domain_parent; + struct regmap *mscm_cp_regmap; + int ret, cpuid; + + domain_parent = irq_find_host(parent); + if (!domain_parent) { + pr_err("vf610_mscm_ir: interrupt-parent not found\n"); + return -EINVAL; + } + + mscm_ir_data = kzalloc(sizeof(*mscm_ir_data), GFP_KERNEL); + if (!mscm_ir_data) + return -ENOMEM; + + mscm_ir_data->mscm_ir_base = of_io_request_and_map(node, 0, "mscm-ir"); + + if (!mscm_ir_data->mscm_ir_base) { + pr_err("vf610_mscm_ir: unable to map mscm register\n"); + ret = -ENOMEM; + goto out_free; + } + + mscm_cp_regmap = syscon_regmap_lookup_by_phandle(node, "fsl,cpucfg"); + if (IS_ERR(mscm_cp_regmap)) { + ret = PTR_ERR(mscm_cp_regmap); + pr_err("vf610_mscm_ir: regmap lookup for cpucfg failed\n"); + goto out_unmap; + } + + regmap_read(mscm_cp_regmap, MSCM_CPxNUM, &cpuid); + mscm_ir_data->cpu_mask = 0x1 << cpuid; + + domain = irq_domain_add_hierarchy(domain_parent, 0, + MSCM_IRSPRC_NUM, node, + &mscm_irq_domain_ops, mscm_ir_data); + if (!domain) { + ret = -ENOMEM; + goto out_unmap; + } + + cpu_pm_register_notifier(&mscm_ir_notifier_block); + + return 0; + +out_unmap: + iounmap(mscm_ir_data->mscm_ir_base); +out_free: + kfree(mscm_ir_data); + return ret; +} +IRQCHIP_DECLARE(vf610_mscm_ir, "fsl,vf610-mscm-ir", vf610_mscm_ir_of_init); diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index 3c92780..ff48da6 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -1755,7 +1755,7 @@ init_card(struct hfc_pci *hc) enable_hwirq(hc); spin_unlock_irqrestore(&hc->lock, flags); /* Timeout 80ms */ - current->state = TASK_UNINTERRUPTIBLE; + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((80 * HZ) / 1000); printk(KERN_INFO "HFC PCI: IRQ %d count %d\n", hc->irq, hc->irqcnt); diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c index 6a7447c..358a574 100644 --- a/drivers/isdn/icn/icn.c +++ b/drivers/isdn/icn/icn.c @@ -1609,7 +1609,7 @@ icn_setup(char *line) if (ints[0] > 1) membase = (unsigned long)ints[2]; if (str && *str) { - strcpy(sid, str); + strlcpy(sid, str, sizeof(sid)); icn_id = sid; if ((p = strchr(sid, ','))) { *p++ = 0; diff --git a/drivers/lguest/Kconfig b/drivers/lguest/Kconfig index ee035ec..169172d 100644 --- a/drivers/lguest/Kconfig +++ b/drivers/lguest/Kconfig @@ -1,6 +1,6 @@ config LGUEST tristate "Linux hypervisor example code" - depends on X86_32 && EVENTFD && TTY + depends on X86_32 && EVENTFD && TTY && PCI_DIRECT select HVC_DRIVER ---help--- This is a very simple module which allows you to run diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index 37de017..74adcd2 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -289,9 +289,16 @@ static void do_region(int rw, unsigned region, struct dm_io_region *where, struct request_queue *q = bdev_get_queue(where->bdev); unsigned short logical_block_size = queue_logical_block_size(q); sector_t num_sectors; + unsigned int uninitialized_var(special_cmd_max_sectors); - /* Reject unsupported discard requests */ - if ((rw & REQ_DISCARD) && !blk_queue_discard(q)) { + /* + * Reject unsupported discard and write same requests. + */ + if (rw & REQ_DISCARD) + special_cmd_max_sectors = q->limits.max_discard_sectors; + else if (rw & REQ_WRITE_SAME) + special_cmd_max_sectors = q->limits.max_write_same_sectors; + if ((rw & (REQ_DISCARD | REQ_WRITE_SAME)) && special_cmd_max_sectors == 0) { dec_count(io, region, -EOPNOTSUPP); return; } @@ -317,7 +324,7 @@ static void do_region(int rw, unsigned region, struct dm_io_region *where, store_io_and_region_in_bio(bio, io, region); if (rw & REQ_DISCARD) { - num_sectors = min_t(sector_t, q->limits.max_discard_sectors, remaining); + num_sectors = min_t(sector_t, special_cmd_max_sectors, remaining); bio->bi_iter.bi_size = num_sectors << SECTOR_SHIFT; remaining -= num_sectors; } else if (rw & REQ_WRITE_SAME) { @@ -326,7 +333,7 @@ static void do_region(int rw, unsigned region, struct dm_io_region *where, */ dp->get_page(dp, &page, &len, &offset); bio_add_page(bio, page, logical_block_size, offset); - num_sectors = min_t(sector_t, q->limits.max_write_same_sectors, remaining); + num_sectors = min_t(sector_t, special_cmd_max_sectors, remaining); bio->bi_iter.bi_size = num_sectors << SECTOR_SHIFT; offset = 0; diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 8b204ae2..f83a0f3 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -20,6 +20,8 @@ #include <linux/log2.h> #include <linux/dm-kcopyd.h> +#include "dm.h" + #include "dm-exception-store.h" #define DM_MSG_PREFIX "snapshots" @@ -291,12 +293,23 @@ struct origin { }; /* + * This structure is allocated for each origin target + */ +struct dm_origin { + struct dm_dev *dev; + struct dm_target *ti; + unsigned split_boundary; + struct list_head hash_list; +}; + +/* * Size of the hash table for origin volumes. If we make this * the size of the minors list then it should be nearly perfect */ #define ORIGIN_HASH_SIZE 256 #define ORIGIN_MASK 0xFF static struct list_head *_origins; +static struct list_head *_dm_origins; static struct rw_semaphore _origins_lock; static DECLARE_WAIT_QUEUE_HEAD(_pending_exceptions_done); @@ -310,12 +323,22 @@ static int init_origin_hash(void) _origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head), GFP_KERNEL); if (!_origins) { - DMERR("unable to allocate memory"); + DMERR("unable to allocate memory for _origins"); return -ENOMEM; } - for (i = 0; i < ORIGIN_HASH_SIZE; i++) INIT_LIST_HEAD(_origins + i); + + _dm_origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head), + GFP_KERNEL); + if (!_dm_origins) { + DMERR("unable to allocate memory for _dm_origins"); + kfree(_origins); + return -ENOMEM; + } + for (i = 0; i < ORIGIN_HASH_SIZE; i++) + INIT_LIST_HEAD(_dm_origins + i); + init_rwsem(&_origins_lock); return 0; @@ -324,6 +347,7 @@ static int init_origin_hash(void) static void exit_origin_hash(void) { kfree(_origins); + kfree(_dm_origins); } static unsigned origin_hash(struct block_device *bdev) @@ -350,6 +374,30 @@ static void __insert_origin(struct origin *o) list_add_tail(&o->hash_list, sl); } +static struct dm_origin *__lookup_dm_origin(struct block_device *origin) +{ + struct list_head *ol; + struct dm_origin *o; + + ol = &_dm_origins[origin_hash(origin)]; + list_for_each_entry (o, ol, hash_list) + if (bdev_equal(o->dev->bdev, origin)) + return o; + + return NULL; +} + +static void __insert_dm_origin(struct dm_origin *o) +{ + struct list_head *sl = &_dm_origins[origin_hash(o->dev->bdev)]; + list_add_tail(&o->hash_list, sl); +} + +static void __remove_dm_origin(struct dm_origin *o) +{ + list_del(&o->hash_list); +} + /* * _origins_lock must be held when calling this function. * Returns number of snapshots registered using the supplied cow device, plus: @@ -1840,9 +1888,40 @@ static int snapshot_preresume(struct dm_target *ti) static void snapshot_resume(struct dm_target *ti) { struct dm_snapshot *s = ti->private; - struct dm_snapshot *snap_src = NULL, *snap_dest = NULL; + struct dm_snapshot *snap_src = NULL, *snap_dest = NULL, *snap_merging = NULL; + struct dm_origin *o; + struct mapped_device *origin_md = NULL; + bool must_restart_merging = false; down_read(&_origins_lock); + + o = __lookup_dm_origin(s->origin->bdev); + if (o) + origin_md = dm_table_get_md(o->ti->table); + if (!origin_md) { + (void) __find_snapshots_sharing_cow(s, NULL, NULL, &snap_merging); + if (snap_merging) + origin_md = dm_table_get_md(snap_merging->ti->table); + } + if (origin_md == dm_table_get_md(ti->table)) + origin_md = NULL; + if (origin_md) { + if (dm_hold(origin_md)) + origin_md = NULL; + } + + up_read(&_origins_lock); + + if (origin_md) { + dm_internal_suspend_fast(origin_md); + if (snap_merging && test_bit(RUNNING_MERGE, &snap_merging->state_bits)) { + must_restart_merging = true; + stop_merge(snap_merging); + } + } + + down_read(&_origins_lock); + (void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL); if (snap_src && snap_dest) { down_write(&snap_src->lock); @@ -1851,8 +1930,16 @@ static void snapshot_resume(struct dm_target *ti) up_write(&snap_dest->lock); up_write(&snap_src->lock); } + up_read(&_origins_lock); + if (origin_md) { + if (must_restart_merging) + start_merge(snap_merging); + dm_internal_resume_fast(origin_md); + dm_put(origin_md); + } + /* Now we have correct chunk size, reregister */ reregister_snapshot(s); @@ -2133,11 +2220,6 @@ static int origin_write_extent(struct dm_snapshot *merging_snap, * Origin: maps a linear range of a device, with hooks for snapshotting. */ -struct dm_origin { - struct dm_dev *dev; - unsigned split_boundary; -}; - /* * Construct an origin mapping: <dev_path> * The context for an origin is merely a 'struct dm_dev *' @@ -2166,6 +2248,7 @@ static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv) goto bad_open; } + o->ti = ti; ti->private = o; ti->num_flush_bios = 1; @@ -2180,6 +2263,7 @@ bad_alloc: static void origin_dtr(struct dm_target *ti) { struct dm_origin *o = ti->private; + dm_put_device(ti, o->dev); kfree(o); } @@ -2216,6 +2300,19 @@ static void origin_resume(struct dm_target *ti) struct dm_origin *o = ti->private; o->split_boundary = get_origin_minimum_chunksize(o->dev->bdev); + + down_write(&_origins_lock); + __insert_dm_origin(o); + up_write(&_origins_lock); +} + +static void origin_postsuspend(struct dm_target *ti) +{ + struct dm_origin *o = ti->private; + + down_write(&_origins_lock); + __remove_dm_origin(o); + up_write(&_origins_lock); } static void origin_status(struct dm_target *ti, status_type_t type, @@ -2258,12 +2355,13 @@ static int origin_iterate_devices(struct dm_target *ti, static struct target_type origin_target = { .name = "snapshot-origin", - .version = {1, 8, 1}, + .version = {1, 9, 0}, .module = THIS_MODULE, .ctr = origin_ctr, .dtr = origin_dtr, .map = origin_map, .resume = origin_resume, + .postsuspend = origin_postsuspend, .status = origin_status, .merge = origin_merge, .iterate_devices = origin_iterate_devices, @@ -2271,7 +2369,7 @@ static struct target_type origin_target = { static struct target_type snapshot_target = { .name = "snapshot", - .version = {1, 12, 0}, + .version = {1, 13, 0}, .module = THIS_MODULE, .ctr = snapshot_ctr, .dtr = snapshot_dtr, @@ -2285,7 +2383,7 @@ static struct target_type snapshot_target = { static struct target_type merge_target = { .name = dm_snapshot_merge_target_name, - .version = {1, 2, 0}, + .version = {1, 3, 0}, .module = THIS_MODULE, .ctr = snapshot_ctr, .dtr = snapshot_dtr, diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 654773c..921aafd 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -2358,17 +2358,6 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio) return DM_MAPIO_REMAPPED; case -ENODATA: - if (get_pool_mode(tc->pool) == PM_READ_ONLY) { - /* - * This block isn't provisioned, and we have no way - * of doing so. - */ - handle_unserviceable_bio(tc->pool, bio); - cell_defer_no_holder(tc, virt_cell); - return DM_MAPIO_SUBMITTED; - } - /* fall through */ - case -EWOULDBLOCK: thin_defer_cell(tc, virt_cell); return DM_MAPIO_SUBMITTED; diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 73f2880..8001fe9 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -433,7 +433,6 @@ static int dm_blk_open(struct block_device *bdev, fmode_t mode) dm_get(md); atomic_inc(&md->open_count); - out: spin_unlock(&_minor_lock); @@ -442,16 +441,20 @@ out: static void dm_blk_close(struct gendisk *disk, fmode_t mode) { - struct mapped_device *md = disk->private_data; + struct mapped_device *md; spin_lock(&_minor_lock); + md = disk->private_data; + if (WARN_ON(!md)) + goto out; + if (atomic_dec_and_test(&md->open_count) && (test_bit(DMF_DEFERRED_REMOVE, &md->flags))) queue_work(deferred_remove_workqueue, &deferred_remove_work); dm_put(md); - +out: spin_unlock(&_minor_lock); } @@ -2241,7 +2244,6 @@ static void free_dev(struct mapped_device *md) int minor = MINOR(disk_devt(md->disk)); unlock_fs(md); - bdput(md->bdev); destroy_workqueue(md->wq); if (md->kworker_task) @@ -2252,19 +2254,22 @@ static void free_dev(struct mapped_device *md) mempool_destroy(md->rq_pool); if (md->bs) bioset_free(md->bs); - blk_integrity_unregister(md->disk); - del_gendisk(md->disk); + cleanup_srcu_struct(&md->io_barrier); free_table_devices(&md->table_devices); - free_minor(minor); + dm_stats_cleanup(&md->stats); spin_lock(&_minor_lock); md->disk->private_data = NULL; spin_unlock(&_minor_lock); - + if (blk_get_integrity(md->disk)) + blk_integrity_unregister(md->disk); + del_gendisk(md->disk); put_disk(md->disk); blk_cleanup_queue(md->queue); - dm_stats_cleanup(&md->stats); + bdput(md->bdev); + free_minor(minor); + module_put(THIS_MODULE); kfree(md); } @@ -2616,6 +2621,19 @@ void dm_get(struct mapped_device *md) BUG_ON(test_bit(DMF_FREEING, &md->flags)); } +int dm_hold(struct mapped_device *md) +{ + spin_lock(&_minor_lock); + if (test_bit(DMF_FREEING, &md->flags)) { + spin_unlock(&_minor_lock); + return -EBUSY; + } + dm_get(md); + spin_unlock(&_minor_lock); + return 0; +} +EXPORT_SYMBOL_GPL(dm_hold); + const char *dm_device_name(struct mapped_device *md) { return md->name; @@ -2629,8 +2647,9 @@ static void __dm_destroy(struct mapped_device *md, bool wait) might_sleep(); - spin_lock(&_minor_lock); map = dm_get_live_table(md, &srcu_idx); + + spin_lock(&_minor_lock); idr_replace(&_minor_idr, MINOR_ALLOCED, MINOR(disk_devt(dm_disk(md)))); set_bit(DMF_FREEING, &md->flags); spin_unlock(&_minor_lock); @@ -2638,10 +2657,16 @@ static void __dm_destroy(struct mapped_device *md, bool wait) if (dm_request_based(md)) flush_kthread_worker(&md->kworker); + /* + * Take suspend_lock so that presuspend and postsuspend methods + * do not race with internal suspend. + */ + mutex_lock(&md->suspend_lock); if (!dm_suspended_md(md)) { dm_table_presuspend_targets(map); dm_table_postsuspend_targets(map); } + mutex_unlock(&md->suspend_lock); /* dm_put_live_table must be before msleep, otherwise deadlock is possible */ dm_put_live_table(md, srcu_idx); @@ -3115,6 +3140,7 @@ void dm_internal_suspend_fast(struct mapped_device *md) flush_workqueue(md->wq); dm_wait_for_completion(md, TASK_UNINTERRUPTIBLE); } +EXPORT_SYMBOL_GPL(dm_internal_suspend_fast); void dm_internal_resume_fast(struct mapped_device *md) { @@ -3126,6 +3152,7 @@ void dm_internal_resume_fast(struct mapped_device *md) done: mutex_unlock(&md->suspend_lock); } +EXPORT_SYMBOL_GPL(dm_internal_resume_fast); /*----------------------------------------------------------------- * Event notification. diff --git a/drivers/md/md.c b/drivers/md/md.c index c8d2bac..e617878 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -249,6 +249,7 @@ static void md_make_request(struct request_queue *q, struct bio *bio) const int rw = bio_data_dir(bio); struct mddev *mddev = q->queuedata; unsigned int sectors; + int cpu; if (mddev == NULL || mddev->pers == NULL || !mddev->ready) { @@ -284,7 +285,10 @@ static void md_make_request(struct request_queue *q, struct bio *bio) sectors = bio_sectors(bio); mddev->pers->make_request(mddev, bio); - generic_start_io_acct(rw, sectors, &mddev->gendisk->part0); + cpu = part_stat_lock(); + part_stat_inc(cpu, &mddev->gendisk->part0, ios[rw]); + part_stat_add(cpu, &mddev->gendisk->part0, sectors[rw], sectors); + part_stat_unlock(); if (atomic_dec_and_test(&mddev->active_io) && mddev->suspended) wake_up(&mddev->sb_wait); @@ -2555,7 +2559,7 @@ state_store(struct md_rdev *rdev, const char *buf, size_t len) return err ? err : len; } static struct rdev_sysfs_entry rdev_state = -__ATTR(state, S_IRUGO|S_IWUSR, state_show, state_store); +__ATTR_PREALLOC(state, S_IRUGO|S_IWUSR, state_show, state_store); static ssize_t errors_show(struct md_rdev *rdev, char *page) @@ -3638,7 +3642,8 @@ resync_start_store(struct mddev *mddev, const char *buf, size_t len) return err ?: len; } static struct md_sysfs_entry md_resync_start = -__ATTR(resync_start, S_IRUGO|S_IWUSR, resync_start_show, resync_start_store); +__ATTR_PREALLOC(resync_start, S_IRUGO|S_IWUSR, + resync_start_show, resync_start_store); /* * The array state can be: @@ -3851,7 +3856,7 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len) return err ?: len; } static struct md_sysfs_entry md_array_state = -__ATTR(array_state, S_IRUGO|S_IWUSR, array_state_show, array_state_store); +__ATTR_PREALLOC(array_state, S_IRUGO|S_IWUSR, array_state_show, array_state_store); static ssize_t max_corrected_read_errors_show(struct mddev *mddev, char *page) { @@ -4101,7 +4106,7 @@ out_unlock: } static struct md_sysfs_entry md_metadata = -__ATTR(metadata_version, S_IRUGO|S_IWUSR, metadata_show, metadata_store); +__ATTR_PREALLOC(metadata_version, S_IRUGO|S_IWUSR, metadata_show, metadata_store); static ssize_t action_show(struct mddev *mddev, char *page) @@ -4189,7 +4194,7 @@ action_store(struct mddev *mddev, const char *page, size_t len) } static struct md_sysfs_entry md_scan_mode = -__ATTR(sync_action, S_IRUGO|S_IWUSR, action_show, action_store); +__ATTR_PREALLOC(sync_action, S_IRUGO|S_IWUSR, action_show, action_store); static ssize_t last_sync_action_show(struct mddev *mddev, char *page) @@ -4335,7 +4340,8 @@ sync_completed_show(struct mddev *mddev, char *page) return sprintf(page, "%llu / %llu\n", resync, max_sectors); } -static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed); +static struct md_sysfs_entry md_sync_completed = + __ATTR_PREALLOC(sync_completed, S_IRUGO, sync_completed_show, NULL); static ssize_t min_sync_show(struct mddev *mddev, char *page) @@ -5078,7 +5084,8 @@ int md_run(struct mddev *mddev) } if (err) { mddev_detach(mddev); - pers->free(mddev, mddev->private); + if (mddev->private) + pers->free(mddev, mddev->private); module_put(pers->owner); bitmap_destroy(mddev); return err; diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index a13f738..3b5d7f7 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -313,7 +313,7 @@ static struct strip_zone *find_zone(struct r0conf *conf, /* * remaps the bio to the target device. we separate two flows. - * power 2 flow and a general flow for the sake of perfromance + * power 2 flow and a general flow for the sake of performance */ static struct md_rdev *map_sector(struct mddev *mddev, struct strip_zone *zone, sector_t sector, sector_t *sector_offset) @@ -467,8 +467,6 @@ static int raid0_run(struct mddev *mddev) dump_zones(mddev); ret = md_integrity_register(mddev); - if (ret) - raid0_free(mddev, conf); return ret; } @@ -526,6 +524,7 @@ static void raid0_make_request(struct mddev *mddev, struct bio *bio) split = bio; } + sector = bio->bi_iter.bi_sector; zone = find_zone(mddev->private, §or); tmp_dev = map_sector(mddev, zone, sector, §or); split->bi_bdev = tmp_dev->bdev; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 4153da5..d34e238 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -560,7 +560,7 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect if (test_bit(WriteMostly, &rdev->flags)) { /* Don't balance among write-mostly, just * use the first as a last resort */ - if (best_disk < 0) { + if (best_dist_disk < 0) { if (is_badblock(rdev, this_sector, sectors, &first_bad, &bad_sectors)) { if (first_bad < this_sector) @@ -569,7 +569,8 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect best_good_sectors = first_bad - this_sector; } else best_good_sectors = sectors; - best_disk = disk; + best_dist_disk = disk; + best_pending_disk = disk; } continue; } diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index e75d48c..cd2f96b 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -5121,12 +5121,17 @@ static inline sector_t sync_request(struct mddev *mddev, sector_t sector_nr, int schedule_timeout_uninterruptible(1); } /* Need to check if array will still be degraded after recovery/resync - * We don't need to check the 'failed' flag as when that gets set, - * recovery aborts. + * Note in case of > 1 drive failures it's possible we're rebuilding + * one drive while leaving another faulty drive in array. */ - for (i = 0; i < conf->raid_disks; i++) - if (conf->disks[i].rdev == NULL) + rcu_read_lock(); + for (i = 0; i < conf->raid_disks; i++) { + struct md_rdev *rdev = ACCESS_ONCE(conf->disks[i].rdev); + + if (rdev == NULL || test_bit(Faulty, &rdev->flags)) still_degraded = 1; + } + rcu_read_unlock(); bitmap_start_sync(mddev->bitmap, sector_nr, &sync_blocks, still_degraded); diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 5d2d8f4..67faa8d 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -1240,7 +1240,7 @@ static int rtl2832_probe(struct i2c_client *client, dev->regmap_config.max_register = 5 * 0x100, dev->regmap_config.ranges = regmap_range_cfg, dev->regmap_config.num_ranges = ARRAY_SIZE(regmap_range_cfg), - dev->regmap_config.cache_type = REGCACHE_RBTREE, + dev->regmap_config.cache_type = REGCACHE_NONE, dev->regmap = regmap_init(&client->dev, ®map_bus, client, &dev->regmap_config); if (IS_ERR(dev->regmap)) { diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c index e4901a5..63c0ee5 100644 --- a/drivers/media/pci/cx23885/cx23885-417.c +++ b/drivers/media/pci/cx23885/cx23885-417.c @@ -1339,14 +1339,13 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(cap->driver, dev->name, sizeof(cap->driver)); strlcpy(cap->card, cx23885_boards[tsport->dev->board].name, sizeof(cap->card)); - sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); - cap->capabilities = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - 0; + sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; if (dev->tuner_type != TUNER_ABSENT) - cap->capabilities |= V4L2_CAP_TUNER; + cap->device_caps |= V4L2_CAP_TUNER; + cap->capabilities = cap->device_caps | V4L2_CAP_VBI_CAPTURE | + V4L2_CAP_AUDIO | V4L2_CAP_DEVICE_CAPS; return 0; } diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 12f7452..a92ff42 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1845,6 +1845,9 @@ static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) struct s5p_jpeg_addr jpeg_addr; u32 pix_size, padding_bytes = 0; + jpeg_addr.cb = 0; + jpeg_addr.cr = 0; + pix_size = ctx->cap_q.w * ctx->cap_q.h; if (ctx->mode == S5P_JPEG_ENCODE) { diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c index e8c2cad..0974b9a 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c @@ -20,7 +20,7 @@ void exynos3250_jpeg_reset(void __iomem *regs) { - u32 reg = 0; + u32 reg = 1; int count = 1000; writel(1, regs + EXYNOS3250_SW_RESET); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 8e44a59..98374e8 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -833,6 +833,7 @@ static int s5p_mfc_open(struct file *file) q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; q->io_modes = VB2_MMAP; q->drv_priv = &ctx->fh; + q->lock = &dev->mfc_mutex; if (vdev == dev->vfd_dec) { q->io_modes = VB2_MMAP; q->ops = get_dec_queue_ops(); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 15f7663..24262bb 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -29,7 +29,7 @@ /* Offset base used to differentiate between CAPTURE and OUTPUT * while mmaping */ -#define DST_QUEUE_OFF_BASE (TASK_SIZE / 2) +#define DST_QUEUE_OFF_BASE (1 << 30) #define MFC_BANK1_ALLOC_CTX 0 #define MFC_BANK2_ALLOC_CTX 1 diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h index de2b8c6..22dfb3e 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h @@ -302,7 +302,7 @@ struct s5p_mfc_hw_ops { void (*write_info)(struct s5p_mfc_ctx *ctx, unsigned int data, unsigned int ofs); unsigned int (*read_info)(struct s5p_mfc_ctx *ctx, - unsigned int ofs); + unsigned long ofs); int (*get_dspl_y_adr)(struct s5p_mfc_dev *dev); int (*get_dec_y_adr)(struct s5p_mfc_dev *dev); int (*get_dspl_status)(struct s5p_mfc_dev *dev); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index 0c4fcf2..b09bcd1 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -263,15 +263,15 @@ static void s5p_mfc_release_dev_context_buffer_v5(struct s5p_mfc_dev *dev) static void s5p_mfc_write_info_v5(struct s5p_mfc_ctx *ctx, unsigned int data, unsigned int ofs) { - writel(data, (volatile void __iomem *)(ctx->shm.virt + ofs)); + writel(data, (void *)(ctx->shm.virt + ofs)); wmb(); } static unsigned int s5p_mfc_read_info_v5(struct s5p_mfc_ctx *ctx, - unsigned int ofs) + unsigned long ofs) { rmb(); - return readl((volatile void __iomem *)(ctx->shm.virt + ofs)); + return readl((void *)(ctx->shm.virt + ofs)); } static void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index d826c58..cefad18 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -1852,17 +1852,17 @@ static void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data, unsigned int ofs) { s5p_mfc_clock_on(); - writel(data, (volatile void __iomem *)((unsigned long)ofs)); + writel(data, (void *)((unsigned long)ofs)); s5p_mfc_clock_off(); } static unsigned int -s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs) +s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned long ofs) { int ret; s5p_mfc_clock_on(); - ret = readl((volatile void __iomem *)((unsigned long)ofs)); + ret = readl((void *)ofs); s5p_mfc_clock_off(); return ret; diff --git a/drivers/media/platform/s5p-tv/Kconfig b/drivers/media/platform/s5p-tv/Kconfig index 5a1835d..697aaed 100644 --- a/drivers/media/platform/s5p-tv/Kconfig +++ b/drivers/media/platform/s5p-tv/Kconfig @@ -20,6 +20,7 @@ if VIDEO_SAMSUNG_S5P_TV config VIDEO_SAMSUNG_S5P_HDMI tristate "Samsung HDMI Driver" depends on VIDEO_V4L2 + depends on I2C depends on VIDEO_SAMSUNG_S5P_TV select VIDEO_SAMSUNG_S5P_HDMIPHY help diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index a901b62..2554f37 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -1158,6 +1158,7 @@ static int sh_veu_probe(struct platform_device *pdev) } *vdev = sh_veu_videodev; + vdev->v4l2_dev = &veu->v4l2_dev; spin_lock_init(&veu->lock); mutex_init(&veu->fop_lock); vdev->lock = &veu->fop_lock; diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 8526bf5..c835beb 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -843,6 +843,8 @@ static int isi_camera_set_bus_param(struct soc_camera_device *icd) if (isi->pdata.full_mode) cfg1 |= ISI_CFG1_FULL_MODE; + cfg1 |= ISI_CFG1_THMASK_BEATS_16; + isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); isi_writel(isi, ISI_CFG1, cfg1); diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index cee7b56..66634b4 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1665,7 +1665,7 @@ eclkreg: eaddpdev: platform_device_put(sasc->pdev); eallocpdev: - devm_kfree(ici->v4l2_dev.dev, sasc); + devm_kfree(ici->v4l2_dev.dev, info); dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); return ret; diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 77dcfdf..87fc0fe 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -780,8 +780,6 @@ static int rtl2832u_frontend_callback(void *adapter_priv, int component, case TUNER_RTL2832_TUA9001: return rtl2832u_tua9001_tuner_callback(d, cmd, arg); } - default: - return -EINVAL; } return 0; diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig index 60af3b1..3fd94fe 100644 --- a/drivers/media/usb/gspca/Kconfig +++ b/drivers/media/usb/gspca/Kconfig @@ -1,6 +1,7 @@ menuconfig USB_GSPCA tristate "GSPCA based webcams" depends on VIDEO_V4L2 + depends on INPUT || INPUT=n default m ---help--- Say Y here if you want to enable selecting webcams based diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index bc08a82..cc16e76 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -3230,18 +3230,13 @@ int vb2_thread_stop(struct vb2_queue *q) if (threadio == NULL) return 0; - call_void_qop(q, wait_finish, q); threadio->stop = true; - vb2_internal_streamoff(q, q->type); - call_void_qop(q, wait_prepare, q); + /* Wake up all pending sleeps in the thread */ + vb2_queue_error(q); err = kthread_stop(threadio->thread); - q->fileio = NULL; - fileio->req.count = 0; - vb2_reqbufs(q, &fileio->req); - kfree(fileio); + __vb2_cleanup_fileio(q); threadio->thread = NULL; kfree(threadio); - q->fileio = NULL; q->threadio = NULL; return err; } diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index b481d20..69e0483 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -632,8 +632,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, } /* extract page list from userspace mapping */ - ret = vb2_dc_get_user_pages(start, pages, n_pages, vma, - dma_dir == DMA_FROM_DEVICE); + ret = vb2_dc_get_user_pages(start, pages, n_pages, vma, dma_dir); if (ret) { unsigned long pfn; if (vb2_dc_get_user_pfn(start, n_pages, vma, &pfn) == 0) { diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index cfff0b6..0d18256 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -49,7 +49,9 @@ static void ab8500_power_off(void) if (!psy) continue; - ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val); + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, + &val); + power_supply_put(psy); if (!ret && val.intval) { charger_present = true; @@ -63,8 +65,8 @@ static void ab8500_power_off(void) /* Check if battery is known */ psy = power_supply_get_by_name("ab8500_btemp"); if (psy) { - ret = psy->get_property(psy, POWER_SUPPLY_PROP_TECHNOLOGY, - &val); + ret = power_supply_get_property(psy, + POWER_SUPPLY_PROP_TECHNOLOGY, &val); if (!ret && val.intval != POWER_SUPPLY_TECHNOLOGY_UNKNOWN) { printk(KERN_INFO "Charger \"%s\" is connected with known battery." @@ -72,6 +74,7 @@ static void ab8500_power_off(void) pss[i]); machine_restart("charging"); } + power_supply_put(psy); } shutdown: diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index b1b580a..0acbe52 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -87,7 +87,7 @@ static struct resource axp20x_pek_resources[] = { }, }; -static struct resource axp288_battery_resources[] = { +static struct resource axp288_fuel_gauge_resources[] = { { .start = AXP288_IRQ_QWBTU, .end = AXP288_IRQ_QWBTU, @@ -350,9 +350,9 @@ static struct mfd_cell axp288_cells[] = { .resources = axp288_charger_resources, }, { - .name = "axp288_battery", - .num_resources = ARRAY_SIZE(axp288_battery_resources), - .resources = axp288_battery_resources, + .name = "axp288_fuel_gauge", + .num_resources = ARRAY_SIZE(axp288_fuel_gauge_resources), + .resources = axp288_fuel_gauge_resources, }, { .name = "axp288_pmic_acpi", diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c index f38ec42..5615522 100644 --- a/drivers/mfd/kempld-core.c +++ b/drivers/mfd/kempld-core.c @@ -739,7 +739,7 @@ static int __init kempld_init(void) for (id = kempld_dmi_table; id->matches[0].slot != DMI_NONE; id++) if (strstr(id->ident, force_device_id)) - if (id->callback && id->callback(id)) + if (id->callback && !id->callback(id)) break; if (id->matches[0].slot == DMI_NONE) return -ENODEV; diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c index ede5024..dbd907d 100644 --- a/drivers/mfd/rtsx_usb.c +++ b/drivers/mfd/rtsx_usb.c @@ -196,18 +196,27 @@ EXPORT_SYMBOL_GPL(rtsx_usb_ep0_write_register); int rtsx_usb_ep0_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) { u16 value; + u8 *buf; + int ret; if (!data) return -EINVAL; - *data = 0; + + buf = kzalloc(sizeof(u8), GFP_KERNEL); + if (!buf) + return -ENOMEM; addr |= EP0_READ_REG_CMD << EP0_OP_SHIFT; value = swab16(addr); - return usb_control_msg(ucr->pusb_dev, + ret = usb_control_msg(ucr->pusb_dev, usb_rcvctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, 0, data, 1, 100); + value, 0, buf, 1, 100); + *data = *buf; + + kfree(buf); + return ret; } EXPORT_SYMBOL_GPL(rtsx_usb_ep0_read_register); @@ -288,18 +297,27 @@ static int rtsx_usb_get_status_with_bulk(struct rtsx_ucr *ucr, u16 *status) int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status) { int ret; + u16 *buf; if (!status) return -EINVAL; - if (polling_pipe == 0) + if (polling_pipe == 0) { + buf = kzalloc(sizeof(u16), GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = usb_control_msg(ucr->pusb_dev, usb_rcvctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_POLL, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, 0, status, 2, 100); - else + 0, 0, buf, 2, 100); + *status = *buf; + + kfree(buf); + } else { ret = rtsx_usb_get_status_with_bulk(ucr, status); + } /* usb_control_msg may return positive when success */ if (ret < 0) diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 38552a3..65fed71 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -202,16 +202,17 @@ static void enclosure_remove_links(struct enclosure_component *cdev) { char name[ENCLOSURE_NAME_SIZE]; + enclosure_link_name(cdev, name); + /* * In odd circumstances, like multipath devices, something else may * already have removed the links, so check for this condition first. */ - if (!cdev->dev->kobj.sd) - return; + if (cdev->dev->kobj.sd) + sysfs_remove_link(&cdev->dev->kobj, name); - enclosure_link_name(cdev, name); - sysfs_remove_link(&cdev->dev->kobj, name); - sysfs_remove_link(&cdev->cdev.kobj, "device"); + if (cdev->cdev.kobj.sd) + sysfs_remove_link(&cdev->cdev.kobj, "device"); } static int enclosure_add_links(struct enclosure_component *cdev) diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 9306219..6ad049a 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -341,6 +341,8 @@ void mei_stop(struct mei_device *dev) dev->dev_state = MEI_DEV_POWER_DOWN; mei_reset(dev); + /* move device to disabled state unconditionally */ + dev->dev_state = MEI_DEV_DISABLED; mutex_unlock(&dev->device_lock); diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c index 82dc5748..7f32712 100644 --- a/drivers/misc/sgi-xp/xpc_main.c +++ b/drivers/misc/sgi-xp/xpc_main.c @@ -1210,7 +1210,7 @@ xpc_system_die(struct notifier_block *nb, unsigned long event, void *_die_args) if (((die_args->trapnr == X86_TRAP_MF) || (die_args->trapnr == X86_TRAP_XF)) && - !user_mode_vm(die_args->regs)) + !user_mode(die_args->regs)) xpc_die_deactivate(); break; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 23f10f7..c296bc0 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -897,6 +897,7 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) DECLARE_WAITQUEUE(wait, current); unsigned long flags; int stop; + bool pm = false; might_sleep(); @@ -916,15 +917,18 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) host->claimed = 1; host->claimer = current; host->claim_cnt += 1; + if (host->claim_cnt == 1) + pm = true; } else wake_up(&host->wq); spin_unlock_irqrestore(&host->lock, flags); remove_wait_queue(&host->wq, &wait); - if (host->ops->enable && !stop && host->claim_cnt == 1) - host->ops->enable(host); + + if (pm) + pm_runtime_get_sync(mmc_dev(host)); + return stop; } - EXPORT_SYMBOL(__mmc_claim_host); /** @@ -940,9 +944,6 @@ void mmc_release_host(struct mmc_host *host) WARN_ON(!host->claimed); - if (host->ops->disable && host->claim_cnt == 1) - host->ops->disable(host); - spin_lock_irqsave(&host->lock, flags); if (--host->claim_cnt) { /* Release for nested claim */ @@ -952,6 +953,8 @@ void mmc_release_host(struct mmc_host *host) host->claimer = NULL; spin_unlock_irqrestore(&host->lock, flags); wake_up(&host->wq); + pm_runtime_mark_last_busy(mmc_dev(host)); + pm_runtime_put_autosuspend(mmc_dev(host)); } } EXPORT_SYMBOL(mmc_release_host); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 1d41e85..c84131e 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -11,6 +11,7 @@ */ #include <linux/err.h> +#include <linux/of.h> #include <linux/slab.h> #include <linux/stat.h> #include <linux/pm_runtime.h> @@ -336,6 +337,8 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) { int err = 0, idx; unsigned int part_size; + struct device_node *np; + bool broken_hpi = false; /* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */ card->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE]; @@ -349,6 +352,11 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) } } + np = mmc_of_find_child_device(card->host, 0); + if (np && of_device_is_compatible(np, "mmc-card")) + broken_hpi = of_property_read_bool(np, "broken-hpi"); + of_node_put(np); + /* * The EXT_CSD format is meant to be forward compatible. As long * as CSD_STRUCTURE does not change, all values for EXT_CSD_REV @@ -494,7 +502,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) } /* check whether the eMMC card supports HPI */ - if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) { + if (!broken_hpi && (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1)) { card->ext_csd.hpi = 1; if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2) card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION; diff --git a/drivers/mmc/core/pwrseq.c b/drivers/mmc/core/pwrseq.c index 8623561..ab21297 100644 --- a/drivers/mmc/core/pwrseq.c +++ b/drivers/mmc/core/pwrseq.c @@ -19,7 +19,7 @@ struct mmc_pwrseq_match { const char *compatible; - int (*alloc)(struct mmc_host *host, struct device *dev); + struct mmc_pwrseq *(*alloc)(struct mmc_host *host, struct device *dev); }; static struct mmc_pwrseq_match pwrseq_match[] = { @@ -52,6 +52,7 @@ int mmc_pwrseq_alloc(struct mmc_host *host) struct platform_device *pdev; struct device_node *np; struct mmc_pwrseq_match *match; + struct mmc_pwrseq *pwrseq; int ret = 0; np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0); @@ -70,9 +71,14 @@ int mmc_pwrseq_alloc(struct mmc_host *host) goto err; } - ret = match->alloc(host, &pdev->dev); - if (!ret) - dev_info(host->parent, "allocated mmc-pwrseq\n"); + pwrseq = match->alloc(host, &pdev->dev); + if (IS_ERR(pwrseq)) { + ret = PTR_ERR(host->pwrseq); + goto err; + } + + host->pwrseq = pwrseq; + dev_info(host->parent, "allocated mmc-pwrseq\n"); err: of_node_put(np); @@ -109,4 +115,6 @@ void mmc_pwrseq_free(struct mmc_host *host) if (pwrseq && pwrseq->ops && pwrseq->ops->free) pwrseq->ops->free(host); + + host->pwrseq = NULL; } diff --git a/drivers/mmc/core/pwrseq.h b/drivers/mmc/core/pwrseq.h index aba3409..096da48 100644 --- a/drivers/mmc/core/pwrseq.h +++ b/drivers/mmc/core/pwrseq.h @@ -27,8 +27,10 @@ void mmc_pwrseq_post_power_on(struct mmc_host *host); void mmc_pwrseq_power_off(struct mmc_host *host); void mmc_pwrseq_free(struct mmc_host *host); -int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev); -int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev); +struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host, + struct device *dev); +struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host, + struct device *dev); #else diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c index a2d5459..9d6d2fb 100644 --- a/drivers/mmc/core/pwrseq_emmc.c +++ b/drivers/mmc/core/pwrseq_emmc.c @@ -49,7 +49,6 @@ static void mmc_pwrseq_emmc_free(struct mmc_host *host) unregister_restart_handler(&pwrseq->reset_nb); gpiod_put(pwrseq->reset_gpio); kfree(pwrseq); - host->pwrseq = NULL; } static struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = { @@ -67,14 +66,15 @@ static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this, return NOTIFY_DONE; } -int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev) +struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host, + struct device *dev) { struct mmc_pwrseq_emmc *pwrseq; int ret = 0; pwrseq = kzalloc(sizeof(struct mmc_pwrseq_emmc), GFP_KERNEL); if (!pwrseq) - return -ENOMEM; + return ERR_PTR(-ENOMEM); pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_LOW); if (IS_ERR(pwrseq->reset_gpio)) { @@ -92,10 +92,9 @@ int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev) register_restart_handler(&pwrseq->reset_nb); pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops; - host->pwrseq = &pwrseq->pwrseq; - return 0; + return &pwrseq->pwrseq; free: kfree(pwrseq); - return ret; + return ERR_PTR(ret); } diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index e9f1d8d..0b14b83 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -85,7 +85,6 @@ static void mmc_pwrseq_simple_free(struct mmc_host *host) clk_put(pwrseq->ext_clk); kfree(pwrseq); - host->pwrseq = NULL; } static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = { @@ -95,7 +94,8 @@ static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = { .free = mmc_pwrseq_simple_free, }; -int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev) +struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host, + struct device *dev) { struct mmc_pwrseq_simple *pwrseq; int i, nr_gpios, ret = 0; @@ -107,7 +107,7 @@ int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev) pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple) + nr_gpios * sizeof(struct gpio_desc *), GFP_KERNEL); if (!pwrseq) - return -ENOMEM; + return ERR_PTR(-ENOMEM); pwrseq->ext_clk = clk_get(dev, "ext_clock"); if (IS_ERR(pwrseq->ext_clk) && @@ -124,7 +124,7 @@ int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev) PTR_ERR(pwrseq->reset_gpios[i]) != -ENOSYS) { ret = PTR_ERR(pwrseq->reset_gpios[i]); - while (--i) + while (i--) gpiod_put(pwrseq->reset_gpios[i]); goto clk_put; @@ -133,13 +133,12 @@ int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev) pwrseq->nr_gpios = nr_gpios; pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops; - host->pwrseq = &pwrseq->pwrseq; - return 0; + return &pwrseq->pwrseq; clk_put: if (!IS_ERR(pwrseq->ext_clk)) clk_put(pwrseq->ext_clk); free: kfree(pwrseq); - return ret; + return ERR_PTR(ret); } diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index ce6cc47..5bc6c7d 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -293,19 +293,22 @@ static int sdio_enable_4bit_bus(struct mmc_card *card) int err; if (card->type == MMC_TYPE_SDIO) - return sdio_enable_wide(card); - - if ((card->host->caps & MMC_CAP_4_BIT_DATA) && - (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { + err = sdio_enable_wide(card); + else if ((card->host->caps & MMC_CAP_4_BIT_DATA) && + (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); if (err) return err; + err = sdio_enable_wide(card); + if (err <= 0) + mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1); } else return 0; - err = sdio_enable_wide(card); - if (err <= 0) - mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1); + if (err > 0) { + mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); + err = 0; + } return err; } @@ -547,13 +550,8 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card) /* * Switch to wider bus (if supported). */ - if (card->host->caps & MMC_CAP_4_BIT_DATA) { + if (card->host->caps & MMC_CAP_4_BIT_DATA) err = sdio_enable_4bit_bus(card); - if (err > 0) { - mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); - err = 0; - } - } /* Set the driver strength for the card */ sdio_select_driver_type(card); @@ -803,9 +801,7 @@ try_again: * Switch to wider bus (if supported). */ err = sdio_enable_4bit_bus(card); - if (err > 0) - mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); - else if (err) + if (err) goto remove; } finish: @@ -983,10 +979,6 @@ static int mmc_sdio_resume(struct mmc_host *host) } else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { /* We may have switched to 1-bit mode during suspend */ err = sdio_enable_4bit_bus(host->card); - if (err > 0) { - mmc_set_bus_width(host, MMC_BUS_WIDTH_4); - err = 0; - } } if (!err && host->sdio_irqs) { diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 61ac63a..7f4db90 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -132,7 +132,7 @@ config MMC_SDHCI_OF_ARASAN config MMC_SDHCI_OF_ESDHC tristate "SDHCI OF support for the Freescale eSDHC controller" depends on MMC_SDHCI_PLTFM - depends on PPC_OF + depends on PPC select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER help This selects the Freescale eSDHC controller support. @@ -144,7 +144,7 @@ config MMC_SDHCI_OF_ESDHC config MMC_SDHCI_OF_HLWD tristate "SDHCI OF support for the Nintendo Wii SDHCI controllers" depends on MMC_SDHCI_PLTFM - depends on PPC_OF + depends on PPC select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER help This selects the Secure Digital Host Controller Interface (SDHCI) @@ -230,7 +230,7 @@ config MMC_SDHCI_PXAV3 tristate "Marvell MMP2 SD Host Controller support (PXAV3)" depends on CLKDEV_LOOKUP depends on MMC_SDHCI_PLTFM - depends on ARCH_MMP || COMPILE_TEST + depends on ARCH_BERLIN || ARCH_MMP || ARCH_MVEBU || COMPILE_TEST default CPU_MMP2 help This selects the Marvell(R) PXAV3 SD Host Controller. @@ -255,6 +255,7 @@ config MMC_SDHCI_PXAV2 config MMC_SDHCI_SPEAR tristate "SDHCI support on ST SPEAr platform" depends on MMC_SDHCI && PLAT_SPEAR + depends on OF help This selects the Secure Digital Host Controller Interface (SDHCI) often referrered to as the HSMMC block in some of the ST SPEAR range @@ -307,6 +308,20 @@ config MMC_SDHCI_F_SDH30 If unsure, say N. +config MMC_SDHCI_IPROC + tristate "SDHCI platform support for the iProc SD/MMC Controller" + depends on ARCH_BCM_IPROC || COMPILE_TEST + depends on MMC_SDHCI_PLTFM + default ARCH_BCM_IPROC + select MMC_SDHCI_IO_ACCESSORS + help + This selects the iProc SD/MMC controller. + + If you have an IPROC platform with SD or MMC devices, + say Y or M here. + + If unsure, say N. + config MMC_MOXART tristate "MOXART SD/MMC Host Controller support" depends on ARCH_MOXART && MMC diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 6a7cfe0..711e913 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o +obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o diff --git a/drivers/mmc/host/atmel-mci-regs.h b/drivers/mmc/host/atmel-mci-regs.h index c97001e..0aa44e6 100644 --- a/drivers/mmc/host/atmel-mci-regs.h +++ b/drivers/mmc/host/atmel-mci-regs.h @@ -135,10 +135,17 @@ #define ATMCI_REGS_SIZE 0x100 /* Register access macros */ -#define atmci_readl(port,reg) \ +#ifdef CONFIG_AVR32 +#define atmci_readl(port, reg) \ __raw_readl((port)->regs + reg) -#define atmci_writel(port,reg,value) \ +#define atmci_writel(port, reg, value) \ __raw_writel((value), (port)->regs + reg) +#else +#define atmci_readl(port, reg) \ + readl_relaxed((port)->regs + reg) +#define atmci_writel(port, reg, value) \ + writel_relaxed((value), (port)->regs + reg) +#endif /* On AVR chips the Peripheral DMA Controller is not connected to MCI. */ #ifdef CONFIG_AVR32 diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index fe32948..e761eb1 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -40,7 +40,12 @@ struct dw_mci_exynos_priv_data { u8 ciu_div; u32 sdr_timing; u32 ddr_timing; + u32 hs400_timing; + u32 tuned_sample; u32 cur_speed; + u32 dqs_delay; + u32 saved_dqs_en; + u32 saved_strobe_ctrl; }; static struct dw_mci_exynos_compatible { @@ -71,6 +76,21 @@ static struct dw_mci_exynos_compatible { }, }; +static inline u8 dw_mci_exynos_get_ciu_div(struct dw_mci *host) +{ + struct dw_mci_exynos_priv_data *priv = host->priv; + + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412) + return EXYNOS4412_FIXED_CIU_CLK_DIV; + else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210) + return EXYNOS4210_FIXED_CIU_CLK_DIV; + else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL64)) + 1; + else + return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL)) + 1; +} + static int dw_mci_exynos_priv_init(struct dw_mci *host) { struct dw_mci_exynos_priv_data *priv = host->priv; @@ -85,6 +105,16 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host) SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT); } + if (priv->ctrl_type >= DW_MCI_TYPE_EXYNOS5420) { + priv->saved_strobe_ctrl = mci_readl(host, HS400_DLINE_CTRL); + priv->saved_dqs_en = mci_readl(host, HS400_DQS_EN); + priv->saved_dqs_en |= AXI_NON_BLOCKING_WR; + mci_writel(host, HS400_DQS_EN, priv->saved_dqs_en); + if (!priv->dqs_delay) + priv->dqs_delay = + DQS_CTRL_GET_RD_DELAY(priv->saved_strobe_ctrl); + } + return 0; } @@ -97,6 +127,26 @@ static int dw_mci_exynos_setup_clock(struct dw_mci *host) return 0; } +static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing) +{ + struct dw_mci_exynos_priv_data *priv = host->priv; + u32 clksel; + + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + clksel = mci_readl(host, CLKSEL64); + else + clksel = mci_readl(host, CLKSEL); + + clksel = (clksel & ~SDMMC_CLKSEL_TIMING_MASK) | timing; + + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) + mci_writel(host, CLKSEL64, clksel); + else + mci_writel(host, CLKSEL, clksel); +} + #ifdef CONFIG_PM_SLEEP static int dw_mci_exynos_suspend(struct device *dev) { @@ -172,30 +222,38 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr) } } -static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) +static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing) { struct dw_mci_exynos_priv_data *priv = host->priv; - unsigned int wanted = ios->clock; - unsigned long actual; - u8 div = priv->ciu_div + 1; + u32 dqs, strobe; - if (ios->timing == MMC_TIMING_MMC_DDR52) { - if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || - priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) - mci_writel(host, CLKSEL64, priv->ddr_timing); - else - mci_writel(host, CLKSEL, priv->ddr_timing); - /* Should be double rate for DDR mode */ - if (ios->bus_width == MMC_BUS_WIDTH_8) - wanted <<= 1; + /* + * Not supported to configure register + * related to HS400 + */ + if (priv->ctrl_type < DW_MCI_TYPE_EXYNOS5420) + return; + + dqs = priv->saved_dqs_en; + strobe = priv->saved_strobe_ctrl; + + if (timing == MMC_TIMING_MMC_HS400) { + dqs |= DATA_STROBE_EN; + strobe = DQS_CTRL_RD_DELAY(strobe, priv->dqs_delay); } else { - if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || - priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) - mci_writel(host, CLKSEL64, priv->sdr_timing); - else - mci_writel(host, CLKSEL, priv->sdr_timing); + dqs &= ~DATA_STROBE_EN; } + mci_writel(host, HS400_DQS_EN, dqs); + mci_writel(host, HS400_DLINE_CTRL, strobe); +} + +static void dw_mci_exynos_adjust_clock(struct dw_mci *host, unsigned int wanted) +{ + struct dw_mci_exynos_priv_data *priv = host->priv; + unsigned long actual; + u8 div; + int ret; /* * Don't care if wanted clock is zero or * ciu clock is unavailable @@ -207,17 +265,52 @@ static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) if (wanted < EXYNOS_CCLKIN_MIN) wanted = EXYNOS_CCLKIN_MIN; - if (wanted != priv->cur_speed) { - int ret = clk_set_rate(host->ciu_clk, wanted * div); - if (ret) - dev_warn(host->dev, - "failed to set clk-rate %u error: %d\n", - wanted * div, ret); - actual = clk_get_rate(host->ciu_clk); - host->bus_hz = actual / div; - priv->cur_speed = wanted; - host->current_speed = 0; + if (wanted == priv->cur_speed) + return; + + div = dw_mci_exynos_get_ciu_div(host); + ret = clk_set_rate(host->ciu_clk, wanted * div); + if (ret) + dev_warn(host->dev, + "failed to set clk-rate %u error: %d\n", + wanted * div, ret); + actual = clk_get_rate(host->ciu_clk); + host->bus_hz = actual / div; + priv->cur_speed = wanted; + host->current_speed = 0; +} + +static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ + struct dw_mci_exynos_priv_data *priv = host->priv; + unsigned int wanted = ios->clock; + u32 timing = ios->timing, clksel; + + switch (timing) { + case MMC_TIMING_MMC_HS400: + /* Update tuned sample timing */ + clksel = SDMMC_CLKSEL_UP_SAMPLE( + priv->hs400_timing, priv->tuned_sample); + wanted <<= 1; + break; + case MMC_TIMING_MMC_DDR52: + clksel = priv->ddr_timing; + /* Should be double rate for DDR mode */ + if (ios->bus_width == MMC_BUS_WIDTH_8) + wanted <<= 1; + break; + default: + clksel = priv->sdr_timing; } + + /* Set clock timing for the requested speed mode*/ + dw_mci_exynos_set_clksel_timing(host, clksel); + + /* Configure setting for HS400 */ + dw_mci_exynos_config_hs400(host, timing); + + /* Configure clock rate */ + dw_mci_exynos_adjust_clock(host, wanted); } static int dw_mci_exynos_parse_dt(struct dw_mci *host) @@ -260,6 +353,16 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host) return ret; priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); + + ret = of_property_read_u32_array(np, + "samsung,dw-mshc-hs400-timing", timing, 2); + if (!ret && of_property_read_u32(np, + "samsung,read-strobe-delay", &priv->dqs_delay)) + dev_dbg(host->dev, + "read-strobe-delay is not found, assuming usage of default value\n"); + + priv->hs400_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], + HS400_FIXED_CIU_CLK_DIV); host->priv = priv; return 0; } @@ -285,7 +388,7 @@ static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample) clksel = mci_readl(host, CLKSEL64); else clksel = mci_readl(host, CLKSEL); - clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample); + clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample); if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) mci_writel(host, CLKSEL64, clksel); @@ -304,13 +407,16 @@ static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host) clksel = mci_readl(host, CLKSEL64); else clksel = mci_readl(host, CLKSEL); + sample = (clksel + 1) & 0x7; - clksel = (clksel & ~0x7) | sample; + clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample); + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) mci_writel(host, CLKSEL64, clksel); else mci_writel(host, CLKSEL, clksel); + return sample; } @@ -343,6 +449,7 @@ out: static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot) { struct dw_mci *host = slot->host; + struct dw_mci_exynos_priv_data *priv = host->priv; struct mmc_host *mmc = slot->mmc; u8 start_smpl, smpl, candiates = 0; s8 found = -1; @@ -360,14 +467,27 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot) } while (start_smpl != smpl); found = dw_mci_exynos_get_best_clksmpl(candiates); - if (found >= 0) + if (found >= 0) { dw_mci_exynos_set_clksmpl(host, found); - else + priv->tuned_sample = found; + } else { ret = -EIO; + } return ret; } +static int dw_mci_exynos_prepare_hs400_tuning(struct dw_mci *host, + struct mmc_ios *ios) +{ + struct dw_mci_exynos_priv_data *priv = host->priv; + + dw_mci_exynos_set_clksel_timing(host, priv->hs400_timing); + dw_mci_exynos_adjust_clock(host, (ios->clock) << 1); + + return 0; +} + /* Common capabilities of Exynos4/Exynos5 SoC */ static unsigned long exynos_dwmmc_caps[4] = { MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23, @@ -384,6 +504,7 @@ static const struct dw_mci_drv_data exynos_drv_data = { .set_ios = dw_mci_exynos_set_ios, .parse_dt = dw_mci_exynos_parse_dt, .execute_tuning = dw_mci_exynos_execute_tuning, + .prepare_hs400_tuning = dw_mci_exynos_prepare_hs400_tuning, }; static const struct of_device_id dw_mci_exynos_match[] = { diff --git a/drivers/mmc/host/dw_mmc-exynos.h b/drivers/mmc/host/dw_mmc-exynos.h index 7872ce5..595c934 100644 --- a/drivers/mmc/host/dw_mmc-exynos.h +++ b/drivers/mmc/host/dw_mmc-exynos.h @@ -12,20 +12,36 @@ #ifndef _DW_MMC_EXYNOS_H_ #define _DW_MMC_EXYNOS_H_ -/* Extended Register's Offset */ #define SDMMC_CLKSEL 0x09C #define SDMMC_CLKSEL64 0x0A8 +/* Extended Register's Offset */ +#define SDMMC_HS400_DQS_EN 0x180 +#define SDMMC_HS400_ASYNC_FIFO_CTRL 0x184 +#define SDMMC_HS400_DLINE_CTRL 0x188 + /* CLKSEL register defines */ #define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0) #define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16) #define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24) #define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7) +#define SDMMC_CLKSEL_GET_DIV(x) (((x) >> 24) & 0x7) +#define SDMMC_CLKSEL_UP_SAMPLE(x, y) (((x) & ~SDMMC_CLKSEL_CCLK_SAMPLE(7)) |\ + SDMMC_CLKSEL_CCLK_SAMPLE(y)) #define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \ SDMMC_CLKSEL_CCLK_DRIVE(y) | \ SDMMC_CLKSEL_CCLK_DIVIDER(z)) +#define SDMMC_CLKSEL_TIMING_MASK SDMMC_CLKSEL_TIMING(0x7, 0x7, 0x7) #define SDMMC_CLKSEL_WAKEUP_INT BIT(11) +/* RCLK_EN register defines */ +#define DATA_STROBE_EN BIT(0) +#define AXI_NON_BLOCKING_WR BIT(7) + +/* DLINE_CTRL register defines */ +#define DQS_CTRL_RD_DELAY(x, y) (((x) & ~0x3FF) | ((y) & 0x3FF)) +#define DQS_CTRL_GET_RD_DELAY(x) ((x) & 0x3FF) + /* Protector Register */ #define SDMMC_EMMCP_BASE 0x1000 #define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010) @@ -49,6 +65,7 @@ /* Fixed clock divider */ #define EXYNOS4210_FIXED_CIU_CLK_DIV 2 #define EXYNOS4412_FIXED_CIU_CLK_DIV 4 +#define HS400_FIXED_CIU_CLK_DIV 1 /* Minimal required clock frequency for cclkin, unit: HZ */ #define EXYNOS_CCLKIN_MIN 50000000 diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index e2a726a..dbf166f 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -76,12 +76,20 @@ static int dw_mci_rockchip_init(struct dw_mci *host) return 0; } +/* Common capabilities of RK3288 SoC */ +static unsigned long dw_mci_rk3288_dwmmc_caps[4] = { + MMC_CAP_RUNTIME_RESUME, /* emmc */ + MMC_CAP_RUNTIME_RESUME, /* sdmmc */ + MMC_CAP_RUNTIME_RESUME, /* sdio0 */ + MMC_CAP_RUNTIME_RESUME, /* sdio1 */ +}; static const struct dw_mci_drv_data rk2928_drv_data = { .prepare_command = dw_mci_rockchip_prepare_command, .init = dw_mci_rockchip_init, }; static const struct dw_mci_drv_data rk3288_drv_data = { + .caps = dw_mci_rk3288_dwmmc_caps, .prepare_command = dw_mci_rockchip_prepare_command, .set_ios = dw_mci_rk3288_set_ios, .setup_clock = dw_mci_rk3288_setup_clock, diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4d2e3c2..38b2926 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -69,7 +69,8 @@ struct idmac_desc_64addr { u32 des2; /*Buffer sizes */ #define IDMAC_64ADDR_SET_BUFFER1_SIZE(d, s) \ - ((d)->des2 = ((d)->des2 & 0x03ffe000) | ((s) & 0x1fff)) + ((d)->des2 = ((d)->des2 & cpu_to_le32(0x03ffe000)) | \ + ((cpu_to_le32(s)) & cpu_to_le32(0x1fff))) u32 des3; /* Reserved */ @@ -81,7 +82,7 @@ struct idmac_desc_64addr { }; struct idmac_desc { - u32 des0; /* Control Descriptor */ + __le32 des0; /* Control Descriptor */ #define IDMAC_DES0_DIC BIT(1) #define IDMAC_DES0_LD BIT(2) #define IDMAC_DES0_FD BIT(3) @@ -90,18 +91,19 @@ struct idmac_desc { #define IDMAC_DES0_CES BIT(30) #define IDMAC_DES0_OWN BIT(31) - u32 des1; /* Buffer sizes */ + __le32 des1; /* Buffer sizes */ #define IDMAC_SET_BUFFER1_SIZE(d, s) \ ((d)->des1 = ((d)->des1 & 0x03ffe000) | ((s) & 0x1fff)) - u32 des2; /* buffer 1 physical address */ + __le32 des2; /* buffer 1 physical address */ - u32 des3; /* buffer 2 physical address */ + __le32 des3; /* buffer 2 physical address */ }; #endif /* CONFIG_MMC_DW_IDMAC */ static bool dw_mci_reset(struct dw_mci *host); static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); +static int dw_mci_card_busy(struct mmc_host *mmc); #if defined(CONFIG_DEBUG_FS) static int dw_mci_req_show(struct seq_file *s, void *v) @@ -335,6 +337,31 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) return cmdr; } +static void dw_mci_wait_while_busy(struct dw_mci *host, u32 cmd_flags) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(500); + + /* + * Databook says that before issuing a new data transfer command + * we need to check to see if the card is busy. Data transfer commands + * all have SDMMC_CMD_PRV_DAT_WAIT set, so we'll key off that. + * + * ...also allow sending for SDMMC_CMD_VOLT_SWITCH where busy is + * expected. + */ + if ((cmd_flags & SDMMC_CMD_PRV_DAT_WAIT) && + !(cmd_flags & SDMMC_CMD_VOLT_SWITCH)) { + while (mci_readl(host, STATUS) & SDMMC_STATUS_BUSY) { + if (time_after(jiffies, timeout)) { + /* Command will fail; we'll pass error then */ + dev_err(host->dev, "Busy; trying anyway\n"); + break; + } + udelay(10); + } + } +} + static void dw_mci_start_command(struct dw_mci *host, struct mmc_command *cmd, u32 cmd_flags) { @@ -345,6 +372,7 @@ static void dw_mci_start_command(struct dw_mci *host, mci_writel(host, CMDARG, cmd->arg); wmb(); + dw_mci_wait_while_busy(host, cmd_flags); mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START); } @@ -477,23 +505,23 @@ static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, * Set the OWN bit and disable interrupts for this * descriptor */ - desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | - IDMAC_DES0_CH; + desc->des0 = cpu_to_le32(IDMAC_DES0_OWN | + IDMAC_DES0_DIC | IDMAC_DES0_CH); /* Buffer length */ IDMAC_SET_BUFFER1_SIZE(desc, length); /* Physical address to DMA to/from */ - desc->des2 = mem_addr; + desc->des2 = cpu_to_le32(mem_addr); } /* Set first descriptor */ desc = host->sg_cpu; - desc->des0 |= IDMAC_DES0_FD; + desc->des0 |= cpu_to_le32(IDMAC_DES0_FD); /* Set last descriptor */ desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc); - desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); - desc->des0 |= IDMAC_DES0_LD; + desc->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | IDMAC_DES0_DIC)); + desc->des0 |= cpu_to_le32(IDMAC_DES0_LD); } wmb(); @@ -562,12 +590,12 @@ static int dw_mci_idmac_init(struct dw_mci *host) /* Forward link the descriptor list */ for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++) - p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * - (i + 1)); + p->des3 = cpu_to_le32(host->sg_dma + + (sizeof(struct idmac_desc) * (i + 1))); /* Set the last descriptor as the end-of-ring descriptor */ - p->des3 = host->sg_dma; - p->des0 = IDMAC_DES0_ER; + p->des3 = cpu_to_le32(host->sg_dma); + p->des0 = cpu_to_le32(IDMAC_DES0_ER); } dw_mci_idmac_reset(host); @@ -737,6 +765,7 @@ static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data) return; if (host->timing != MMC_TIMING_MMC_HS200 && + host->timing != MMC_TIMING_MMC_HS400 && host->timing != MMC_TIMING_UHS_SDR104) goto disable; @@ -876,6 +905,7 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) mci_writel(host, CMDARG, arg); wmb(); + dw_mci_wait_while_busy(host, cmd); mci_writel(host, CMD, SDMMC_CMD_START | cmd); while (time_before(jiffies, timeout)) { @@ -992,6 +1022,26 @@ static void __dw_mci_start_request(struct dw_mci *host, dw_mci_start_command(host, cmd, cmdflags); + if (cmd->opcode == SD_SWITCH_VOLTAGE) { + unsigned long irqflags; + + /* + * Databook says to fail after 2ms w/ no response, but evidence + * shows that sometimes the cmd11 interrupt takes over 130ms. + * We'll set to 500ms, plus an extra jiffy just in case jiffies + * is just about to roll over. + * + * We do this whole thing under spinlock and only if the + * command hasn't already completed (indicating the the irq + * already ran so we don't want the timeout). + */ + spin_lock_irqsave(&host->irq_lock, irqflags); + if (!test_bit(EVENT_CMD_COMPLETE, &host->pending_events)) + mod_timer(&host->cmd11_timer, + jiffies + msecs_to_jiffies(500) + 1); + spin_unlock_irqrestore(&host->irq_lock, irqflags); + } + if (mrq->stop) host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop); else @@ -1084,7 +1134,8 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) regs = mci_readl(slot->host, UHS_REG); /* DDR mode set */ - if (ios->timing == MMC_TIMING_MMC_DDR52) + if (ios->timing == MMC_TIMING_MMC_DDR52 || + ios->timing == MMC_TIMING_MMC_HS400) regs |= ((0x1 << slot->id) << 16); else regs &= ~((0x1 << slot->id) << 16); @@ -1101,12 +1152,6 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (drv_data && drv_data->set_ios) drv_data->set_ios(slot->host, ios); - /* Slot specific timing and width adjustment */ - dw_mci_setup_bus(slot, false); - - if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0) - slot->host->state = STATE_IDLE; - switch (ios->power_mode) { case MMC_POWER_UP: if (!IS_ERR(mmc->supply.vmmc)) { @@ -1125,23 +1170,39 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) mci_writel(slot->host, PWREN, regs); break; case MMC_POWER_ON: - if (!IS_ERR(mmc->supply.vqmmc) && !slot->host->vqmmc_enabled) { - ret = regulator_enable(mmc->supply.vqmmc); - if (ret < 0) - dev_err(slot->host->dev, - "failed to enable vqmmc regulator\n"); - else + if (!slot->host->vqmmc_enabled) { + if (!IS_ERR(mmc->supply.vqmmc)) { + ret = regulator_enable(mmc->supply.vqmmc); + if (ret < 0) + dev_err(slot->host->dev, + "failed to enable vqmmc\n"); + else + slot->host->vqmmc_enabled = true; + + } else { + /* Keep track so we don't reset again */ slot->host->vqmmc_enabled = true; + } + + /* Reset our state machine after powering on */ + dw_mci_ctrl_reset(slot->host, + SDMMC_CTRL_ALL_RESET_FLAGS); } + + /* Adjust clock / bus width after power is up */ + dw_mci_setup_bus(slot, false); + break; case MMC_POWER_OFF: + /* Turn clock off before power goes down */ + dw_mci_setup_bus(slot, false); + if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); - if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled) { + if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled) regulator_disable(mmc->supply.vqmmc); - slot->host->vqmmc_enabled = false; - } + slot->host->vqmmc_enabled = false; regs = mci_readl(slot->host, PWREN); regs &= ~(1 << slot->id); @@ -1150,6 +1211,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) default: break; } + + if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0) + slot->host->state = STATE_IDLE; } static int dw_mci_card_busy(struct mmc_host *mmc) @@ -1323,6 +1387,18 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) return err; } +static int dw_mci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = slot->host; + const struct dw_mci_drv_data *drv_data = host->drv_data; + + if (drv_data && drv_data->prepare_hs400_tuning) + return drv_data->prepare_hs400_tuning(host, ios); + + return 0; +} + static const struct mmc_host_ops dw_mci_ops = { .request = dw_mci_request, .pre_req = dw_mci_pre_req, @@ -1335,6 +1411,7 @@ static const struct mmc_host_ops dw_mci_ops = { .card_busy = dw_mci_card_busy, .start_signal_voltage_switch = dw_mci_switch_voltage, .init_card = dw_mci_init_card, + .prepare_hs400_tuning = dw_mci_prepare_hs400_tuning, }; static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) @@ -1520,7 +1597,10 @@ static void dw_mci_tasklet_func(unsigned long priv) if (test_and_clear_bit(EVENT_DATA_ERROR, &host->pending_events)) { dw_mci_stop_dma(host); - send_stop_abort(host, data); + if (data->stop || + !(host->data_status & (SDMMC_INT_DRTO | + SDMMC_INT_EBE))) + send_stop_abort(host, data); state = STATE_DATA_ERROR; break; } @@ -1547,7 +1627,10 @@ static void dw_mci_tasklet_func(unsigned long priv) if (test_and_clear_bit(EVENT_DATA_ERROR, &host->pending_events)) { dw_mci_stop_dma(host); - send_stop_abort(host, data); + if (data->stop || + !(host->data_status & (SDMMC_INT_DRTO | + SDMMC_INT_EBE))) + send_stop_abort(host, data); state = STATE_DATA_ERROR; break; } @@ -1685,8 +1768,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) buf += len; cnt -= len; if (host->part_buf_count == 2) { - mci_writew(host, DATA(host->data_offset), - host->part_buf16); + mci_fifo_writew(host->fifo_reg, host->part_buf16); host->part_buf_count = 0; } } @@ -1703,15 +1785,14 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) cnt -= len; /* push data from aligned buffer into fifo */ for (i = 0; i < items; ++i) - mci_writew(host, DATA(host->data_offset), - aligned_buf[i]); + mci_fifo_writew(host->fifo_reg, aligned_buf[i]); } } else #endif { u16 *pdata = buf; for (; cnt >= 2; cnt -= 2) - mci_writew(host, DATA(host->data_offset), *pdata++); + mci_fifo_writew(host->fifo_reg, *pdata++); buf = pdata; } /* put anything remaining in the part_buf */ @@ -1720,8 +1801,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) /* Push data if we have reached the expected data length */ if ((data->bytes_xfered + init_cnt) == (data->blksz * data->blocks)) - mci_writew(host, DATA(host->data_offset), - host->part_buf16); + mci_fifo_writew(host->fifo_reg, host->part_buf16); } } @@ -1736,8 +1816,7 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt) int items = len >> 1; int i; for (i = 0; i < items; ++i) - aligned_buf[i] = mci_readw(host, - DATA(host->data_offset)); + aligned_buf[i] = mci_fifo_readw(host->fifo_reg); /* memcpy from aligned buffer into output buffer */ memcpy(buf, aligned_buf, len); buf += len; @@ -1748,11 +1827,11 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt) { u16 *pdata = buf; for (; cnt >= 2; cnt -= 2) - *pdata++ = mci_readw(host, DATA(host->data_offset)); + *pdata++ = mci_fifo_readw(host->fifo_reg); buf = pdata; } if (cnt) { - host->part_buf16 = mci_readw(host, DATA(host->data_offset)); + host->part_buf16 = mci_fifo_readw(host->fifo_reg); dw_mci_pull_final_bytes(host, buf, cnt); } } @@ -1768,8 +1847,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) buf += len; cnt -= len; if (host->part_buf_count == 4) { - mci_writel(host, DATA(host->data_offset), - host->part_buf32); + mci_fifo_writel(host->fifo_reg, host->part_buf32); host->part_buf_count = 0; } } @@ -1786,15 +1864,14 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) cnt -= len; /* push data from aligned buffer into fifo */ for (i = 0; i < items; ++i) - mci_writel(host, DATA(host->data_offset), - aligned_buf[i]); + mci_fifo_writel(host->fifo_reg, aligned_buf[i]); } } else #endif { u32 *pdata = buf; for (; cnt >= 4; cnt -= 4) - mci_writel(host, DATA(host->data_offset), *pdata++); + mci_fifo_writel(host->fifo_reg, *pdata++); buf = pdata; } /* put anything remaining in the part_buf */ @@ -1803,8 +1880,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) /* Push data if we have reached the expected data length */ if ((data->bytes_xfered + init_cnt) == (data->blksz * data->blocks)) - mci_writel(host, DATA(host->data_offset), - host->part_buf32); + mci_fifo_writel(host->fifo_reg, host->part_buf32); } } @@ -1819,8 +1895,7 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt) int items = len >> 2; int i; for (i = 0; i < items; ++i) - aligned_buf[i] = mci_readl(host, - DATA(host->data_offset)); + aligned_buf[i] = mci_fifo_readl(host->fifo_reg); /* memcpy from aligned buffer into output buffer */ memcpy(buf, aligned_buf, len); buf += len; @@ -1831,11 +1906,11 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt) { u32 *pdata = buf; for (; cnt >= 4; cnt -= 4) - *pdata++ = mci_readl(host, DATA(host->data_offset)); + *pdata++ = mci_fifo_readl(host->fifo_reg); buf = pdata; } if (cnt) { - host->part_buf32 = mci_readl(host, DATA(host->data_offset)); + host->part_buf32 = mci_fifo_readl(host->fifo_reg); dw_mci_pull_final_bytes(host, buf, cnt); } } @@ -1852,8 +1927,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) cnt -= len; if (host->part_buf_count == 8) { - mci_writeq(host, DATA(host->data_offset), - host->part_buf); + mci_fifo_writeq(host->fifo_reg, host->part_buf); host->part_buf_count = 0; } } @@ -1870,15 +1944,14 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) cnt -= len; /* push data from aligned buffer into fifo */ for (i = 0; i < items; ++i) - mci_writeq(host, DATA(host->data_offset), - aligned_buf[i]); + mci_fifo_writeq(host->fifo_reg, aligned_buf[i]); } } else #endif { u64 *pdata = buf; for (; cnt >= 8; cnt -= 8) - mci_writeq(host, DATA(host->data_offset), *pdata++); + mci_fifo_writeq(host->fifo_reg, *pdata++); buf = pdata; } /* put anything remaining in the part_buf */ @@ -1887,8 +1960,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) /* Push data if we have reached the expected data length */ if ((data->bytes_xfered + init_cnt) == (data->blksz * data->blocks)) - mci_writeq(host, DATA(host->data_offset), - host->part_buf); + mci_fifo_writeq(host->fifo_reg, host->part_buf); } } @@ -1903,8 +1975,8 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt) int items = len >> 3; int i; for (i = 0; i < items; ++i) - aligned_buf[i] = mci_readq(host, - DATA(host->data_offset)); + aligned_buf[i] = mci_fifo_readq(host->fifo_reg); + /* memcpy from aligned buffer into output buffer */ memcpy(buf, aligned_buf, len); buf += len; @@ -1915,11 +1987,11 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt) { u64 *pdata = buf; for (; cnt >= 8; cnt -= 8) - *pdata++ = mci_readq(host, DATA(host->data_offset)); + *pdata++ = mci_fifo_readq(host->fifo_reg); buf = pdata; } if (cnt) { - host->part_buf = mci_readq(host, DATA(host->data_offset)); + host->part_buf = mci_fifo_readq(host->fifo_reg); dw_mci_pull_final_bytes(host, buf, cnt); } } @@ -2097,9 +2169,20 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) /* Check volt switch first, since it can look like an error */ if ((host->state == STATE_SENDING_CMD11) && (pending & SDMMC_INT_VOLT_SWITCH)) { + unsigned long irqflags; + mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH); pending &= ~SDMMC_INT_VOLT_SWITCH; + + /* + * Hold the lock; we know cmd11_timer can't be kicked + * off after the lock is released, so safe to delete. + */ + spin_lock_irqsave(&host->irq_lock, irqflags); dw_mci_cmd_interrupt(host, pending); + spin_unlock_irqrestore(&host->irq_lock, irqflags); + + del_timer(&host->cmd11_timer); } if (pending & DW_MCI_CMD_ERROR_FLAGS) { @@ -2156,6 +2239,10 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) /* Handle SDIO Interrupts */ for (i = 0; i < host->num_slots; i++) { struct dw_mci_slot *slot = host->slot[i]; + + if (!slot) + continue; + if (pending & SDMMC_INT_SDIO(slot->sdio_id)) { mci_writel(host, RINTSTS, SDMMC_INT_SDIO(slot->sdio_id)); @@ -2506,6 +2593,20 @@ ciu_out: return ret; } +static void dw_mci_cmd11_timer(unsigned long arg) +{ + struct dw_mci *host = (struct dw_mci *)arg; + + if (host->state != STATE_SENDING_CMD11) { + dev_warn(host->dev, "Unexpected CMD11 timeout\n"); + return; + } + + host->cmd_status = SDMMC_INT_RTO; + set_bit(EVENT_CMD_COMPLETE, &host->pending_events); + tasklet_schedule(&host->tasklet); +} + #ifdef CONFIG_OF static struct dw_mci_of_quirks { char *quirk; @@ -2574,6 +2675,34 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) } #endif /* CONFIG_OF */ +static void dw_mci_enable_cd(struct dw_mci *host) +{ + struct dw_mci_board *brd = host->pdata; + unsigned long irqflags; + u32 temp; + int i; + + /* No need for CD if broken card detection */ + if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) + return; + + /* No need for CD if all slots have a non-error GPIO */ + for (i = 0; i < host->num_slots; i++) { + struct dw_mci_slot *slot = host->slot[i]; + + if (IS_ERR_VALUE(mmc_gpio_get_cd(slot->mmc))) + break; + } + if (i == host->num_slots) + return; + + spin_lock_irqsave(&host->irq_lock, irqflags); + temp = mci_readl(host, INTMASK); + temp |= SDMMC_INT_CD; + mci_writel(host, INTMASK, temp); + spin_unlock_irqrestore(&host->irq_lock, irqflags); +} + int dw_mci_probe(struct dw_mci *host) { const struct dw_mci_drv_data *drv_data = host->drv_data; @@ -2652,6 +2781,9 @@ int dw_mci_probe(struct dw_mci *host) } } + setup_timer(&host->cmd11_timer, + dw_mci_cmd11_timer, (unsigned long)host); + host->quirks = host->pdata->quirks; spin_lock_init(&host->lock); @@ -2731,9 +2863,9 @@ int dw_mci_probe(struct dw_mci *host) dev_info(host->dev, "Version ID is %04x\n", host->verid); if (host->verid < DW_MMC_240A) - host->data_offset = DATA_OFFSET; + host->fifo_reg = host->regs + DATA_OFFSET; else - host->data_offset = DATA_240A_OFFSET; + host->fifo_reg = host->regs + DATA_240A_OFFSET; tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host); ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, @@ -2747,13 +2879,13 @@ int dw_mci_probe(struct dw_mci *host) host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1; /* - * Enable interrupts for command done, data over, data empty, card det, + * Enable interrupts for command done, data over, data empty, * receive ready and error such as transmit, receive timeout, crc error */ mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | SDMMC_INT_TXDR | SDMMC_INT_RXDR | - DW_MCI_ERROR_FLAGS | SDMMC_INT_CD); + DW_MCI_ERROR_FLAGS); mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */ dev_info(host->dev, "DW MMC controller at irq %d, " @@ -2778,6 +2910,9 @@ int dw_mci_probe(struct dw_mci *host) goto err_dmaunmap; } + /* Now that slots are all setup, we can enable card detect */ + dw_mci_enable_cd(host); + if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) dev_info(host->dev, "Internal DMAC interrupt fix enabled.\n"); @@ -2864,7 +2999,7 @@ int dw_mci_resume(struct dw_mci *host) mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | SDMMC_INT_TXDR | SDMMC_INT_RXDR | - DW_MCI_ERROR_FLAGS | SDMMC_INT_CD); + DW_MCI_ERROR_FLAGS); mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); for (i = 0; i < host->num_slots; i++) { @@ -2876,6 +3011,10 @@ int dw_mci_resume(struct dw_mci *host) dw_mci_setup_bus(slot, true); } } + + /* Now that slots are all setup, we can enable card detect */ + dw_mci_enable_cd(host); + return 0; } EXPORT_SYMBOL(dw_mci_resume); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 18c4afe..f45ab91 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -169,24 +169,34 @@ #define SDMMC_CTRL_ALL_RESET_FLAGS \ (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET) +/* FIFO register access macros. These should not change the data endian-ness + * as they are written to memory to be dealt with by the upper layers */ +#define mci_fifo_readw(__reg) __raw_readw(__reg) +#define mci_fifo_readl(__reg) __raw_readl(__reg) +#define mci_fifo_readq(__reg) __raw_readq(__reg) + +#define mci_fifo_writew(__value, __reg) __raw_writew(__reg, __value) +#define mci_fifo_writel(__value, __reg) __raw_writel(__reg, __value) +#define mci_fifo_writeq(__value, __reg) __raw_writeq(__reg, __value) + /* Register access macros */ #define mci_readl(dev, reg) \ - __raw_readl((dev)->regs + SDMMC_##reg) + readl_relaxed((dev)->regs + SDMMC_##reg) #define mci_writel(dev, reg, value) \ - __raw_writel((value), (dev)->regs + SDMMC_##reg) + writel_relaxed((value), (dev)->regs + SDMMC_##reg) /* 16-bit FIFO access macros */ #define mci_readw(dev, reg) \ - __raw_readw((dev)->regs + SDMMC_##reg) + readw_relaxed((dev)->regs + SDMMC_##reg) #define mci_writew(dev, reg, value) \ - __raw_writew((value), (dev)->regs + SDMMC_##reg) + writew_relaxed((value), (dev)->regs + SDMMC_##reg) /* 64-bit FIFO access macros */ #ifdef readq #define mci_readq(dev, reg) \ - __raw_readq((dev)->regs + SDMMC_##reg) + readq_relaxed((dev)->regs + SDMMC_##reg) #define mci_writeq(dev, reg, value) \ - __raw_writeq((value), (dev)->regs + SDMMC_##reg) + writeq_relaxed((value), (dev)->regs + SDMMC_##reg) #else /* * Dummy readq implementation for architectures that don't define it. @@ -200,6 +210,10 @@ (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg)) #define mci_writeq(dev, reg, value) \ (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value)) + +#define __raw_writeq(__value, __reg) \ + (*(volatile u64 __force *)(__reg) = (__value)) +#define __raw_readq(__reg) (*(volatile u64 __force *)(__reg)) #endif extern int dw_mci_probe(struct dw_mci *host); @@ -271,5 +285,7 @@ struct dw_mci_drv_data { void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios); int (*parse_dt)(struct dw_mci *host); int (*execute_tuning)(struct dw_mci_slot *slot); + int (*prepare_hs400_tuning)(struct dw_mci *host, + struct mmc_ios *ios); }; #endif /* _DW_MMC_H_ */ diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index e4a0754..ae19d83 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1507,7 +1507,7 @@ static int mmc_spi_remove(struct spi_device *spi) return 0; } -static struct of_device_id mmc_spi_of_match_table[] = { +static const struct of_device_id mmc_spi_of_match_table[] = { { .compatible = "mmc-spi-slot", }, {}, }; diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 7fe1619..fb26674 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1613,7 +1613,10 @@ static int mmci_probe(struct amba_device *dev, dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max); /* Get regulators and the supported OCR mask */ - mmc_regulator_get_supply(mmc); + ret = mmc_regulator_get_supply(mmc); + if (ret == -EPROBE_DEFER) + goto clk_disable; + if (!mmc->ocr_avail) mmc->ocr_avail = plat->ocr_mask; else if (plat->ocr_mask) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index f84cfb0..9df2b68 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -222,10 +222,6 @@ struct omap_hsmmc_host { struct omap_hsmmc_next next_data; struct omap_hsmmc_platform_data *pdata; - /* To handle board related suspend/resume functionality for MMC */ - int (*suspend)(struct device *dev); - int (*resume)(struct device *dev); - /* return MMC cover switch state, can be NULL if not supported. * * possible return values: @@ -234,12 +230,7 @@ struct omap_hsmmc_host { */ int (*get_cover_state)(struct device *dev); - /* Card detection IRQs */ - int card_detect_irq; - int (*card_detect)(struct device *dev); - int (*get_ro)(struct device *dev); - }; struct omap_mmc_of_data { @@ -256,13 +247,6 @@ static int omap_hsmmc_card_detect(struct device *dev) return mmc_gpio_get_cd(host->mmc); } -static int omap_hsmmc_get_wp(struct device *dev) -{ - struct omap_hsmmc_host *host = dev_get_drvdata(dev); - - return mmc_gpio_get_ro(host->mmc); -} - static int omap_hsmmc_get_cover_state(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); @@ -434,7 +418,7 @@ static inline int omap_hsmmc_have_reg(void) #endif -static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id); +static irqreturn_t omap_hsmmc_cover_irq(int irq, void *dev_id); static int omap_hsmmc_gpio_init(struct mmc_host *mmc, struct omap_hsmmc_host *host, @@ -442,29 +426,25 @@ static int omap_hsmmc_gpio_init(struct mmc_host *mmc, { int ret; - if (gpio_is_valid(pdata->switch_pin)) { - if (pdata->cover) - host->get_cover_state = - omap_hsmmc_get_cover_state; - else - host->card_detect = omap_hsmmc_card_detect; - host->card_detect_irq = - gpio_to_irq(pdata->switch_pin); - mmc_gpio_set_cd_isr(mmc, omap_hsmmc_detect); - ret = mmc_gpio_request_cd(mmc, pdata->switch_pin, 0); + if (gpio_is_valid(pdata->gpio_cod)) { + ret = mmc_gpio_request_cd(mmc, pdata->gpio_cod, 0); if (ret) return ret; - } else { - pdata->switch_pin = -EINVAL; + + host->get_cover_state = omap_hsmmc_get_cover_state; + mmc_gpio_set_cd_isr(mmc, omap_hsmmc_cover_irq); + } else if (gpio_is_valid(pdata->gpio_cd)) { + ret = mmc_gpio_request_cd(mmc, pdata->gpio_cd, 0); + if (ret) + return ret; + + host->card_detect = omap_hsmmc_card_detect; } if (gpio_is_valid(pdata->gpio_wp)) { - host->get_ro = omap_hsmmc_get_wp; ret = mmc_gpio_request_ro(mmc, pdata->gpio_wp); if (ret) return ret; - } else { - pdata->gpio_wp = -EINVAL; } return 0; @@ -882,6 +862,8 @@ static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_req return; host->mrq = NULL; mmc_request_done(host->mmc, mrq); + pm_runtime_mark_last_busy(host->dev); + pm_runtime_put_autosuspend(host->dev); } /* @@ -1252,26 +1234,16 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host) } /* - * irq handler to notify the core about card insertion/removal + * irq handler when (cell-phone) cover is mounted/removed */ -static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id) +static irqreturn_t omap_hsmmc_cover_irq(int irq, void *dev_id) { struct omap_hsmmc_host *host = dev_id; - int carddetect; sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); - if (host->card_detect) - carddetect = host->card_detect(host->dev); - else { - omap_hsmmc_protect_card(host); - carddetect = -ENOSYS; - } - - if (carddetect) - mmc_detect_change(host->mmc, (HZ * 200) / 1000); - else - mmc_detect_change(host->mmc, (HZ * 50) / 1000); + omap_hsmmc_protect_card(host); + mmc_detect_change(host->mmc, (HZ * 200) / 1000); return IRQ_HANDLED; } @@ -1305,6 +1277,8 @@ static void omap_hsmmc_dma_callback(void *param) host->mrq = NULL; mmc_request_done(host->mmc, mrq); + pm_runtime_mark_last_busy(host->dev); + pm_runtime_put_autosuspend(host->dev); } } @@ -1537,6 +1511,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req) BUG_ON(host->req_in_progress); BUG_ON(host->dma_ch != -1); + pm_runtime_get_sync(host->dev); if (host->protect_card) { if (host->reqs_blocked < 3) { /* @@ -1553,6 +1528,8 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req) req->data->error = -EBADF; req->cmd->retries = 0; mmc_request_done(mmc, req); + pm_runtime_mark_last_busy(host->dev); + pm_runtime_put_autosuspend(host->dev); return; } else if (host->reqs_blocked) host->reqs_blocked = 0; @@ -1566,6 +1543,8 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req) req->data->error = err; host->mrq = NULL; mmc_request_done(mmc, req); + pm_runtime_mark_last_busy(host->dev); + pm_runtime_put_autosuspend(host->dev); return; } if (req->sbc && !(host->flags & AUTO_CMD23)) { @@ -1641,15 +1620,6 @@ static int omap_hsmmc_get_cd(struct mmc_host *mmc) return host->card_detect(host->dev); } -static int omap_hsmmc_get_ro(struct mmc_host *mmc) -{ - struct omap_hsmmc_host *host = mmc_priv(mmc); - - if (!host->get_ro) - return -ENOSYS; - return host->get_ro(host->dev); -} - static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card) { struct omap_hsmmc_host *host = mmc_priv(mmc); @@ -1778,25 +1748,6 @@ static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host) set_sd_bus_power(host); } -static int omap_hsmmc_enable_fclk(struct mmc_host *mmc) -{ - struct omap_hsmmc_host *host = mmc_priv(mmc); - - pm_runtime_get_sync(host->dev); - - return 0; -} - -static int omap_hsmmc_disable_fclk(struct mmc_host *mmc) -{ - struct omap_hsmmc_host *host = mmc_priv(mmc); - - pm_runtime_mark_last_busy(host->dev); - pm_runtime_put_autosuspend(host->dev); - - return 0; -} - static int omap_hsmmc_multi_io_quirk(struct mmc_card *card, unsigned int direction, int blk_size) { @@ -1808,14 +1759,12 @@ static int omap_hsmmc_multi_io_quirk(struct mmc_card *card, } static struct mmc_host_ops omap_hsmmc_ops = { - .enable = omap_hsmmc_enable_fclk, - .disable = omap_hsmmc_disable_fclk, .post_req = omap_hsmmc_post_req, .pre_req = omap_hsmmc_pre_req, .request = omap_hsmmc_request, .set_ios = omap_hsmmc_set_ios, .get_cd = omap_hsmmc_get_cd, - .get_ro = omap_hsmmc_get_ro, + .get_ro = mmc_gpio_get_ro, .init_card = omap_hsmmc_init_card, .enable_sdio_irq = omap_hsmmc_enable_sdio_irq, }; @@ -1937,7 +1886,8 @@ static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev) if (of_find_property(np, "ti,dual-volt", NULL)) pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; - pdata->switch_pin = -EINVAL; + pdata->gpio_cd = -EINVAL; + pdata->gpio_cod = -EINVAL; pdata->gpio_wp = -EINVAL; if (of_find_property(np, "ti,non-removable", NULL)) { @@ -2179,9 +2129,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (ret < 0) goto err_slot_name; } - if (host->card_detect_irq && host->get_cover_state) { + if (host->get_cover_state) { ret = device_create_file(&mmc->class_dev, - &dev_attr_cover_switch); + &dev_attr_cover_switch); if (ret < 0) goto err_slot_name; } @@ -2236,7 +2186,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int omap_hsmmc_suspend(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); @@ -2292,10 +2242,6 @@ static int omap_hsmmc_resume(struct device *dev) pm_runtime_put_autosuspend(host->dev); return 0; } - -#else -#define omap_hsmmc_suspend NULL -#define omap_hsmmc_resume NULL #endif static int omap_hsmmc_runtime_suspend(struct device *dev) @@ -2376,8 +2322,7 @@ static int omap_hsmmc_runtime_resume(struct device *dev) } static struct dev_pm_ops omap_hsmmc_dev_pm_ops = { - .suspend = omap_hsmmc_suspend, - .resume = omap_hsmmc_resume, + SET_SYSTEM_SLEEP_PM_OPS(omap_hsmmc_suspend, omap_hsmmc_resume) .runtime_suspend = omap_hsmmc_runtime_suspend, .runtime_resume = omap_hsmmc_runtime_resume, }; diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index a45ed39..22d929f 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -40,7 +40,6 @@ #include <linux/mmc/host.h> #include <linux/mmc/pm.h> #include <linux/mmc/slot-gpio.h> -#include <linux/mmc/sdhci.h> #include "sdhci.h" diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 34bb8f9..2bd90fb 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -54,7 +54,6 @@ struct sdhci_bcm_kona_dev { struct mutex write_lock; /* protect back to back writes */ - struct clk *external_clk; }; @@ -175,24 +174,6 @@ static void sdhci_bcm_kona_card_event(struct sdhci_host *host) } } -/* - * Get the base clock. Use central clock source for now. Not sure if different - * clock speed to each dev is allowed - */ -static unsigned int sdhci_bcm_kona_get_max_clk(struct sdhci_host *host) -{ - struct sdhci_bcm_kona_dev *kona_dev; - struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host); - kona_dev = sdhci_pltfm_priv(pltfm_priv); - - return host->mmc->f_max; -} - -static unsigned int sdhci_bcm_kona_get_timeout_clock(struct sdhci_host *host) -{ - return sdhci_bcm_kona_get_max_clk(host); -} - static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host, u8 power_mode) { @@ -207,8 +188,8 @@ static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host, static struct sdhci_ops sdhci_bcm_kona_ops = { .set_clock = sdhci_set_clock, - .get_max_clock = sdhci_bcm_kona_get_max_clk, - .get_timeout_clock = sdhci_bcm_kona_get_timeout_clock, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, .platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, @@ -264,21 +245,21 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev) goto err_pltfm_free; } - /* Get and enable the external clock */ - kona_dev->external_clk = devm_clk_get(dev, NULL); - if (IS_ERR(kona_dev->external_clk)) { - dev_err(dev, "Failed to get external clock\n"); - ret = PTR_ERR(kona_dev->external_clk); + /* Get and enable the core clock */ + pltfm_priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(pltfm_priv->clk)) { + dev_err(dev, "Failed to get core clock\n"); + ret = PTR_ERR(pltfm_priv->clk); goto err_pltfm_free; } - if (clk_set_rate(kona_dev->external_clk, host->mmc->f_max) != 0) { - dev_err(dev, "Failed to set rate external clock\n"); + if (clk_set_rate(pltfm_priv->clk, host->mmc->f_max) != 0) { + dev_err(dev, "Failed to set rate core clock\n"); goto err_pltfm_free; } - if (clk_prepare_enable(kona_dev->external_clk) != 0) { - dev_err(dev, "Failed to enable external clock\n"); + if (clk_prepare_enable(pltfm_priv->clk) != 0) { + dev_err(dev, "Failed to enable core clock\n"); goto err_pltfm_free; } @@ -333,7 +314,7 @@ err_reset: sdhci_bcm_kona_sd_reset(host); err_clk_disable: - clk_disable_unprepare(kona_dev->external_clk); + clk_disable_unprepare(pltfm_priv->clk); err_pltfm_free: sdhci_pltfm_free(pdev); @@ -342,22 +323,6 @@ err_pltfm_free: return ret; } -static int sdhci_bcm_kona_remove(struct platform_device *pdev) -{ - struct sdhci_host *host = platform_get_drvdata(pdev); - struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host); - struct sdhci_bcm_kona_dev *kona_dev = sdhci_pltfm_priv(pltfm_priv); - int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); - - sdhci_remove_host(host, dead); - - clk_disable_unprepare(kona_dev->external_clk); - - sdhci_pltfm_free(pdev); - - return 0; -} - static struct platform_driver sdhci_bcm_kona_driver = { .driver = { .name = "sdhci-kona", @@ -365,7 +330,7 @@ static struct platform_driver sdhci_bcm_kona_driver = { .of_match_table = sdhci_bcm_kona_of_match, }, .probe = sdhci_bcm_kona_probe, - .remove = sdhci_bcm_kona_remove, + .remove = sdhci_pltfm_unregister, }; module_platform_driver(sdhci_bcm_kona_driver); diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c index 439d259..0ef0343 100644 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -180,11 +180,6 @@ err: return ret; } -static int bcm2835_sdhci_remove(struct platform_device *pdev) -{ - return sdhci_pltfm_unregister(pdev); -} - static const struct of_device_id bcm2835_sdhci_of_match[] = { { .compatible = "brcm,bcm2835-sdhci" }, { } @@ -198,7 +193,7 @@ static struct platform_driver bcm2835_sdhci_driver = { .pm = SDHCI_PLTFM_PMOPS, }, .probe = bcm2835_sdhci_probe, - .remove = bcm2835_sdhci_remove, + .remove = sdhci_pltfm_unregister, }; module_platform_driver(bcm2835_sdhci_driver); diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index a7935a8..59f2923 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -98,18 +98,13 @@ static int sdhci_cns3xxx_probe(struct platform_device *pdev) return sdhci_pltfm_register(pdev, &sdhci_cns3xxx_pdata, 0); } -static int sdhci_cns3xxx_remove(struct platform_device *pdev) -{ - return sdhci_pltfm_unregister(pdev); -} - static struct platform_driver sdhci_cns3xxx_driver = { .driver = { .name = "sdhci-cns3xxx", .pm = SDHCI_PLTFM_PMOPS, }, .probe = sdhci_cns3xxx_probe, - .remove = sdhci_cns3xxx_remove, + .remove = sdhci_pltfm_unregister, }; module_platform_driver(sdhci_cns3xxx_driver); diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index ca969d2..407c21f 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -28,10 +28,6 @@ #include "sdhci-pltfm.h" -struct sdhci_dove_priv { - struct clk *clk; -}; - static u16 sdhci_dove_readw(struct sdhci_host *host, int reg) { u16 ret; @@ -84,27 +80,17 @@ static int sdhci_dove_probe(struct platform_device *pdev) { struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; - struct sdhci_dove_priv *priv; int ret; - priv = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_dove_priv), - GFP_KERNEL); - if (!priv) { - dev_err(&pdev->dev, "unable to allocate private data"); - return -ENOMEM; - } - - priv->clk = devm_clk_get(&pdev->dev, NULL); - host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata, 0); if (IS_ERR(host)) return PTR_ERR(host); pltfm_host = sdhci_priv(host); - pltfm_host->priv = priv; + pltfm_host->clk = devm_clk_get(&pdev->dev, NULL); - if (!IS_ERR(priv->clk)) - clk_prepare_enable(priv->clk); + if (!IS_ERR(pltfm_host->clk)) + clk_prepare_enable(pltfm_host->clk); ret = mmc_of_parse(host->mmc); if (ret) @@ -117,26 +103,11 @@ static int sdhci_dove_probe(struct platform_device *pdev) return 0; err_sdhci_add: - if (!IS_ERR(priv->clk)) - clk_disable_unprepare(priv->clk); + clk_disable_unprepare(pltfm_host->clk); sdhci_pltfm_free(pdev); return ret; } -static int sdhci_dove_remove(struct platform_device *pdev) -{ - struct sdhci_host *host = platform_get_drvdata(pdev); - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_dove_priv *priv = pltfm_host->priv; - - sdhci_pltfm_unregister(pdev); - - if (!IS_ERR(priv->clk)) - clk_disable_unprepare(priv->clk); - - return 0; -} - static const struct of_device_id sdhci_dove_of_match_table[] = { { .compatible = "marvell,dove-sdhci", }, {} @@ -150,7 +121,7 @@ static struct platform_driver sdhci_dove_driver = { .of_match_table = sdhci_dove_of_match_table, }, .probe = sdhci_dove_probe, - .remove = sdhci_dove_remove, + .remove = sdhci_pltfm_unregister, }; module_platform_driver(sdhci_dove_driver); diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 10ef824..82f512d 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -416,7 +416,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON; else new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON; - writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); + writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); return; case SDHCI_HOST_CONTROL2: new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); @@ -864,6 +864,7 @@ static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { #ifdef CONFIG_OF static int sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, + struct sdhci_host *host, struct esdhc_platform_data *boarddata) { struct device_node *np = pdev->dev.of_node; @@ -900,11 +901,14 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line)) boarddata->delay_line = 0; + mmc_of_parse_voltage(np, &host->ocr_mask); + return 0; } #else static inline int sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, + struct sdhci_host *host, struct esdhc_platform_data *boarddata) { return -ENODEV; @@ -999,7 +1003,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) host->ioaddr + ESDHC_TUNING_CTRL); boarddata = &imx_data->boarddata; - if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) { + if (sdhci_esdhc_imx_probe_dt(pdev, host, boarddata) < 0) { if (!host->mmc->parent->platform_data) { dev_err(mmc_dev(host->mmc), "no board data!\n"); err = -EINVAL; @@ -1009,40 +1013,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) host->mmc->parent->platform_data); } - /* write_protect */ - if (boarddata->wp_type == ESDHC_WP_GPIO) { - err = mmc_gpio_request_ro(host->mmc, boarddata->wp_gpio); - if (err) { - dev_err(mmc_dev(host->mmc), - "failed to request write-protect gpio!\n"); - goto disable_clk; - } - host->mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; - } - /* card_detect */ - switch (boarddata->cd_type) { - case ESDHC_CD_GPIO: - err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio, 0); - if (err) { - dev_err(mmc_dev(host->mmc), - "failed to request card-detect gpio!\n"); - goto disable_clk; - } - /* fall through */ - - case ESDHC_CD_CONTROLLER: - /* we have a working card_detect back */ + if (boarddata->cd_type == ESDHC_CD_CONTROLLER) host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; - break; - - case ESDHC_CD_PERMANENT: - host->mmc->caps |= MMC_CAP_NONREMOVABLE; - break; - - case ESDHC_CD_NONE: - break; - } switch (boarddata->max_bus_width) { case 8: @@ -1075,6 +1048,11 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; } + /* call to generic mmc_of_parse to support additional capabilities */ + err = mmc_of_parse(host->mmc); + if (err) + goto disable_clk; + err = sdhci_add_host(host); if (err) goto disable_clk; diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c new file mode 100644 index 0000000..3b423b0 --- /dev/null +++ b/drivers/mmc/host/sdhci-iproc.c @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * 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 version 2. + * + * 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. + */ + +/* + * iProc SDHCI platform driver + */ + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/mmc/host.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include "sdhci-pltfm.h" + +struct sdhci_iproc_data { + const struct sdhci_pltfm_data *pdata; + u32 caps; + u32 caps1; +}; + +struct sdhci_iproc_host { + const struct sdhci_iproc_data *data; + u32 shadow_cmd; + u32 shadow_blk; +}; + +#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18) + +static inline u32 sdhci_iproc_readl(struct sdhci_host *host, int reg) +{ + u32 val = readl(host->ioaddr + reg); + + pr_debug("%s: readl [0x%02x] 0x%08x\n", + mmc_hostname(host->mmc), reg, val); + return val; +} + +static u16 sdhci_iproc_readw(struct sdhci_host *host, int reg) +{ + u32 val = sdhci_iproc_readl(host, (reg & ~3)); + u16 word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff; + return word; +} + +static u8 sdhci_iproc_readb(struct sdhci_host *host, int reg) +{ + u32 val = sdhci_iproc_readl(host, (reg & ~3)); + u8 byte = val >> REG_OFFSET_IN_BITS(reg) & 0xff; + return byte; +} + +static inline void sdhci_iproc_writel(struct sdhci_host *host, u32 val, int reg) +{ + pr_debug("%s: writel [0x%02x] 0x%08x\n", + mmc_hostname(host->mmc), reg, val); + + writel(val, host->ioaddr + reg); + + if (host->clock <= 400000) { + /* Round up to micro-second four SD clock delay */ + if (host->clock) + udelay((4 * 1000000 + host->clock - 1) / host->clock); + else + udelay(10); + } +} + +/* + * The Arasan has a bugette whereby it may lose the content of successive + * writes to the same register that are within two SD-card clock cycles of + * each other (a clock domain crossing problem). The data + * register does not have this problem, which is just as well - otherwise we'd + * have to nobble the DMA engine too. + * + * This wouldn't be a problem with the code except that we can only write the + * controller with 32-bit writes. So two different 16-bit registers are + * written back to back creates the problem. + * + * In reality, this only happens when SDHCI_BLOCK_SIZE and SDHCI_BLOCK_COUNT + * are written followed by SDHCI_TRANSFER_MODE and SDHCI_COMMAND. + * The BLOCK_SIZE and BLOCK_COUNT are meaningless until a command issued so + * the work around can be further optimized. We can keep shadow values of + * BLOCK_SIZE, BLOCK_COUNT, and TRANSFER_MODE until a COMMAND is issued. + * Then, write the BLOCK_SIZE+BLOCK_COUNT in a single 32-bit write followed + * by the TRANSFER+COMMAND in another 32-bit write. + */ +static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_iproc_host *iproc_host = sdhci_pltfm_priv(pltfm_host); + u32 word_shift = REG_OFFSET_IN_BITS(reg); + u32 mask = 0xffff << word_shift; + u32 oldval, newval; + + if (reg == SDHCI_COMMAND) { + /* Write the block now as we are issuing a command */ + if (iproc_host->shadow_blk != 0) { + sdhci_iproc_writel(host, iproc_host->shadow_blk, + SDHCI_BLOCK_SIZE); + iproc_host->shadow_blk = 0; + } + oldval = iproc_host->shadow_cmd; + } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) { + /* Block size and count are stored in shadow reg */ + oldval = iproc_host->shadow_blk; + } else { + /* Read reg, all other registers are not shadowed */ + oldval = sdhci_iproc_readl(host, (reg & ~3)); + } + newval = (oldval & ~mask) | (val << word_shift); + + if (reg == SDHCI_TRANSFER_MODE) { + /* Save the transfer mode until the command is issued */ + iproc_host->shadow_cmd = newval; + } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) { + /* Save the block info until the command is issued */ + iproc_host->shadow_blk = newval; + } else { + /* Command or other regular 32-bit write */ + sdhci_iproc_writel(host, newval, reg & ~3); + } +} + +static void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg) +{ + u32 oldval = sdhci_iproc_readl(host, (reg & ~3)); + u32 byte_shift = REG_OFFSET_IN_BITS(reg); + u32 mask = 0xff << byte_shift; + u32 newval = (oldval & ~mask) | (val << byte_shift); + + sdhci_iproc_writel(host, newval, reg & ~3); +} + +static const struct sdhci_ops sdhci_iproc_ops = { + .read_l = sdhci_iproc_readl, + .read_w = sdhci_iproc_readw, + .read_b = sdhci_iproc_readb, + .write_l = sdhci_iproc_writel, + .write_w = sdhci_iproc_writew, + .write_b = sdhci_iproc_writeb, + .set_clock = sdhci_set_clock, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, +}; + +static const struct sdhci_pltfm_data sdhci_iproc_pltfm_data = { + .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK, + .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN, + .ops = &sdhci_iproc_ops, +}; + +static const struct sdhci_iproc_data iproc_data = { + .pdata = &sdhci_iproc_pltfm_data, + .caps = 0x05E90000, + .caps1 = 0x00000064, +}; + +static const struct of_device_id sdhci_iproc_of_match[] = { + { .compatible = "brcm,sdhci-iproc-cygnus", .data = &iproc_data }, + { } +}; +MODULE_DEVICE_TABLE(of, sdhci_iproc_of_match); + +static int sdhci_iproc_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + const struct sdhci_iproc_data *iproc_data; + struct sdhci_host *host; + struct sdhci_iproc_host *iproc_host; + struct sdhci_pltfm_host *pltfm_host; + int ret; + + match = of_match_device(sdhci_iproc_of_match, &pdev->dev); + if (!match) + return -EINVAL; + iproc_data = match->data; + + host = sdhci_pltfm_init(pdev, iproc_data->pdata, sizeof(*iproc_host)); + if (IS_ERR(host)) + return PTR_ERR(host); + + pltfm_host = sdhci_priv(host); + iproc_host = sdhci_pltfm_priv(pltfm_host); + + iproc_host->data = iproc_data; + + mmc_of_parse(host->mmc); + sdhci_get_of_property(pdev); + + /* Enable EMMC 1/8V DDR capable */ + host->mmc->caps |= MMC_CAP_1_8V_DDR; + + pltfm_host->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pltfm_host->clk)) { + ret = PTR_ERR(pltfm_host->clk); + goto err; + } + + if (iproc_host->data->pdata->quirks & SDHCI_QUIRK_MISSING_CAPS) { + host->caps = iproc_host->data->caps; + host->caps1 = iproc_host->data->caps1; + } + + return sdhci_add_host(host); + +err: + sdhci_pltfm_free(pdev); + return ret; +} + +static int sdhci_iproc_remove(struct platform_device *pdev) +{ + return sdhci_pltfm_unregister(pdev); +} + +static struct platform_driver sdhci_iproc_driver = { + .driver = { + .name = "sdhci-iproc", + .of_match_table = sdhci_iproc_of_match, + .pm = SDHCI_PLTFM_PMOPS, + }, + .probe = sdhci_iproc_probe, + .remove = sdhci_iproc_remove, +}; +module_platform_driver(sdhci_iproc_driver); + +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("IPROC SDHCI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 3d32ce8..4a09f76 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -22,6 +22,11 @@ #include "sdhci-pltfm.h" +#define CORE_MCI_VERSION 0x50 +#define CORE_VERSION_MAJOR_SHIFT 28 +#define CORE_VERSION_MAJOR_MASK (0xf << CORE_VERSION_MAJOR_SHIFT) +#define CORE_VERSION_MINOR_MASK 0xff + #define CORE_HC_MODE 0x78 #define HC_MODE_EN 0x1 #define CORE_POWER 0x0 @@ -41,6 +46,8 @@ #define CORE_VENDOR_SPEC 0x10c #define CORE_CLK_PWRSAVE BIT(1) +#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c + #define CDR_SELEXT_SHIFT 20 #define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT) #define CMUX_SHIFT_PHASE_SHIFT 24 @@ -426,7 +433,9 @@ static int sdhci_msm_probe(struct platform_device *pdev) struct sdhci_msm_host *msm_host; struct resource *core_memres; int ret; - u16 host_version; + u16 host_version, core_minor; + u32 core_version, caps; + u8 core_major; msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL); if (!msm_host) @@ -516,6 +525,24 @@ static int sdhci_msm_probe(struct platform_device *pdev) host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT)); + core_version = readl_relaxed(msm_host->core_mem + CORE_MCI_VERSION); + core_major = (core_version & CORE_VERSION_MAJOR_MASK) >> + CORE_VERSION_MAJOR_SHIFT; + core_minor = core_version & CORE_VERSION_MINOR_MASK; + dev_dbg(&pdev->dev, "MCI Version: 0x%08x, major: 0x%04x, minor: 0x%02x\n", + core_version, core_major, core_minor); + + /* + * Support for some capabilities is not advertised by newer + * controller versions and must be explicitly enabled. + */ + if (core_major >= 1 && core_minor != 0x11 && core_minor != 0x12) { + caps = readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES); + caps |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT; + writel_relaxed(caps, host->ioaddr + + CORE_VENDOR_SPEC_CAPABILITIES0); + } + ret = sdhci_add_host(host); if (ret) goto clk_disable; diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index bcb51e9..6287d42 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -173,6 +173,12 @@ static int sdhci_arasan_probe(struct platform_device *pdev) pltfm_host->priv = sdhci_arasan; pltfm_host->clk = clk_xin; + ret = mmc_of_parse(host->mmc); + if (ret) { + dev_err(&pdev->dev, "parsing dt failed (%u)\n", ret); + goto clk_disable_all; + } + ret = sdhci_add_host(host); if (ret) goto err_pltfm_free; @@ -195,7 +201,6 @@ static int sdhci_arasan_remove(struct platform_device *pdev) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; - clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(sdhci_arasan->clk_ahb); return sdhci_pltfm_unregister(pdev); diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 17fe02e..22e9111 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -386,11 +386,6 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) return ret; } -static int sdhci_esdhc_remove(struct platform_device *pdev) -{ - return sdhci_pltfm_unregister(pdev); -} - static const struct of_device_id sdhci_esdhc_of_match[] = { { .compatible = "fsl,mpc8379-esdhc" }, { .compatible = "fsl,mpc8536-esdhc" }, @@ -406,7 +401,7 @@ static struct platform_driver sdhci_esdhc_driver = { .pm = ESDHC_PMOPS, }, .probe = sdhci_esdhc_probe, - .remove = sdhci_esdhc_remove, + .remove = sdhci_pltfm_unregister, }; module_platform_driver(sdhci_esdhc_driver); diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c index be47927..4079a96 100644 --- a/drivers/mmc/host/sdhci-of-hlwd.c +++ b/drivers/mmc/host/sdhci-of-hlwd.c @@ -75,11 +75,6 @@ static int sdhci_hlwd_probe(struct platform_device *pdev) return sdhci_pltfm_register(pdev, &sdhci_hlwd_pdata, 0); } -static int sdhci_hlwd_remove(struct platform_device *pdev) -{ - return sdhci_pltfm_unregister(pdev); -} - static const struct of_device_id sdhci_hlwd_of_match[] = { { .compatible = "nintendo,hollywood-sdhci" }, { } @@ -93,7 +88,7 @@ static struct platform_driver sdhci_hlwd_driver = { .pm = SDHCI_PLTFM_PMOPS, }, .probe = sdhci_hlwd_probe, - .remove = sdhci_hlwd_remove, + .remove = sdhci_pltfm_unregister, }; module_platform_driver(sdhci_hlwd_driver); diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 29eaff7..7a3fc16 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -650,6 +650,7 @@ static int rtsx_probe_slot(struct sdhci_pci_slot *slot) static const struct sdhci_pci_fixes sdhci_rtsx = { .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_BROKEN_64_BIT_DMA | SDHCI_QUIRK2_BROKEN_DDR50, .probe_slot = rtsx_probe_slot, }; diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index c5b01d6..a207f5a 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -75,43 +75,41 @@ void sdhci_get_of_property(struct platform_device *pdev) u32 bus_width; int size; - if (of_device_is_available(np)) { - if (of_get_property(np, "sdhci,auto-cmd12", NULL)) - host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; + if (of_get_property(np, "sdhci,auto-cmd12", NULL)) + host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; - if (of_get_property(np, "sdhci,1-bit-only", NULL) || - (of_property_read_u32(np, "bus-width", &bus_width) == 0 && - bus_width == 1)) - host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA; + if (of_get_property(np, "sdhci,1-bit-only", NULL) || + (of_property_read_u32(np, "bus-width", &bus_width) == 0 && + bus_width == 1)) + host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA; - if (sdhci_of_wp_inverted(np)) - host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT; + if (sdhci_of_wp_inverted(np)) + host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT; - if (of_get_property(np, "broken-cd", NULL)) - host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; + if (of_get_property(np, "broken-cd", NULL)) + host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; - if (of_get_property(np, "no-1-8-v", NULL)) - host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; + if (of_get_property(np, "no-1-8-v", NULL)) + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; - if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc")) - host->quirks |= SDHCI_QUIRK_BROKEN_DMA; + if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc")) + host->quirks |= SDHCI_QUIRK_BROKEN_DMA; - if (of_device_is_compatible(np, "fsl,p2020-esdhc") || - of_device_is_compatible(np, "fsl,p1010-esdhc") || - of_device_is_compatible(np, "fsl,t4240-esdhc") || - of_device_is_compatible(np, "fsl,mpc8536-esdhc")) - host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; + if (of_device_is_compatible(np, "fsl,p2020-esdhc") || + of_device_is_compatible(np, "fsl,p1010-esdhc") || + of_device_is_compatible(np, "fsl,t4240-esdhc") || + of_device_is_compatible(np, "fsl,mpc8536-esdhc")) + host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; - clk = of_get_property(np, "clock-frequency", &size); - if (clk && size == sizeof(*clk) && *clk) - pltfm_host->clock = be32_to_cpup(clk); + clk = of_get_property(np, "clock-frequency", &size); + if (clk && size == sizeof(*clk) && *clk) + pltfm_host->clock = be32_to_cpup(clk); - if (of_find_property(np, "keep-power-in-suspend", NULL)) - host->mmc->pm_caps |= MMC_PM_KEEP_POWER; + if (of_find_property(np, "keep-power-in-suspend", NULL)) + host->mmc->pm_caps |= MMC_PM_KEEP_POWER; - if (of_find_property(np, "enable-sdio-wakeup", NULL)) - host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; - } + if (of_find_property(np, "enable-sdio-wakeup", NULL)) + host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; } #else void sdhci_get_of_property(struct platform_device *pdev) {} @@ -225,9 +223,11 @@ EXPORT_SYMBOL_GPL(sdhci_pltfm_register); int sdhci_pltfm_unregister(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); sdhci_remove_host(host, dead); + clk_disable_unprepare(pltfm_host->clk); sdhci_pltfm_free(pdev); return 0; diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index f6f82ec..32848eb 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -20,17 +20,9 @@ #define SIRF_TUNING_COUNT 128 struct sdhci_sirf_priv { - struct clk *clk; int gpio_cd; }; -static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host) -{ - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host); - return clk_get_rate(priv->clk); -} - static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width) { u8 ctrl; @@ -56,7 +48,7 @@ static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode) int tuning_seq_cnt = 3; u8 phase, tuned_phases[SIRF_TUNING_COUNT]; u8 tuned_phase_cnt = 0; - int rc, longest_range = 0; + int rc = 0, longest_range = 0; int start = -1, end = 0, tuning_value = -1, range = 0; u16 clock_setting; struct mmc_host *mmc = host->mmc; @@ -68,7 +60,7 @@ retry: phase = 0; do { sdhci_writel(host, - clock_setting | phase | (phase << 7) | (phase << 16), + clock_setting | phase, SDHCI_CLK_DELAY_SETTING); if (!mmc_send_tuning(mmc)) { @@ -102,7 +94,7 @@ retry: */ phase = tuning_value; sdhci_writel(host, - clock_setting | phase | (phase << 7) | (phase << 16), + clock_setting | phase, SDHCI_CLK_DELAY_SETTING); dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n", @@ -122,7 +114,7 @@ retry: static struct sdhci_ops sdhci_sirf_ops = { .platform_execute_tuning = sdhci_sirf_execute_tuning, .set_clock = sdhci_set_clock, - .get_max_clock = sdhci_sirf_get_max_clk, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, .set_bus_width = sdhci_sirf_set_bus_width, .reset = sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, @@ -162,13 +154,13 @@ static int sdhci_sirf_probe(struct platform_device *pdev) return PTR_ERR(host); pltfm_host = sdhci_priv(host); + pltfm_host->clk = clk; priv = sdhci_pltfm_priv(pltfm_host); - priv->clk = clk; priv->gpio_cd = gpio_cd; sdhci_get_of_property(pdev); - ret = clk_prepare_enable(priv->clk); + ret = clk_prepare_enable(pltfm_host->clk); if (ret) goto err_clk_prepare; @@ -195,37 +187,24 @@ static int sdhci_sirf_probe(struct platform_device *pdev) err_request_cd: sdhci_remove_host(host, 0); err_sdhci_add: - clk_disable_unprepare(priv->clk); + clk_disable_unprepare(pltfm_host->clk); err_clk_prepare: sdhci_pltfm_free(pdev); return ret; } -static int sdhci_sirf_remove(struct platform_device *pdev) -{ - struct sdhci_host *host = platform_get_drvdata(pdev); - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host); - - sdhci_pltfm_unregister(pdev); - - clk_disable_unprepare(priv->clk); - return 0; -} - #ifdef CONFIG_PM_SLEEP static int sdhci_sirf_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host); int ret; ret = sdhci_suspend_host(host); if (ret) return ret; - clk_disable(priv->clk); + clk_disable(pltfm_host->clk); return 0; } @@ -234,10 +213,9 @@ static int sdhci_sirf_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host); int ret; - ret = clk_enable(priv->clk); + ret = clk_enable(pltfm_host->clk); if (ret) { dev_dbg(dev, "Resume: Error enabling clock\n"); return ret; @@ -264,7 +242,7 @@ static struct platform_driver sdhci_sirf_driver = { #endif }, .probe = sdhci_sirf_probe, - .remove = sdhci_sirf_remove, + .remove = sdhci_pltfm_unregister, }; module_platform_driver(sdhci_sirf_driver); diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 22e5826..df08834 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -26,14 +26,13 @@ #include <linux/pm.h> #include <linux/slab.h> #include <linux/mmc/host.h> -#include <linux/mmc/sdhci-spear.h> #include <linux/mmc/slot-gpio.h> #include <linux/io.h> #include "sdhci.h" struct spear_sdhci { struct clk *clk; - struct sdhci_plat_data *data; + int card_int_gpio; }; /* sdhci ops */ @@ -44,38 +43,20 @@ static const struct sdhci_ops sdhci_pltfm_ops = { .set_uhs_signaling = sdhci_set_uhs_signaling, }; -#ifdef CONFIG_OF -static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev) +static void sdhci_probe_config_dt(struct device_node *np, + struct spear_sdhci *host) { - struct device_node *np = pdev->dev.of_node; - struct sdhci_plat_data *pdata = NULL; int cd_gpio; cd_gpio = of_get_named_gpio(np, "cd-gpios", 0); if (!gpio_is_valid(cd_gpio)) cd_gpio = -1; - /* If pdata is required */ - if (cd_gpio != -1) { - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - dev_err(&pdev->dev, "DT: kzalloc failed\n"); - else - pdata->card_int_gpio = cd_gpio; - } - - return pdata; -} -#else -static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev) -{ - return ERR_PTR(-ENOSYS); + host->card_int_gpio = cd_gpio; } -#endif static int sdhci_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; struct sdhci_host *host; struct resource *iomem; struct spear_sdhci *sdhci; @@ -124,28 +105,18 @@ static int sdhci_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Error setting desired clk, clk=%lu\n", clk_get_rate(sdhci->clk)); - if (np) { - sdhci->data = sdhci_probe_config_dt(pdev); - if (IS_ERR(sdhci->data)) { - dev_err(&pdev->dev, "DT: Failed to get pdata\n"); - goto disable_clk; - } - } else { - sdhci->data = dev_get_platdata(&pdev->dev); - } - + sdhci_probe_config_dt(pdev->dev.of_node, sdhci); /* * It is optional to use GPIOs for sdhci card detection. If - * sdhci->data is NULL, then use original sdhci lines otherwise + * sdhci->card_int_gpio < 0, then use original sdhci lines otherwise * GPIO lines. We use the built-in GPIO support for this. */ - if (sdhci->data && sdhci->data->card_int_gpio >= 0) { - ret = mmc_gpio_request_cd(host->mmc, - sdhci->data->card_int_gpio, 0); + if (sdhci->card_int_gpio >= 0) { + ret = mmc_gpio_request_cd(host->mmc, sdhci->card_int_gpio, 0); if (ret < 0) { dev_dbg(&pdev->dev, "failed to request card-detect gpio%d\n", - sdhci->data->card_int_gpio); + sdhci->card_int_gpio); goto disable_clk; } } diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c index 882b07e..682f2bb 100644 --- a/drivers/mmc/host/sdhci-st.c +++ b/drivers/mmc/host/sdhci-st.c @@ -23,9 +23,295 @@ #include <linux/module.h> #include <linux/err.h> #include <linux/mmc/host.h> - +#include <linux/reset.h> #include "sdhci-pltfm.h" +struct st_mmc_platform_data { + struct reset_control *rstc; + void __iomem *top_ioaddr; +}; + +/* MMCSS glue logic to setup the HC on some ST SoCs (e.g. STiH407 family) */ + +#define ST_MMC_CCONFIG_REG_1 0x400 +#define ST_MMC_CCONFIG_TIMEOUT_CLK_UNIT BIT(24) +#define ST_MMC_CCONFIG_TIMEOUT_CLK_FREQ BIT(12) +#define ST_MMC_CCONFIG_TUNING_COUNT_DEFAULT BIT(8) +#define ST_MMC_CCONFIG_ASYNC_WAKEUP BIT(0) +#define ST_MMC_CCONFIG_1_DEFAULT \ + ((ST_MMC_CCONFIG_TIMEOUT_CLK_UNIT) | \ + (ST_MMC_CCONFIG_TIMEOUT_CLK_FREQ) | \ + (ST_MMC_CCONFIG_TUNING_COUNT_DEFAULT)) + +#define ST_MMC_CCONFIG_REG_2 0x404 +#define ST_MMC_CCONFIG_HIGH_SPEED BIT(28) +#define ST_MMC_CCONFIG_ADMA2 BIT(24) +#define ST_MMC_CCONFIG_8BIT BIT(20) +#define ST_MMC_CCONFIG_MAX_BLK_LEN 16 +#define MAX_BLK_LEN_1024 1 +#define MAX_BLK_LEN_2048 2 +#define BASE_CLK_FREQ_200 0xc8 +#define BASE_CLK_FREQ_100 0x64 +#define BASE_CLK_FREQ_50 0x32 +#define ST_MMC_CCONFIG_2_DEFAULT \ + (ST_MMC_CCONFIG_HIGH_SPEED | ST_MMC_CCONFIG_ADMA2 | \ + ST_MMC_CCONFIG_8BIT | \ + (MAX_BLK_LEN_1024 << ST_MMC_CCONFIG_MAX_BLK_LEN)) + +#define ST_MMC_CCONFIG_REG_3 0x408 +#define ST_MMC_CCONFIG_EMMC_SLOT_TYPE BIT(28) +#define ST_MMC_CCONFIG_64BIT BIT(24) +#define ST_MMC_CCONFIG_ASYNCH_INTR_SUPPORT BIT(20) +#define ST_MMC_CCONFIG_1P8_VOLT BIT(16) +#define ST_MMC_CCONFIG_3P0_VOLT BIT(12) +#define ST_MMC_CCONFIG_3P3_VOLT BIT(8) +#define ST_MMC_CCONFIG_SUSP_RES_SUPPORT BIT(4) +#define ST_MMC_CCONFIG_SDMA BIT(0) +#define ST_MMC_CCONFIG_3_DEFAULT \ + (ST_MMC_CCONFIG_ASYNCH_INTR_SUPPORT | \ + ST_MMC_CCONFIG_3P3_VOLT | \ + ST_MMC_CCONFIG_SUSP_RES_SUPPORT | \ + ST_MMC_CCONFIG_SDMA) + +#define ST_MMC_CCONFIG_REG_4 0x40c +#define ST_MMC_CCONFIG_D_DRIVER BIT(20) +#define ST_MMC_CCONFIG_C_DRIVER BIT(16) +#define ST_MMC_CCONFIG_A_DRIVER BIT(12) +#define ST_MMC_CCONFIG_DDR50 BIT(8) +#define ST_MMC_CCONFIG_SDR104 BIT(4) +#define ST_MMC_CCONFIG_SDR50 BIT(0) +#define ST_MMC_CCONFIG_4_DEFAULT 0 + +#define ST_MMC_CCONFIG_REG_5 0x410 +#define ST_MMC_CCONFIG_TUNING_FOR_SDR50 BIT(8) +#define RETUNING_TIMER_CNT_MAX 0xf +#define ST_MMC_CCONFIG_5_DEFAULT 0 + +/* I/O configuration for Arasan IP */ +#define ST_MMC_GP_OUTPUT 0x450 +#define ST_MMC_GP_OUTPUT_CD BIT(12) + +#define ST_MMC_STATUS_R 0x460 + +#define ST_TOP_MMC_DLY_FIX_OFF(x) (x - 0x8) + +/* TOP config registers to manage static and dynamic delay */ +#define ST_TOP_MMC_TX_CLK_DLY ST_TOP_MMC_DLY_FIX_OFF(0x8) +#define ST_TOP_MMC_RX_CLK_DLY ST_TOP_MMC_DLY_FIX_OFF(0xc) +/* MMC delay control register */ +#define ST_TOP_MMC_DLY_CTRL ST_TOP_MMC_DLY_FIX_OFF(0x18) +#define ST_TOP_MMC_DLY_CTRL_DLL_BYPASS_CMD BIT(0) +#define ST_TOP_MMC_DLY_CTRL_DLL_BYPASS_PH_SEL BIT(1) +#define ST_TOP_MMC_DLY_CTRL_TX_DLL_ENABLE BIT(8) +#define ST_TOP_MMC_DLY_CTRL_RX_DLL_ENABLE BIT(9) +#define ST_TOP_MMC_DLY_CTRL_ATUNE_NOT_CFG_DLY BIT(10) +#define ST_TOP_MMC_START_DLL_LOCK BIT(11) + +/* register to provide the phase-shift value for DLL */ +#define ST_TOP_MMC_TX_DLL_STEP_DLY ST_TOP_MMC_DLY_FIX_OFF(0x1c) +#define ST_TOP_MMC_RX_DLL_STEP_DLY ST_TOP_MMC_DLY_FIX_OFF(0x20) +#define ST_TOP_MMC_RX_CMD_STEP_DLY ST_TOP_MMC_DLY_FIX_OFF(0x24) + +/* phase shift delay on the tx clk 2.188ns */ +#define ST_TOP_MMC_TX_DLL_STEP_DLY_VALID 0x6 + +#define ST_TOP_MMC_DLY_MAX 0xf + +#define ST_TOP_MMC_DYN_DLY_CONF \ + (ST_TOP_MMC_DLY_CTRL_TX_DLL_ENABLE | \ + ST_TOP_MMC_DLY_CTRL_ATUNE_NOT_CFG_DLY | \ + ST_TOP_MMC_START_DLL_LOCK) + +/* + * For clock speeds greater than 90MHz, we need to check that the + * DLL procedure has finished before switching to ultra-speed modes. + */ +#define CLK_TO_CHECK_DLL_LOCK 90000000 + +static inline void st_mmcss_set_static_delay(void __iomem *ioaddr) +{ + if (!ioaddr) + return; + + writel_relaxed(0x0, ioaddr + ST_TOP_MMC_DLY_CTRL); + writel_relaxed(ST_TOP_MMC_DLY_MAX, + ioaddr + ST_TOP_MMC_TX_CLK_DLY); +} + +/** + * st_mmcss_cconfig: configure the Arasan HC inside the flashSS. + * @np: dt device node. + * @host: sdhci host + * Description: this function is to configure the Arasan host controller. + * On some ST SoCs, i.e. STiH407 family, the MMC devices inside a dedicated + * flashSS sub-system which needs to be configured to be compliant to eMMC 4.5 + * or eMMC4.3. This has to be done before registering the sdhci host. + */ +static void st_mmcss_cconfig(struct device_node *np, struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct mmc_host *mhost = host->mmc; + u32 cconf2, cconf3, cconf4, cconf5; + + if (!of_device_is_compatible(np, "st,sdhci-stih407")) + return; + + cconf2 = ST_MMC_CCONFIG_2_DEFAULT; + cconf3 = ST_MMC_CCONFIG_3_DEFAULT; + cconf4 = ST_MMC_CCONFIG_4_DEFAULT; + cconf5 = ST_MMC_CCONFIG_5_DEFAULT; + + writel_relaxed(ST_MMC_CCONFIG_1_DEFAULT, + host->ioaddr + ST_MMC_CCONFIG_REG_1); + + /* Set clock frequency, default to 50MHz if max-frequency is not + * provided */ + + switch (mhost->f_max) { + case 200000000: + clk_set_rate(pltfm_host->clk, mhost->f_max); + cconf2 |= BASE_CLK_FREQ_200; + break; + case 100000000: + clk_set_rate(pltfm_host->clk, mhost->f_max); + cconf2 |= BASE_CLK_FREQ_100; + break; + default: + clk_set_rate(pltfm_host->clk, 50000000); + cconf2 |= BASE_CLK_FREQ_50; + } + + writel_relaxed(cconf2, host->ioaddr + ST_MMC_CCONFIG_REG_2); + + if (mhost->caps & MMC_CAP_NONREMOVABLE) + cconf3 |= ST_MMC_CCONFIG_EMMC_SLOT_TYPE; + else + /* CARD _D ET_CTRL */ + writel_relaxed(ST_MMC_GP_OUTPUT_CD, + host->ioaddr + ST_MMC_GP_OUTPUT); + + if (mhost->caps & MMC_CAP_UHS_SDR50) { + /* use 1.8V */ + cconf3 |= ST_MMC_CCONFIG_1P8_VOLT; + cconf4 |= ST_MMC_CCONFIG_SDR50; + /* Use tuning */ + cconf5 |= ST_MMC_CCONFIG_TUNING_FOR_SDR50; + /* Max timeout for retuning */ + cconf5 |= RETUNING_TIMER_CNT_MAX; + } + + if (mhost->caps & MMC_CAP_UHS_SDR104) { + /* + * SDR104 implies the HC can support HS200 mode, so + * it's mandatory to use 1.8V + */ + cconf3 |= ST_MMC_CCONFIG_1P8_VOLT; + cconf4 |= ST_MMC_CCONFIG_SDR104; + /* Max timeout for retuning */ + cconf5 |= RETUNING_TIMER_CNT_MAX; + } + + if (mhost->caps & MMC_CAP_UHS_DDR50) + cconf4 |= ST_MMC_CCONFIG_DDR50; + + writel_relaxed(cconf3, host->ioaddr + ST_MMC_CCONFIG_REG_3); + writel_relaxed(cconf4, host->ioaddr + ST_MMC_CCONFIG_REG_4); + writel_relaxed(cconf5, host->ioaddr + ST_MMC_CCONFIG_REG_5); +} + +static inline void st_mmcss_set_dll(void __iomem *ioaddr) +{ + if (!ioaddr) + return; + + writel_relaxed(ST_TOP_MMC_DYN_DLY_CONF, ioaddr + ST_TOP_MMC_DLY_CTRL); + writel_relaxed(ST_TOP_MMC_TX_DLL_STEP_DLY_VALID, + ioaddr + ST_TOP_MMC_TX_DLL_STEP_DLY); +} + +static int st_mmcss_lock_dll(void __iomem *ioaddr) +{ + unsigned long curr, value; + unsigned long finish = jiffies + HZ; + + /* Checks if the DLL procedure is finished */ + do { + curr = jiffies; + value = readl(ioaddr + ST_MMC_STATUS_R); + if (value & 0x1) + return 0; + + cpu_relax(); + } while (!time_after_eq(curr, finish)); + + return -EBUSY; +} + +static int sdhci_st_set_dll_for_clock(struct sdhci_host *host) +{ + int ret = 0; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct st_mmc_platform_data *pdata = pltfm_host->priv; + + if (host->clock > CLK_TO_CHECK_DLL_LOCK) { + st_mmcss_set_dll(pdata->top_ioaddr); + ret = st_mmcss_lock_dll(host->ioaddr); + } + + return ret; +} + +static void sdhci_st_set_uhs_signaling(struct sdhci_host *host, + unsigned int uhs) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct st_mmc_platform_data *pdata = pltfm_host->priv; + u16 ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); + int ret = 0; + + /* Select Bus Speed Mode for host */ + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; + switch (uhs) { + /* + * Set V18_EN -- UHS modes do not work without this. + * does not change signaling voltage + */ + + case MMC_TIMING_UHS_SDR12: + st_mmcss_set_static_delay(pdata->top_ioaddr); + ctrl_2 |= SDHCI_CTRL_UHS_SDR12 | SDHCI_CTRL_VDD_180; + break; + case MMC_TIMING_UHS_SDR25: + st_mmcss_set_static_delay(pdata->top_ioaddr); + ctrl_2 |= SDHCI_CTRL_UHS_SDR25 | SDHCI_CTRL_VDD_180; + break; + case MMC_TIMING_UHS_SDR50: + st_mmcss_set_static_delay(pdata->top_ioaddr); + ctrl_2 |= SDHCI_CTRL_UHS_SDR50 | SDHCI_CTRL_VDD_180; + ret = sdhci_st_set_dll_for_clock(host); + break; + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: + st_mmcss_set_static_delay(pdata->top_ioaddr); + ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180; + ret = sdhci_st_set_dll_for_clock(host); + break; + case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: + st_mmcss_set_static_delay(pdata->top_ioaddr); + ctrl_2 |= SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180; + break; + } + + if (ret) + dev_warn(mmc_dev(host->mmc), "Error setting dll for clock " + "(uhs %d)\n", uhs); + + dev_dbg(mmc_dev(host->mmc), "uhs %d, ctrl_2 %04X\n", uhs, ctrl_2); + + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); +} + static u32 sdhci_st_readl(struct sdhci_host *host, int reg) { u32 ret; @@ -48,22 +334,33 @@ static const struct sdhci_ops sdhci_st_ops = { .set_bus_width = sdhci_set_bus_width, .read_l = sdhci_st_readl, .reset = sdhci_reset, + .set_uhs_signaling = sdhci_st_set_uhs_signaling, }; static const struct sdhci_pltfm_data sdhci_st_pdata = { .ops = &sdhci_st_ops, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | - SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_NO_HISPD_BIT, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_STOP_WITH_TC, }; static int sdhci_st_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct sdhci_host *host; + struct st_mmc_platform_data *pdata; struct sdhci_pltfm_host *pltfm_host; struct clk *clk; int ret = 0; u16 host_version; + struct resource *res; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; clk = devm_clk_get(&pdev->dev, "mmc"); if (IS_ERR(clk)) { @@ -71,10 +368,17 @@ static int sdhci_st_probe(struct platform_device *pdev) return PTR_ERR(clk); } + pdata->rstc = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(pdata->rstc)) + pdata->rstc = NULL; + else + reset_control_deassert(pdata->rstc); + host = sdhci_pltfm_init(pdev, &sdhci_st_pdata, 0); if (IS_ERR(host)) { dev_err(&pdev->dev, "Failed sdhci_pltfm_init\n"); - return PTR_ERR(host); + ret = PTR_ERR(host); + goto err_pltfm_init; } ret = mmc_of_parse(host->mmc); @@ -85,9 +389,22 @@ static int sdhci_st_probe(struct platform_device *pdev) clk_prepare_enable(clk); + /* Configure the FlashSS Top registers for setting eMMC TX/RX delay */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "top-mmc-delay"); + pdata->top_ioaddr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pdata->top_ioaddr)) { + dev_warn(&pdev->dev, "FlashSS Top Dly registers not available"); + pdata->top_ioaddr = NULL; + } + pltfm_host = sdhci_priv(host); + pltfm_host->priv = pdata; pltfm_host->clk = clk; + /* Configure the Arasan HC inside the flashSS */ + st_mmcss_cconfig(np, host); + ret = sdhci_add_host(host); if (ret) { dev_err(&pdev->dev, "Failed sdhci_add_host\n"); @@ -109,6 +426,9 @@ err_out: clk_disable_unprepare(clk); err_of: sdhci_pltfm_free(pdev); +err_pltfm_init: + if (pdata->rstc) + reset_control_assert(pdata->rstc); return ret; } @@ -117,10 +437,15 @@ static int sdhci_st_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct st_mmc_platform_data *pdata = pltfm_host->priv; + int ret; - clk_disable_unprepare(pltfm_host->clk); + ret = sdhci_pltfm_unregister(pdev); + + if (pdata->rstc) + reset_control_assert(pdata->rstc); - return sdhci_pltfm_unregister(pdev); + return ret; } #ifdef CONFIG_PM_SLEEP @@ -128,11 +453,15 @@ static int sdhci_st_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct st_mmc_platform_data *pdata = pltfm_host->priv; int ret = sdhci_suspend_host(host); if (ret) goto out; + if (pdata->rstc) + reset_control_assert(pdata->rstc); + clk_disable_unprepare(pltfm_host->clk); out: return ret; @@ -142,9 +471,16 @@ static int sdhci_st_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct st_mmc_platform_data *pdata = pltfm_host->priv; + struct device_node *np = dev->of_node; clk_prepare_enable(pltfm_host->clk); + if (pdata->rstc) + reset_control_deassert(pdata->rstc); + + st_mmcss_cconfig(np, host); + return sdhci_resume_host(host); } #endif diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index f3778d5..ad28b49 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -20,11 +20,10 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/of_gpio.h> -#include <linux/gpio.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> #include <linux/mmc/slot-gpio.h> +#include <linux/gpio/consumer.h> #include "sdhci-pltfm.h" @@ -41,7 +40,6 @@ #define NVQUIRK_DISABLE_SDR50 BIT(3) #define NVQUIRK_DISABLE_SDR104 BIT(4) #define NVQUIRK_DISABLE_DDR50 BIT(5) -#define NVQUIRK_SHADOW_XFER_MODE_REG BIT(6) struct sdhci_tegra_soc_data { const struct sdhci_pltfm_data *pdata; @@ -50,7 +48,7 @@ struct sdhci_tegra_soc_data { struct sdhci_tegra { const struct sdhci_tegra_soc_data *soc_data; - int power_gpio; + struct gpio_desc *power_gpio; }; static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) @@ -71,23 +69,19 @@ static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_tegra *tegra_host = pltfm_host->priv; - const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; - if (soc_data->nvquirks & NVQUIRK_SHADOW_XFER_MODE_REG) { - switch (reg) { - case SDHCI_TRANSFER_MODE: - /* - * Postpone this write, we must do it together with a - * command write that is down below. - */ - pltfm_host->xfer_mode_shadow = val; - return; - case SDHCI_COMMAND: - writel((val << 16) | pltfm_host->xfer_mode_shadow, - host->ioaddr + SDHCI_TRANSFER_MODE); - return; - } + switch (reg) { + case SDHCI_TRANSFER_MODE: + /* + * Postpone this write, we must do it together with a + * command write that is down below. + */ + pltfm_host->xfer_mode_shadow = val; + return; + case SDHCI_COMMAND: + writel((val << 16) | pltfm_host->xfer_mode_shadow, + host->ioaddr + SDHCI_TRANSFER_MODE); + return; } writew(val, host->ioaddr + reg); @@ -173,7 +167,6 @@ static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) static const struct sdhci_ops tegra_sdhci_ops = { .get_ro = tegra_sdhci_get_ro, .read_w = tegra_sdhci_readw, - .write_w = tegra_sdhci_writew, .write_l = tegra_sdhci_writel, .set_clock = sdhci_set_clock, .set_bus_width = tegra_sdhci_set_bus_width, @@ -214,6 +207,18 @@ static struct sdhci_tegra_soc_data soc_data_tegra30 = { NVQUIRK_DISABLE_SDR104, }; +static const struct sdhci_ops tegra114_sdhci_ops = { + .get_ro = tegra_sdhci_get_ro, + .read_w = tegra_sdhci_readw, + .write_w = tegra_sdhci_writew, + .write_l = tegra_sdhci_writel, + .set_clock = sdhci_set_clock, + .set_bus_width = tegra_sdhci_set_bus_width, + .reset = tegra_sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, +}; + static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | @@ -221,15 +226,14 @@ static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, - .ops = &tegra_sdhci_ops, + .ops = &tegra114_sdhci_ops, }; static struct sdhci_tegra_soc_data soc_data_tegra114 = { .pdata = &sdhci_tegra114_pdata, .nvquirks = NVQUIRK_DISABLE_SDR50 | NVQUIRK_DISABLE_DDR50 | - NVQUIRK_DISABLE_SDR104 | - NVQUIRK_SHADOW_XFER_MODE_REG, + NVQUIRK_DISABLE_SDR104, }; static const struct of_device_id sdhci_tegra_dt_match[] = { @@ -241,17 +245,6 @@ static const struct of_device_id sdhci_tegra_dt_match[] = { }; MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match); -static int sdhci_tegra_parse_dt(struct device *dev) -{ - struct device_node *np = dev->of_node; - struct sdhci_host *host = dev_get_drvdata(dev); - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_tegra *tegra_host = pltfm_host->priv; - - tegra_host->power_gpio = of_get_named_gpio(np, "power-gpios", 0); - return mmc_of_parse(host->mmc); -} - static int sdhci_tegra_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -281,21 +274,18 @@ static int sdhci_tegra_probe(struct platform_device *pdev) tegra_host->soc_data = soc_data; pltfm_host->priv = tegra_host; - rc = sdhci_tegra_parse_dt(&pdev->dev); + rc = mmc_of_parse(host->mmc); if (rc) goto err_parse_dt; - if (gpio_is_valid(tegra_host->power_gpio)) { - rc = gpio_request(tegra_host->power_gpio, "sdhci_power"); - if (rc) { - dev_err(mmc_dev(host->mmc), - "failed to allocate power gpio\n"); - goto err_power_req; - } - gpio_direction_output(tegra_host->power_gpio, 1); + tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", + GPIOD_OUT_HIGH); + if (IS_ERR(tegra_host->power_gpio)) { + rc = PTR_ERR(tegra_host->power_gpio); + goto err_power_req; } - clk = clk_get(mmc_dev(host->mmc), NULL); + clk = devm_clk_get(mmc_dev(host->mmc), NULL); if (IS_ERR(clk)) { dev_err(mmc_dev(host->mmc), "clk err\n"); rc = PTR_ERR(clk); @@ -312,10 +302,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev) err_add_host: clk_disable_unprepare(pltfm_host->clk); - clk_put(pltfm_host->clk); err_clk_get: - if (gpio_is_valid(tegra_host->power_gpio)) - gpio_free(tegra_host->power_gpio); err_power_req: err_parse_dt: err_alloc_tegra_host: @@ -323,26 +310,6 @@ err_alloc_tegra_host: return rc; } -static int sdhci_tegra_remove(struct platform_device *pdev) -{ - struct sdhci_host *host = platform_get_drvdata(pdev); - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_tegra *tegra_host = pltfm_host->priv; - int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); - - sdhci_remove_host(host, dead); - - if (gpio_is_valid(tegra_host->power_gpio)) - gpio_free(tegra_host->power_gpio); - - clk_disable_unprepare(pltfm_host->clk); - clk_put(pltfm_host->clk); - - sdhci_pltfm_free(pdev); - - return 0; -} - static struct platform_driver sdhci_tegra_driver = { .driver = { .name = "sdhci-tegra", @@ -350,7 +317,7 @@ static struct platform_driver sdhci_tegra_driver = { .pm = SDHCI_PLTFM_PMOPS, }, .probe = sdhci_tegra_probe, - .remove = sdhci_tegra_remove, + .remove = sdhci_pltfm_unregister, }; module_platform_driver(sdhci_tegra_driver); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 0ad412a..c80287a 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -28,6 +28,7 @@ #include <linux/mmc/mmc.h> #include <linux/mmc/host.h> #include <linux/mmc/card.h> +#include <linux/mmc/sdio.h> #include <linux/mmc/slot-gpio.h> #include "sdhci.h" @@ -56,6 +57,7 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); static int sdhci_pre_dma_transfer(struct sdhci_host *host, struct mmc_data *data, struct sdhci_host_next *next); +static int sdhci_do_get_cd(struct sdhci_host *host); #ifdef CONFIG_PM static int sdhci_runtime_pm_get(struct sdhci_host *host); @@ -931,7 +933,8 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, * If we are sending CMD23, CMD12 never gets sent * on successful completion (so no Auto-CMD12). */ - if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) + if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12) && + (cmd->opcode != SD_IO_RW_EXTENDED)) mode |= SDHCI_TRNS_AUTO_CMD12; else if (host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) { mode |= SDHCI_TRNS_AUTO_CMD23; @@ -1356,7 +1359,8 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) sdhci_runtime_pm_get(host); - present = mmc_gpio_get_cd(host->mmc); + /* Firstly check card presence */ + present = sdhci_do_get_cd(host); spin_lock_irqsave(&host->lock, flags); @@ -1379,22 +1383,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) host->mrq = mrq; - /* - * Firstly check card presence from cd-gpio. The return could - * be one of the following possibilities: - * negative: cd-gpio is not available - * zero: cd-gpio is used, and card is removed - * one: cd-gpio is used, and card is present - */ - if (present < 0) { - /* If polling, assume that the card is always present. */ - if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) - present = 1; - else - present = sdhci_readl(host, SDHCI_PRESENT_STATE) & - SDHCI_CARD_PRESENT; - } - if (!present || host->flags & SDHCI_DEVICE_DEAD) { host->mrq->cmd->error = -ENOMEDIUM; tasklet_schedule(&host->finish_tasklet); @@ -3164,7 +3152,8 @@ int sdhci_add_host(struct sdhci_host *host) /* Auto-CMD23 stuff only works in ADMA or PIO. */ if ((host->version >= SDHCI_SPEC_300) && ((host->flags & SDHCI_USE_ADMA) || - !(host->flags & SDHCI_USE_SDMA))) { + !(host->flags & SDHCI_USE_SDMA)) && + !(host->quirks2 & SDHCI_QUIRK2_ACMD23_BROKEN)) { host->flags |= SDHCI_AUTO_CMD23; DBG("%s: Auto-CMD23 available\n", mmc_hostname(mmc)); } else { diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 0315e18..e639b7f 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -18,7 +18,7 @@ #include <linux/types.h> #include <linux/io.h> -#include <linux/mmc/sdhci.h> +#include <linux/mmc/host.h> /* * Controller registers @@ -309,6 +309,207 @@ struct sdhci_adma2_64_desc { */ #define SDHCI_MAX_SEGS 128 +struct sdhci_host_next { + unsigned int sg_count; + s32 cookie; +}; + +struct sdhci_host { + /* Data set by hardware interface driver */ + const char *hw_name; /* Hardware bus name */ + + unsigned int quirks; /* Deviations from spec. */ + +/* Controller doesn't honor resets unless we touch the clock register */ +#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0) +/* Controller has bad caps bits, but really supports DMA */ +#define SDHCI_QUIRK_FORCE_DMA (1<<1) +/* Controller doesn't like to be reset when there is no card inserted. */ +#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2) +/* Controller doesn't like clearing the power reg before a change */ +#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3) +/* Controller has flaky internal state so reset it on each ios change */ +#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4) +/* Controller has an unusable DMA engine */ +#define SDHCI_QUIRK_BROKEN_DMA (1<<5) +/* Controller has an unusable ADMA engine */ +#define SDHCI_QUIRK_BROKEN_ADMA (1<<6) +/* Controller can only DMA from 32-bit aligned addresses */ +#define SDHCI_QUIRK_32BIT_DMA_ADDR (1<<7) +/* Controller can only DMA chunk sizes that are a multiple of 32 bits */ +#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<8) +/* Controller can only ADMA chunks that are a multiple of 32 bits */ +#define SDHCI_QUIRK_32BIT_ADMA_SIZE (1<<9) +/* Controller needs to be reset after each request to stay stable */ +#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<10) +/* Controller needs voltage and power writes to happen separately */ +#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<11) +/* Controller provides an incorrect timeout value for transfers */ +#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12) +/* Controller has an issue with buffer bits for small transfers */ +#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13) +/* Controller does not provide transfer-complete interrupt when not busy */ +#define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14) +/* Controller has unreliable card detection */ +#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15) +/* Controller reports inverted write-protect state */ +#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16) +/* Controller does not like fast PIO transfers */ +#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) +/* Controller has to be forced to use block size of 2048 bytes */ +#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20) +/* Controller cannot do multi-block transfers */ +#define SDHCI_QUIRK_NO_MULTIBLOCK (1<<21) +/* Controller can only handle 1-bit data transfers */ +#define SDHCI_QUIRK_FORCE_1_BIT_DATA (1<<22) +/* Controller needs 10ms delay between applying power and clock */ +#define SDHCI_QUIRK_DELAY_AFTER_POWER (1<<23) +/* Controller uses SDCLK instead of TMCLK for data timeouts */ +#define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1<<24) +/* Controller reports wrong base clock capability */ +#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25) +/* Controller cannot support End Attribute in NOP ADMA descriptor */ +#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26) +/* Controller is missing device caps. Use caps provided by host */ +#define SDHCI_QUIRK_MISSING_CAPS (1<<27) +/* Controller uses Auto CMD12 command to stop the transfer */ +#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28) +/* Controller doesn't have HISPD bit field in HI-SPEED SD card */ +#define SDHCI_QUIRK_NO_HISPD_BIT (1<<29) +/* Controller treats ADMA descriptors with length 0000h incorrectly */ +#define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC (1<<30) +/* The read-only detection via SDHCI_PRESENT_STATE register is unstable */ +#define SDHCI_QUIRK_UNSTABLE_RO_DETECT (1<<31) + + unsigned int quirks2; /* More deviations from spec. */ + +#define SDHCI_QUIRK2_HOST_OFF_CARD_ON (1<<0) +#define SDHCI_QUIRK2_HOST_NO_CMD23 (1<<1) +/* The system physically doesn't support 1.8v, even if the host does */ +#define SDHCI_QUIRK2_NO_1_8_V (1<<2) +#define SDHCI_QUIRK2_PRESET_VALUE_BROKEN (1<<3) +#define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1<<4) +/* Controller has a non-standard host control register */ +#define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5) +/* Controller does not support HS200 */ +#define SDHCI_QUIRK2_BROKEN_HS200 (1<<6) +/* Controller does not support DDR50 */ +#define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7) +/* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */ +#define SDHCI_QUIRK2_STOP_WITH_TC (1<<8) +/* Controller does not support 64-bit DMA */ +#define SDHCI_QUIRK2_BROKEN_64_BIT_DMA (1<<9) +/* need clear transfer mode register before send cmd */ +#define SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1<<10) +/* Capability register bit-63 indicates HS400 support */ +#define SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1<<11) +/* forced tuned clock */ +#define SDHCI_QUIRK2_TUNING_WORK_AROUND (1<<12) +/* disable the block count for single block transactions */ +#define SDHCI_QUIRK2_SUPPORT_SINGLE (1<<13) +/* Controller broken with using ACMD23 */ +#define SDHCI_QUIRK2_ACMD23_BROKEN (1<<14) + + int irq; /* Device IRQ */ + void __iomem *ioaddr; /* Mapped address */ + + const struct sdhci_ops *ops; /* Low level hw interface */ + + /* Internal data */ + struct mmc_host *mmc; /* MMC structure */ + u64 dma_mask; /* custom DMA mask */ + +#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) + struct led_classdev led; /* LED control */ + char led_name[32]; +#endif + + spinlock_t lock; /* Mutex */ + + int flags; /* Host attributes */ +#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */ +#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */ +#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ +#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ +#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */ +#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */ +#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */ +#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */ +#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */ +#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */ +#define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */ +#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */ +#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */ +#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */ + + unsigned int version; /* SDHCI spec. version */ + + unsigned int max_clk; /* Max possible freq (MHz) */ + unsigned int timeout_clk; /* Timeout freq (KHz) */ + unsigned int clk_mul; /* Clock Muliplier value */ + + unsigned int clock; /* Current clock (MHz) */ + u8 pwr; /* Current voltage */ + + bool runtime_suspended; /* Host is runtime suspended */ + bool bus_on; /* Bus power prevents runtime suspend */ + bool preset_enabled; /* Preset is enabled */ + + struct mmc_request *mrq; /* Current request */ + struct mmc_command *cmd; /* Current command */ + struct mmc_data *data; /* Current data request */ + unsigned int data_early:1; /* Data finished before cmd */ + unsigned int busy_handle:1; /* Handling the order of Busy-end */ + + struct sg_mapping_iter sg_miter; /* SG state for PIO */ + unsigned int blocks; /* remaining PIO blocks */ + + int sg_count; /* Mapped sg entries */ + + void *adma_table; /* ADMA descriptor table */ + void *align_buffer; /* Bounce buffer */ + + size_t adma_table_sz; /* ADMA descriptor table size */ + size_t align_buffer_sz; /* Bounce buffer size */ + + dma_addr_t adma_addr; /* Mapped ADMA descr. table */ + dma_addr_t align_addr; /* Mapped bounce buffer */ + + unsigned int desc_sz; /* ADMA descriptor size */ + unsigned int align_sz; /* ADMA alignment */ + unsigned int align_mask; /* ADMA alignment mask */ + + struct tasklet_struct finish_tasklet; /* Tasklet structures */ + + struct timer_list timer; /* Timer for timeouts */ + + u32 caps; /* Alternative CAPABILITY_0 */ + u32 caps1; /* Alternative CAPABILITY_1 */ + + unsigned int ocr_avail_sdio; /* OCR bit masks */ + unsigned int ocr_avail_sd; + unsigned int ocr_avail_mmc; + u32 ocr_mask; /* available voltages */ + + unsigned timing; /* Current timing */ + + u32 thread_isr; + + /* cached registers */ + u32 ier; + + wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ + unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */ + + unsigned int tuning_count; /* Timer count for re-tuning */ + unsigned int tuning_mode; /* Re-tuning mode supported by host */ +#define SDHCI_TUNING_MODE_1 0 + struct timer_list tuning_timer; /* Timer for tuning */ + + struct sdhci_host_next next_data; + unsigned long private[0] ____cacheline_aligned; +}; + struct sdhci_ops { #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS u32 (*read_l)(struct sdhci_host *host, int reg); diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 7d9d6a3..072f670 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -875,6 +875,7 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, struct mmc_command *cmd = mrq->cmd; u32 opc = cmd->opcode; u32 mask; + unsigned long flags; switch (opc) { /* response busy check */ @@ -909,10 +910,12 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, /* set arg */ sh_mmcif_writel(host->addr, MMCIF_CE_ARG, cmd->arg); /* set cmd */ + spin_lock_irqsave(&host->lock, flags); sh_mmcif_writel(host->addr, MMCIF_CE_CMD_SET, opc); host->wait_for = MMCIF_WAIT_FOR_CMD; schedule_delayed_work(&host->timeout_work, host->timeout); + spin_unlock_irqrestore(&host->lock, flags); } static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, @@ -1171,6 +1174,12 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) struct sh_mmcif_host *host = dev_id; struct mmc_request *mrq; bool wait = false; + unsigned long flags; + int wait_work; + + spin_lock_irqsave(&host->lock, flags); + wait_work = host->wait_for; + spin_unlock_irqrestore(&host->lock, flags); cancel_delayed_work_sync(&host->timeout_work); @@ -1188,7 +1197,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) * All handlers return true, if processing continues, and false, if the * request has to be completed - successfully or not */ - switch (host->wait_for) { + switch (wait_work) { case MMCIF_WAIT_FOR_REQUEST: /* We're too late, the timeout has already kicked in */ mutex_unlock(&host->thread_lock); @@ -1312,15 +1321,15 @@ static void mmcif_timeout_work(struct work_struct *work) /* Don't run after mmc_remove_host() */ return; - dev_err(&host->pd->dev, "Timeout waiting for %u on CMD%u\n", - host->wait_for, mrq->cmd->opcode); - spin_lock_irqsave(&host->lock, flags); if (host->state == STATE_IDLE) { spin_unlock_irqrestore(&host->lock, flags); return; } + dev_err(&host->pd->dev, "Timeout waiting for %u on CMD%u\n", + host->wait_for, mrq->cmd->opcode); + host->state = STATE_TIMEOUT; spin_unlock_irqrestore(&host->lock, flags); diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index e8a4218..4d3e1ff 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -293,7 +293,7 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host, struct mmc_data *data) { struct sunxi_idma_des *pdes = (struct sunxi_idma_des *)host->sg_cpu; - struct sunxi_idma_des *pdes_pa = (struct sunxi_idma_des *)host->sg_dma; + dma_addr_t next_desc = host->sg_dma; int i, max_len = (1 << host->idma_des_size_bits); for (i = 0; i < data->sg_len; i++) { @@ -305,8 +305,9 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host, else pdes[i].buf_size = data->sg[i].length; + next_desc += sizeof(struct sunxi_idma_des); pdes[i].buf_addr_ptr1 = sg_dma_address(&data->sg[i]); - pdes[i].buf_addr_ptr2 = (u32)&pdes_pa[i + 1]; + pdes[i].buf_addr_ptr2 = (u32)next_desc; } pdes[0].config |= SDXC_IDMAC_DES0_FD; @@ -930,7 +931,9 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, return PTR_ERR(host->clk_sample); } - host->reset = devm_reset_control_get(&pdev->dev, "ahb"); + host->reset = devm_reset_control_get_optional(&pdev->dev, "ahb"); + if (PTR_ERR(host->reset) == -EPROBE_DEFER) + return PTR_ERR(host->reset); ret = clk_prepare_enable(host->clk_ahb); if (ret) { @@ -1028,7 +1031,7 @@ static int sunxi_mmc_probe(struct platform_device *pdev) mmc->f_min = 400000; mmc->f_max = 50000000; mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_ERASE; + MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ; ret = mmc_of_parse(mmc); if (ret) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index a31c357..dba7e1c 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1073,8 +1073,6 @@ EXPORT_SYMBOL(tmio_mmc_host_alloc); void tmio_mmc_host_free(struct tmio_mmc_host *host) { mmc_free_host(host->mmc); - - host->mmc = NULL; } EXPORT_SYMBOL(tmio_mmc_host_free); diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index dd2e1aa..5af0055 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -744,7 +744,7 @@ static struct wmt_mci_caps wm8505_caps = { .max_blk_size = 2048, }; -static struct of_device_id wmt_mci_dt_ids[] = { +static const struct of_device_id wmt_mci_dt_ids[] = { { .compatible = "wm,wm8505-sdhc", .data = &wm8505_caps }, { /* Sentinel */ }, }; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 5b76a17..5897d8d 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -526,6 +526,7 @@ config MTD_NAND_SUNXI config MTD_NAND_HISI504 tristate "Support for NAND controller on Hisilicon SoC Hip04" + depends on HAS_DMA help Enables support for NAND controller on Hisilicon SoC Hip04. diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c index 289ad3a..8dcc7b8 100644 --- a/drivers/mtd/nand/hisi504_nand.c +++ b/drivers/mtd/nand/hisi504_nand.c @@ -758,8 +758,7 @@ static int hisi_nfc_probe(struct platform_device *pdev) hisi_nfc_host_init(host); - ret = devm_request_irq(dev, irq, hinfc_irq_handle, IRQF_DISABLED, - "nandc", host); + ret = devm_request_irq(dev, irq, hinfc_irq_handle, 0x0, "nandc", host); if (ret) { dev_err(dev, "failed to request IRQ\n"); goto err_res; diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 96b0b1d..10b1f7a 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -480,6 +480,42 @@ static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) nand_writel(info, NDCR, ndcr | int_mask); } +static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len) +{ + if (info->ecc_bch) { + int timeout; + + /* + * According to the datasheet, when reading from NDDB + * with BCH enabled, after each 32 bytes reads, we + * have to make sure that the NDSR.RDDREQ bit is set. + * + * Drain the FIFO 8 32 bits reads at a time, and skip + * the polling on the last read. + */ + while (len > 8) { + __raw_readsl(info->mmio_base + NDDB, data, 8); + + for (timeout = 0; + !(nand_readl(info, NDSR) & NDSR_RDDREQ); + timeout++) { + if (timeout >= 5) { + dev_err(&info->pdev->dev, + "Timeout on RDDREQ while draining the FIFO\n"); + return; + } + + mdelay(1); + } + + data += 32; + len -= 8; + } + } + + __raw_readsl(info->mmio_base + NDDB, data, len); +} + static void handle_data_pio(struct pxa3xx_nand_info *info) { unsigned int do_bytes = min(info->data_size, info->chunk_size); @@ -496,14 +532,14 @@ static void handle_data_pio(struct pxa3xx_nand_info *info) DIV_ROUND_UP(info->oob_size, 4)); break; case STATE_PIO_READING: - __raw_readsl(info->mmio_base + NDDB, - info->data_buff + info->data_buff_pos, - DIV_ROUND_UP(do_bytes, 4)); + drain_fifo(info, + info->data_buff + info->data_buff_pos, + DIV_ROUND_UP(do_bytes, 4)); if (info->oob_size > 0) - __raw_readsl(info->mmio_base + NDDB, - info->oob_buff + info->oob_buff_pos, - DIV_ROUND_UP(info->oob_size, 4)); + drain_fifo(info, + info->oob_buff + info->oob_buff_pos, + DIV_ROUND_UP(info->oob_size, 4)); break; default: dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, @@ -1572,6 +1608,8 @@ static int alloc_nand_resource(struct platform_device *pdev) int ret, irq, cs; pdata = dev_get_platdata(&pdev->dev); + if (pdata->num_cs <= 0) + return -ENODEV; info = devm_kzalloc(&pdev->dev, sizeof(*info) + (sizeof(*mtd) + sizeof(*host)) * pdata->num_cs, GFP_KERNEL); if (!info) diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index da4c792..16e34b3 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -425,9 +425,10 @@ retry: ubi_warn(ubi, "corrupted VID header at PEB %d, LEB %d:%d", pnum, vol_id, lnum); err = -EBADMSG; - } else + } else { err = -EINVAL; ubi_ro_mode(ubi); + } } goto out_free; } else if (err == UBI_IO_BITFLIPS) diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 84673eb..df51d60 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -157,7 +157,7 @@ config IPVLAN making it transparent to the connected L2 switch. Ipvlan devices can be added using the "ip" command from the - iproute2 package starting with the iproute2-X.Y.ZZ release: + iproute2 package starting with the iproute2-3.19 release: "ip link add link <main-dev> [ NAME ] type ipvlan" diff --git a/drivers/net/appletalk/Kconfig b/drivers/net/appletalk/Kconfig index 4ce6ca5..dc6b78e 100644 --- a/drivers/net/appletalk/Kconfig +++ b/drivers/net/appletalk/Kconfig @@ -40,7 +40,7 @@ config DEV_APPLETALK config LTPC tristate "Apple/Farallon LocalTalk PC support" - depends on DEV_APPLETALK && (ISA || EISA) && ISA_DMA_API + depends on DEV_APPLETALK && (ISA || EISA) && ISA_DMA_API && VIRT_TO_BUS help This allows you to use the AppleTalk PC card to connect to LocalTalk networks. The card is also known as the Farallon PhoneNet PC card. diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index b979c26..089a402 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3850,7 +3850,8 @@ static inline int bond_slave_override(struct bonding *bond, /* Find out if any slaves have the same mapping as this skb. */ bond_for_each_slave_rcu(bond, slave, iter) { if (slave->queue_id == skb->queue_mapping) { - if (bond_slave_can_tx(slave)) { + if (bond_slave_is_up(slave) && + slave->link == BOND_LINK_UP) { bond_dev_queue_xmit(bond, skb, slave->dev); return 0; } diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 98d73aa..58808f6 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -131,7 +131,7 @@ config CAN_RCAR config CAN_XILINXCAN tristate "Xilinx CAN" - depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST + depends on ARCH_ZYNQ || ARM64 || MICROBLAZE || COMPILE_TEST depends on COMMON_CLK && HAS_IOMEM ---help--- Xilinx CAN driver. This driver supports both soft AXI CAN IP and diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 3c82e02..b0f6924 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -579,6 +579,10 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) skb->pkt_type = PACKET_BROADCAST; skb->ip_summed = CHECKSUM_UNNECESSARY; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; @@ -603,6 +607,10 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev, skb->pkt_type = PACKET_BROADCAST; skb->ip_summed = CHECKSUM_UNNECESSARY; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 80c46ad..ad0a7e8 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -592,13 +592,12 @@ static int flexcan_poll_state(struct net_device *dev, u32 reg_esr) rx_state = unlikely(reg_esr & FLEXCAN_ESR_RX_WRN) ? CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE; new_state = max(tx_state, rx_state); - } else if (unlikely(flt == FLEXCAN_ESR_FLT_CONF_PASSIVE)) { + } else { __flexcan_get_berr_counter(dev, &bec); - new_state = CAN_STATE_ERROR_PASSIVE; + new_state = flt == FLEXCAN_ESR_FLT_CONF_PASSIVE ? + CAN_STATE_ERROR_PASSIVE : CAN_STATE_BUS_OFF; rx_state = bec.rxerr >= bec.txerr ? new_state : 0; tx_state = bec.rxerr <= bec.txerr ? new_state : 0; - } else { - new_state = CAN_STATE_BUS_OFF; } /* state hasn't changed */ @@ -1158,12 +1157,19 @@ static int flexcan_probe(struct platform_device *pdev) const struct flexcan_devtype_data *devtype_data; struct net_device *dev; struct flexcan_priv *priv; + struct regulator *reg_xceiver; struct resource *mem; struct clk *clk_ipg = NULL, *clk_per = NULL; void __iomem *base; int err, irq; u32 clock_freq = 0; + reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver"); + if (PTR_ERR(reg_xceiver) == -EPROBE_DEFER) + return -EPROBE_DEFER; + else if (IS_ERR(reg_xceiver)) + reg_xceiver = NULL; + if (pdev->dev.of_node) of_property_read_u32(pdev->dev.of_node, "clock-frequency", &clock_freq); @@ -1224,9 +1230,7 @@ static int flexcan_probe(struct platform_device *pdev) priv->pdata = dev_get_platdata(&pdev->dev); priv->devtype_data = devtype_data; - priv->reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver"); - if (IS_ERR(priv->reg_xceiver)) - priv->reg_xceiver = NULL; + priv->reg_xceiver = reg_xceiver; netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT); diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index 009acc8..8b4d3e6 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -901,6 +901,8 @@ static int gs_usb_probe(struct usb_interface *intf, const struct usb_device_id * } dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; init_usb_anchor(&dev->rx_submitted); atomic_set(&dev->active_channels, 0); diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c index 2928f70..57611fd 100644 --- a/drivers/net/can/usb/kvaser_usb.c +++ b/drivers/net/can/usb/kvaser_usb.c @@ -14,6 +14,8 @@ * Copyright (C) 2015 Valeo S.A. */ +#include <linux/spinlock.h> +#include <linux/kernel.h> #include <linux/completion.h> #include <linux/module.h> #include <linux/netdevice.h> @@ -23,7 +25,6 @@ #include <linux/can/dev.h> #include <linux/can/error.h> -#define MAX_TX_URBS 16 #define MAX_RX_URBS 4 #define START_TIMEOUT 1000 /* msecs */ #define STOP_TIMEOUT 1000 /* msecs */ @@ -441,6 +442,7 @@ struct kvaser_usb_error_summary { }; }; +/* Context for an outstanding, not yet ACKed, transmission */ struct kvaser_usb_tx_urb_context { struct kvaser_usb_net_priv *priv; u32 echo_index; @@ -454,8 +456,13 @@ struct kvaser_usb { struct usb_endpoint_descriptor *bulk_in, *bulk_out; struct usb_anchor rx_submitted; + /* @max_tx_urbs: Firmware-reported maximum number of oustanding, + * not yet ACKed, transmissions on this device. This value is + * also used as a sentinel for marking free tx contexts. + */ u32 fw_version; unsigned int nchannels; + unsigned int max_tx_urbs; enum kvaser_usb_family family; bool rxinitdone; @@ -465,18 +472,18 @@ struct kvaser_usb { struct kvaser_usb_net_priv { struct can_priv can; - - atomic_t active_tx_urbs; - struct usb_anchor tx_submitted; - struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS]; - - struct completion start_comp, stop_comp; + struct can_berr_counter bec; struct kvaser_usb *dev; struct net_device *netdev; int channel; - struct can_berr_counter bec; + struct completion start_comp, stop_comp; + struct usb_anchor tx_submitted; + + spinlock_t tx_contexts_lock; + int active_tx_contexts; + struct kvaser_usb_tx_urb_context tx_contexts[]; }; static const struct usb_device_id kvaser_usb_table[] = { @@ -584,8 +591,15 @@ static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id, while (pos <= actual_len - MSG_HEADER_LEN) { tmp = buf + pos; - if (!tmp->len) - break; + /* Handle messages crossing the USB endpoint max packet + * size boundary. Check kvaser_usb_read_bulk_callback() + * for further details. + */ + if (tmp->len == 0) { + pos = round_up(pos, le16_to_cpu(dev->bulk_in-> + wMaxPacketSize)); + continue; + } if (pos + tmp->len > actual_len) { dev_err(dev->udev->dev.parent, @@ -647,9 +661,13 @@ static int kvaser_usb_get_software_info(struct kvaser_usb *dev) switch (dev->family) { case KVASER_LEAF: dev->fw_version = le32_to_cpu(msg.u.leaf.softinfo.fw_version); + dev->max_tx_urbs = + le16_to_cpu(msg.u.leaf.softinfo.max_outstanding_tx); break; case KVASER_USBCAN: dev->fw_version = le32_to_cpu(msg.u.usbcan.softinfo.fw_version); + dev->max_tx_urbs = + le16_to_cpu(msg.u.usbcan.softinfo.max_outstanding_tx); break; } @@ -686,6 +704,7 @@ static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev, struct kvaser_usb_net_priv *priv; struct sk_buff *skb; struct can_frame *cf; + unsigned long flags; u8 channel, tid; channel = msg->u.tx_acknowledge_header.channel; @@ -704,7 +723,7 @@ static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev, stats = &priv->netdev->stats; - context = &priv->tx_contexts[tid % MAX_TX_URBS]; + context = &priv->tx_contexts[tid % dev->max_tx_urbs]; /* Sometimes the state change doesn't come after a bus-off event */ if (priv->can.restart_ms && @@ -729,12 +748,15 @@ static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev, stats->tx_packets++; stats->tx_bytes += context->dlc; - can_get_echo_skb(priv->netdev, context->echo_index); - context->echo_index = MAX_TX_URBS; - atomic_dec(&priv->active_tx_urbs); + spin_lock_irqsave(&priv->tx_contexts_lock, flags); + can_get_echo_skb(priv->netdev, context->echo_index); + context->echo_index = dev->max_tx_urbs; + --priv->active_tx_contexts; netif_wake_queue(priv->netdev); + + spin_unlock_irqrestore(&priv->tx_contexts_lock, flags); } static void kvaser_usb_simple_msg_callback(struct urb *urb) @@ -787,7 +809,6 @@ static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv, netdev_err(netdev, "Error transmitting URB\n"); usb_unanchor_urb(urb); usb_free_urb(urb); - kfree(buf); return err; } @@ -796,17 +817,6 @@ static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv, return 0; } -static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv) -{ - int i; - - usb_kill_anchored_urbs(&priv->tx_submitted); - atomic_set(&priv->active_tx_urbs, 0); - - for (i = 0; i < MAX_TX_URBS; i++) - priv->tx_contexts[i].echo_index = MAX_TX_URBS; -} - static void kvaser_usb_rx_error_update_can_state(struct kvaser_usb_net_priv *priv, const struct kvaser_usb_error_summary *es, struct can_frame *cf) @@ -1317,8 +1327,20 @@ static void kvaser_usb_read_bulk_callback(struct urb *urb) while (pos <= urb->actual_length - MSG_HEADER_LEN) { msg = urb->transfer_buffer + pos; - if (!msg->len) - break; + /* The Kvaser firmware can only read and write messages that + * does not cross the USB's endpoint wMaxPacketSize boundary. + * If a follow-up command crosses such boundary, firmware puts + * a placeholder zero-length command in its place then aligns + * the real command to the next max packet size. + * + * Handle such cases or we're going to miss a significant + * number of events in case of a heavy rx load on the bus. + */ + if (msg->len == 0) { + pos = round_up(pos, le16_to_cpu(dev->bulk_in-> + wMaxPacketSize)); + continue; + } if (pos + msg->len > urb->actual_length) { dev_err(dev->udev->dev.parent, "Format error\n"); @@ -1326,7 +1348,6 @@ static void kvaser_usb_read_bulk_callback(struct urb *urb) } kvaser_usb_handle_message(dev, msg); - pos += msg->len; } @@ -1498,6 +1519,26 @@ error: return err; } +static void kvaser_usb_reset_tx_urb_contexts(struct kvaser_usb_net_priv *priv) +{ + int i, max_tx_urbs; + + max_tx_urbs = priv->dev->max_tx_urbs; + + priv->active_tx_contexts = 0; + for (i = 0; i < max_tx_urbs; i++) + priv->tx_contexts[i].echo_index = max_tx_urbs; +} + +/* This method might sleep. Do not call it in the atomic context + * of URB completions. + */ +static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv) +{ + usb_kill_anchored_urbs(&priv->tx_submitted); + kvaser_usb_reset_tx_urb_contexts(priv); +} + static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev) { int i; @@ -1615,9 +1656,9 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, struct urb *urb; void *buf; struct kvaser_msg *msg; - int i, err; - int ret = NETDEV_TX_OK; + int i, err, ret = NETDEV_TX_OK; u8 *msg_tx_can_flags = NULL; /* GCC */ + unsigned long flags; if (can_dropped_invalid_skb(netdev, skb)) return NETDEV_TX_OK; @@ -1634,7 +1675,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, if (!buf) { stats->tx_dropped++; dev_kfree_skb(skb); - goto nobufmem; + goto freeurb; } msg = buf; @@ -1671,22 +1712,32 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, if (cf->can_id & CAN_RTR_FLAG) *msg_tx_can_flags |= MSG_FLAG_REMOTE_FRAME; - for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) { - if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) { + spin_lock_irqsave(&priv->tx_contexts_lock, flags); + for (i = 0; i < dev->max_tx_urbs; i++) { + if (priv->tx_contexts[i].echo_index == dev->max_tx_urbs) { context = &priv->tx_contexts[i]; + + context->echo_index = i; + can_put_echo_skb(skb, netdev, context->echo_index); + ++priv->active_tx_contexts; + if (priv->active_tx_contexts >= dev->max_tx_urbs) + netif_stop_queue(netdev); + break; } } + spin_unlock_irqrestore(&priv->tx_contexts_lock, flags); /* This should never happen; it implies a flow control bug */ if (!context) { netdev_warn(netdev, "cannot find free context\n"); + + kfree(buf); ret = NETDEV_TX_BUSY; - goto releasebuf; + goto freeurb; } context->priv = priv; - context->echo_index = i; context->dlc = cf->can_dlc; msg->u.tx_can.tid = context->echo_index; @@ -1698,18 +1749,17 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, kvaser_usb_write_bulk_callback, context); usb_anchor_urb(urb, &priv->tx_submitted); - can_put_echo_skb(skb, netdev, context->echo_index); - - atomic_inc(&priv->active_tx_urbs); - - if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS) - netif_stop_queue(netdev); - err = usb_submit_urb(urb, GFP_ATOMIC); if (unlikely(err)) { + spin_lock_irqsave(&priv->tx_contexts_lock, flags); + can_free_echo_skb(netdev, context->echo_index); + context->echo_index = dev->max_tx_urbs; + --priv->active_tx_contexts; + netif_wake_queue(netdev); + + spin_unlock_irqrestore(&priv->tx_contexts_lock, flags); - atomic_dec(&priv->active_tx_urbs); usb_unanchor_urb(urb); stats->tx_dropped++; @@ -1719,16 +1769,12 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, else netdev_warn(netdev, "Failed tx_urb %d\n", err); - goto releasebuf; + goto freeurb; } - usb_free_urb(urb); - - return NETDEV_TX_OK; + ret = NETDEV_TX_OK; -releasebuf: - kfree(buf); -nobufmem: +freeurb: usb_free_urb(urb); return ret; } @@ -1840,13 +1886,15 @@ static int kvaser_usb_init_one(struct usb_interface *intf, struct kvaser_usb *dev = usb_get_intfdata(intf); struct net_device *netdev; struct kvaser_usb_net_priv *priv; - int i, err; + int err; err = kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, channel); if (err) return err; - netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS); + netdev = alloc_candev(sizeof(*priv) + + dev->max_tx_urbs * sizeof(*priv->tx_contexts), + dev->max_tx_urbs); if (!netdev) { dev_err(&intf->dev, "Cannot alloc candev\n"); return -ENOMEM; @@ -1854,19 +1902,17 @@ static int kvaser_usb_init_one(struct usb_interface *intf, priv = netdev_priv(netdev); + init_usb_anchor(&priv->tx_submitted); init_completion(&priv->start_comp); init_completion(&priv->stop_comp); - init_usb_anchor(&priv->tx_submitted); - atomic_set(&priv->active_tx_urbs, 0); - - for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) - priv->tx_contexts[i].echo_index = MAX_TX_URBS; - priv->dev = dev; priv->netdev = netdev; priv->channel = channel; + spin_lock_init(&priv->tx_contexts_lock); + kvaser_usb_reset_tx_urb_contexts(priv); + priv->can.state = CAN_STATE_STOPPED; priv->can.clock.freq = CAN_USB_CLOCK; priv->can.bittiming_const = &kvaser_usb_bittiming_const; @@ -1976,6 +2022,13 @@ static int kvaser_usb_probe(struct usb_interface *intf, return err; } + dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n", + ((dev->fw_version >> 24) & 0xff), + ((dev->fw_version >> 16) & 0xff), + (dev->fw_version & 0xffff)); + + dev_dbg(&intf->dev, "Max oustanding tx = %d URBs\n", dev->max_tx_urbs); + err = kvaser_usb_get_card_info(dev); if (err) { dev_err(&intf->dev, @@ -1983,11 +2036,6 @@ static int kvaser_usb_probe(struct usb_interface *intf, return err; } - dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n", - ((dev->fw_version >> 24) & 0xff), - ((dev->fw_version >> 16) & 0xff), - (dev->fw_version & 0xffff)); - for (i = 0; i < dev->nchannels; i++) { err = kvaser_usb_init_one(intf, id, i); if (err) { diff --git a/drivers/net/can/usb/peak_usb/pcan_ucan.h b/drivers/net/can/usb/peak_usb/pcan_ucan.h index 1ba7c250..e8fc495 100644 --- a/drivers/net/can/usb/peak_usb/pcan_ucan.h +++ b/drivers/net/can/usb/peak_usb/pcan_ucan.h @@ -26,8 +26,8 @@ #define PUCAN_CMD_FILTER_STD 0x008 #define PUCAN_CMD_TX_ABORT 0x009 #define PUCAN_CMD_WR_ERR_CNT 0x00a -#define PUCAN_CMD_RX_FRAME_ENABLE 0x00b -#define PUCAN_CMD_RX_FRAME_DISABLE 0x00c +#define PUCAN_CMD_SET_EN_OPTION 0x00b +#define PUCAN_CMD_CLR_DIS_OPTION 0x00c #define PUCAN_CMD_END_OF_COLLECTION 0x3ff /* uCAN received messages list */ @@ -101,14 +101,15 @@ struct __packed pucan_wr_err_cnt { u16 unused; }; -/* uCAN RX_FRAME_ENABLE command fields */ -#define PUCAN_FLTEXT_ERROR 0x0001 -#define PUCAN_FLTEXT_BUSLOAD 0x0002 +/* uCAN SET_EN/CLR_DIS _OPTION command fields */ +#define PUCAN_OPTION_ERROR 0x0001 +#define PUCAN_OPTION_BUSLOAD 0x0002 +#define PUCAN_OPTION_CANDFDISO 0x0004 -struct __packed pucan_filter_ext { +struct __packed pucan_options { __le16 opcode_channel; - __le16 ext_mask; + __le16 options; u32 unused; }; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c index 962c3f0..a9221ad 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c @@ -110,13 +110,13 @@ struct __packed pcan_ufd_led { u8 unused[5]; }; -/* Extended usage of uCAN commands CMD_RX_FRAME_xxxABLE for PCAN-USB Pro FD */ +/* Extended usage of uCAN commands CMD_xxx_xx_OPTION for PCAN-USB Pro FD */ #define PCAN_UFD_FLTEXT_CALIBRATION 0x8000 -struct __packed pcan_ufd_filter_ext { +struct __packed pcan_ufd_options { __le16 opcode_channel; - __le16 ext_mask; + __le16 ucan_mask; u16 unused; __le16 usb_mask; }; @@ -251,6 +251,27 @@ static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf) /* moves the pointer forward */ pc += sizeof(struct pucan_wr_err_cnt); + /* add command to switch from ISO to non-ISO mode, if fw allows it */ + if (dev->can.ctrlmode_supported & CAN_CTRLMODE_FD_NON_ISO) { + struct pucan_options *puo = (struct pucan_options *)pc; + + puo->opcode_channel = + (dev->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) ? + pucan_cmd_opcode_channel(dev, + PUCAN_CMD_CLR_DIS_OPTION) : + pucan_cmd_opcode_channel(dev, PUCAN_CMD_SET_EN_OPTION); + + puo->options = cpu_to_le16(PUCAN_OPTION_CANDFDISO); + + /* to be sure that no other extended bits will be taken into + * account + */ + puo->unused = 0; + + /* moves the pointer forward */ + pc += sizeof(struct pucan_options); + } + /* next, go back to operational mode */ cmd = (struct pucan_command *)pc; cmd->opcode_channel = pucan_cmd_opcode_channel(dev, @@ -321,21 +342,21 @@ static int pcan_usb_fd_set_filter_std(struct peak_usb_device *dev, int idx, return pcan_usb_fd_send_cmd(dev, cmd); } -/* set/unset notifications filter: +/* set/unset options * - * onoff sets(1)/unset(0) notifications - * mask each bit defines a kind of notification to set/unset + * onoff set(1)/unset(0) options + * mask each bit defines a kind of options to set/unset */ -static int pcan_usb_fd_set_filter_ext(struct peak_usb_device *dev, - bool onoff, u16 ext_mask, u16 usb_mask) +static int pcan_usb_fd_set_options(struct peak_usb_device *dev, + bool onoff, u16 ucan_mask, u16 usb_mask) { - struct pcan_ufd_filter_ext *cmd = pcan_usb_fd_cmd_buffer(dev); + struct pcan_ufd_options *cmd = pcan_usb_fd_cmd_buffer(dev); cmd->opcode_channel = pucan_cmd_opcode_channel(dev, - (onoff) ? PUCAN_CMD_RX_FRAME_ENABLE : - PUCAN_CMD_RX_FRAME_DISABLE); + (onoff) ? PUCAN_CMD_SET_EN_OPTION : + PUCAN_CMD_CLR_DIS_OPTION); - cmd->ext_mask = cpu_to_le16(ext_mask); + cmd->ucan_mask = cpu_to_le16(ucan_mask); cmd->usb_mask = cpu_to_le16(usb_mask); /* send the command */ @@ -770,9 +791,9 @@ static int pcan_usb_fd_start(struct peak_usb_device *dev) &pcan_usb_pro_fd); /* enable USB calibration messages */ - err = pcan_usb_fd_set_filter_ext(dev, 1, - PUCAN_FLTEXT_ERROR, - PCAN_UFD_FLTEXT_CALIBRATION); + err = pcan_usb_fd_set_options(dev, 1, + PUCAN_OPTION_ERROR, + PCAN_UFD_FLTEXT_CALIBRATION); } pdev->usb_if->dev_opened_count++; @@ -806,9 +827,9 @@ static int pcan_usb_fd_stop(struct peak_usb_device *dev) /* turn off special msgs for that interface if no other dev opened */ if (pdev->usb_if->dev_opened_count == 1) - pcan_usb_fd_set_filter_ext(dev, 0, - PUCAN_FLTEXT_ERROR, - PCAN_UFD_FLTEXT_CALIBRATION); + pcan_usb_fd_set_options(dev, 0, + PUCAN_OPTION_ERROR, + PCAN_UFD_FLTEXT_CALIBRATION); pdev->usb_if->dev_opened_count--; return 0; @@ -860,8 +881,14 @@ static int pcan_usb_fd_init(struct peak_usb_device *dev) pdev->usb_if->fw_info.fw_version[2], dev->adapter->ctrl_count); - /* the currently supported hw is non-ISO */ - dev->can.ctrlmode = CAN_CTRLMODE_FD_NON_ISO; + /* check for ability to switch between ISO/non-ISO modes */ + if (pdev->usb_if->fw_info.fw_version[0] >= 2) { + /* firmware >= 2.x supports ISO/non-ISO switching */ + dev->can.ctrlmode_supported |= CAN_CTRLMODE_FD_NON_ISO; + } else { + /* firmware < 2.x only supports fixed(!) non-ISO */ + dev->can.ctrlmode |= CAN_CTRLMODE_FD_NON_ISO; + } /* tell the hardware the can driver is running */ err = pcan_usb_fd_drv_loaded(dev, 1); @@ -879,6 +906,10 @@ static int pcan_usb_fd_init(struct peak_usb_device *dev) pdev->usb_if = ppdev->usb_if; pdev->cmd_buffer_addr = ppdev->cmd_buffer_addr; + + /* do a copy of the ctrlmode[_supported] too */ + dev->can.ctrlmode = ppdev->dev.can.ctrlmode; + dev->can.ctrlmode_supported = ppdev->dev.can.ctrlmode_supported; } pdev->usb_if->dev[dev->ctrl_idx] = dev; @@ -933,9 +964,9 @@ static void pcan_usb_fd_exit(struct peak_usb_device *dev) if (dev->ctrl_idx == 0) { /* turn off calibration message if any device were opened */ if (pdev->usb_if->dev_opened_count > 0) - pcan_usb_fd_set_filter_ext(dev, 0, - PUCAN_FLTEXT_ERROR, - PCAN_UFD_FLTEXT_CALIBRATION); + pcan_usb_fd_set_options(dev, 0, + PUCAN_OPTION_ERROR, + PCAN_UFD_FLTEXT_CALIBRATION); /* tell USB adapter that the driver is being unloaded */ pcan_usb_fd_drv_loaded(dev, 0); diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h index ee9f650..7b7053d 100644 --- a/drivers/net/dsa/bcm_sf2.h +++ b/drivers/net/dsa/bcm_sf2.h @@ -105,8 +105,8 @@ static inline u64 name##_readq(struct bcm_sf2_priv *priv, u32 off) \ { \ u32 indir, dir; \ spin_lock(&priv->indir_lock); \ - indir = reg_readl(priv, REG_DIR_DATA_READ); \ dir = __raw_readl(priv->name + off); \ + indir = reg_readl(priv, REG_DIR_DATA_READ); \ spin_unlock(&priv->indir_lock); \ return (u64)indir << 32 | dir; \ } \ diff --git a/drivers/net/ethernet/8390/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c index 7769c05..ec6eac1 100644 --- a/drivers/net/ethernet/8390/axnet_cs.c +++ b/drivers/net/ethernet/8390/axnet_cs.c @@ -484,11 +484,8 @@ static int axnet_open(struct net_device *dev) link->open++; info->link_status = 0x00; - init_timer(&info->watchdog); - info->watchdog.function = ei_watchdog; - info->watchdog.data = (u_long)dev; - info->watchdog.expires = jiffies + HZ; - add_timer(&info->watchdog); + setup_timer(&info->watchdog, ei_watchdog, (u_long)dev); + mod_timer(&info->watchdog, jiffies + HZ); return ax_open(dev); } /* axnet_open */ diff --git a/drivers/net/ethernet/8390/pcnet_cs.c b/drivers/net/ethernet/8390/pcnet_cs.c index 9fb7b9d..2777289 100644 --- a/drivers/net/ethernet/8390/pcnet_cs.c +++ b/drivers/net/ethernet/8390/pcnet_cs.c @@ -918,11 +918,8 @@ static int pcnet_open(struct net_device *dev) info->phy_id = info->eth_phy; info->link_status = 0x00; - init_timer(&info->watchdog); - info->watchdog.function = ei_watchdog; - info->watchdog.data = (u_long)dev; - info->watchdog.expires = jiffies + HZ; - add_timer(&info->watchdog); + setup_timer(&info->watchdog, ei_watchdog, (u_long)dev); + mod_timer(&info->watchdog, jiffies + HZ); return ei_open(dev); } /* pcnet_open */ diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c index 760c72c..6725dc0 100644 --- a/drivers/net/ethernet/altera/altera_tse_main.c +++ b/drivers/net/ethernet/altera/altera_tse_main.c @@ -376,7 +376,8 @@ static int tse_rx(struct altera_tse_private *priv, int limit) u16 pktlength; u16 pktstatus; - while ((rxstatus = priv->dmaops->get_rx_status(priv)) != 0) { + while (((rxstatus = priv->dmaops->get_rx_status(priv)) != 0) && + (count < limit)) { pktstatus = rxstatus >> 16; pktlength = rxstatus & 0xffff; @@ -491,28 +492,27 @@ static int tse_poll(struct napi_struct *napi, int budget) struct altera_tse_private *priv = container_of(napi, struct altera_tse_private, napi); int rxcomplete = 0; - int txcomplete = 0; unsigned long int flags; - txcomplete = tse_tx_complete(priv); + tse_tx_complete(priv); rxcomplete = tse_rx(priv, budget); - if (rxcomplete >= budget || txcomplete > 0) - return rxcomplete; + if (rxcomplete < budget) { - napi_gro_flush(napi, false); - __napi_complete(napi); + napi_gro_flush(napi, false); + __napi_complete(napi); - netdev_dbg(priv->dev, - "NAPI Complete, did %d packets with budget %d\n", - txcomplete+rxcomplete, budget); + netdev_dbg(priv->dev, + "NAPI Complete, did %d packets with budget %d\n", + rxcomplete, budget); - spin_lock_irqsave(&priv->rxdma_irq_lock, flags); - priv->dmaops->enable_rxirq(priv); - priv->dmaops->enable_txirq(priv); - spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags); - return rxcomplete + txcomplete; + spin_lock_irqsave(&priv->rxdma_irq_lock, flags); + priv->dmaops->enable_rxirq(priv); + priv->dmaops->enable_txirq(priv); + spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags); + } + return rxcomplete; } /* DMA TX & RX FIFO interrupt routing @@ -521,7 +521,6 @@ static irqreturn_t altera_isr(int irq, void *dev_id) { struct net_device *dev = dev_id; struct altera_tse_private *priv; - unsigned long int flags; if (unlikely(!dev)) { pr_err("%s: invalid dev pointer\n", __func__); @@ -529,20 +528,20 @@ static irqreturn_t altera_isr(int irq, void *dev_id) } priv = netdev_priv(dev); - /* turn off desc irqs and enable napi rx */ - spin_lock_irqsave(&priv->rxdma_irq_lock, flags); + spin_lock(&priv->rxdma_irq_lock); + /* reset IRQs */ + priv->dmaops->clear_rxirq(priv); + priv->dmaops->clear_txirq(priv); + spin_unlock(&priv->rxdma_irq_lock); if (likely(napi_schedule_prep(&priv->napi))) { + spin_lock(&priv->rxdma_irq_lock); priv->dmaops->disable_rxirq(priv); priv->dmaops->disable_txirq(priv); + spin_unlock(&priv->rxdma_irq_lock); __napi_schedule(&priv->napi); } - /* reset IRQs */ - priv->dmaops->clear_rxirq(priv); - priv->dmaops->clear_txirq(priv); - - spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags); return IRQ_HANDLED; } @@ -1399,7 +1398,7 @@ static int altera_tse_probe(struct platform_device *pdev) } if (of_property_read_u32(pdev->dev.of_node, "tx-fifo-depth", - &priv->rx_fifo_depth)) { + &priv->tx_fifo_depth)) { dev_err(&pdev->dev, "cannot obtain tx-fifo-depth\n"); ret = -ENXIO; goto err_free_netdev; diff --git a/drivers/net/ethernet/amd/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c index 11d6e65..15a8190 100644 --- a/drivers/net/ethernet/amd/pcnet32.c +++ b/drivers/net/ethernet/amd/pcnet32.c @@ -1543,7 +1543,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) { struct pcnet32_private *lp; int i, media; - int fdx, mii, fset, dxsuflo; + int fdx, mii, fset, dxsuflo, sram; int chip_version; char *chipname; struct net_device *dev; @@ -1580,7 +1580,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) } /* initialize variables */ - fdx = mii = fset = dxsuflo = 0; + fdx = mii = fset = dxsuflo = sram = 0; chip_version = (chip_version >> 12) & 0xffff; switch (chip_version) { @@ -1613,6 +1613,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) chipname = "PCnet/FAST III 79C973"; /* PCI */ fdx = 1; mii = 1; + sram = 1; break; case 0x2626: chipname = "PCnet/Home 79C978"; /* PCI */ @@ -1636,6 +1637,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) chipname = "PCnet/FAST III 79C975"; /* PCI */ fdx = 1; mii = 1; + sram = 1; break; case 0x2628: chipname = "PCnet/PRO 79C976"; @@ -1664,6 +1666,31 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) dxsuflo = 1; } + /* + * The Am79C973/Am79C975 controllers come with 12K of SRAM + * which we can use for the Tx/Rx buffers but most importantly, + * the use of SRAM allow us to use the BCR18:NOUFLO bit to avoid + * Tx fifo underflows. + */ + if (sram) { + /* + * The SRAM is being configured in two steps. First we + * set the SRAM size in the BCR25:SRAM_SIZE bits. According + * to the datasheet, each bit corresponds to a 512-byte + * page so we can have at most 24 pages. The SRAM_SIZE + * holds the value of the upper 8 bits of the 16-bit SRAM size. + * The low 8-bits start at 0x00 and end at 0xff. So the + * address range is from 0x0000 up to 0x17ff. Therefore, + * the SRAM_SIZE is set to 0x17. The next step is to set + * the BCR26:SRAM_BND midway through so the Tx and Rx + * buffers can share the SRAM equally. + */ + a->write_bcr(ioaddr, 25, 0x17); + a->write_bcr(ioaddr, 26, 0xc); + /* And finally enable the NOUFLO bit */ + a->write_bcr(ioaddr, 18, a->read_bcr(ioaddr, 18) | (1 << 11)); + } + dev = alloc_etherdev(sizeof(*lp)); if (!dev) { ret = -ENOMEM; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index b93d440..885b02b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -609,6 +609,68 @@ static void xgbe_napi_disable(struct xgbe_prv_data *pdata, unsigned int del) } } +static int xgbe_request_irqs(struct xgbe_prv_data *pdata) +{ + struct xgbe_channel *channel; + struct net_device *netdev = pdata->netdev; + unsigned int i; + int ret; + + ret = devm_request_irq(pdata->dev, pdata->dev_irq, xgbe_isr, 0, + netdev->name, pdata); + if (ret) { + netdev_alert(netdev, "error requesting irq %d\n", + pdata->dev_irq); + return ret; + } + + if (!pdata->per_channel_irq) + return 0; + + channel = pdata->channel; + for (i = 0; i < pdata->channel_count; i++, channel++) { + snprintf(channel->dma_irq_name, + sizeof(channel->dma_irq_name) - 1, + "%s-TxRx-%u", netdev_name(netdev), + channel->queue_index); + + ret = devm_request_irq(pdata->dev, channel->dma_irq, + xgbe_dma_isr, 0, + channel->dma_irq_name, channel); + if (ret) { + netdev_alert(netdev, "error requesting irq %d\n", + channel->dma_irq); + goto err_irq; + } + } + + return 0; + +err_irq: + /* Using an unsigned int, 'i' will go to UINT_MAX and exit */ + for (i--, channel--; i < pdata->channel_count; i--, channel--) + devm_free_irq(pdata->dev, channel->dma_irq, channel); + + devm_free_irq(pdata->dev, pdata->dev_irq, pdata); + + return ret; +} + +static void xgbe_free_irqs(struct xgbe_prv_data *pdata) +{ + struct xgbe_channel *channel; + unsigned int i; + + devm_free_irq(pdata->dev, pdata->dev_irq, pdata); + + if (!pdata->per_channel_irq) + return; + + channel = pdata->channel; + for (i = 0; i < pdata->channel_count; i++, channel++) + devm_free_irq(pdata->dev, channel->dma_irq, channel); +} + void xgbe_init_tx_coalesce(struct xgbe_prv_data *pdata) { struct xgbe_hw_if *hw_if = &pdata->hw_if; @@ -810,20 +872,20 @@ int xgbe_powerdown(struct net_device *netdev, unsigned int caller) return -EINVAL; } - phy_stop(pdata->phydev); - spin_lock_irqsave(&pdata->lock, flags); if (caller == XGMAC_DRIVER_CONTEXT) netif_device_detach(netdev); netif_tx_stop_all_queues(netdev); - xgbe_napi_disable(pdata, 0); - /* Powerdown Tx/Rx */ hw_if->powerdown_tx(pdata); hw_if->powerdown_rx(pdata); + xgbe_napi_disable(pdata, 0); + + phy_stop(pdata->phydev); + pdata->power_down = 1; spin_unlock_irqrestore(&pdata->lock, flags); @@ -854,14 +916,14 @@ int xgbe_powerup(struct net_device *netdev, unsigned int caller) phy_start(pdata->phydev); - /* Enable Tx/Rx */ + xgbe_napi_enable(pdata, 0); + hw_if->powerup_tx(pdata); hw_if->powerup_rx(pdata); if (caller == XGMAC_DRIVER_CONTEXT) netif_device_attach(netdev); - xgbe_napi_enable(pdata, 0); netif_tx_start_all_queues(netdev); spin_unlock_irqrestore(&pdata->lock, flags); @@ -875,6 +937,7 @@ static int xgbe_start(struct xgbe_prv_data *pdata) { struct xgbe_hw_if *hw_if = &pdata->hw_if; struct net_device *netdev = pdata->netdev; + int ret; DBGPR("-->xgbe_start\n"); @@ -884,17 +947,31 @@ static int xgbe_start(struct xgbe_prv_data *pdata) phy_start(pdata->phydev); + xgbe_napi_enable(pdata, 1); + + ret = xgbe_request_irqs(pdata); + if (ret) + goto err_napi; + hw_if->enable_tx(pdata); hw_if->enable_rx(pdata); xgbe_init_tx_timers(pdata); - xgbe_napi_enable(pdata, 1); netif_tx_start_all_queues(netdev); DBGPR("<--xgbe_start\n"); return 0; + +err_napi: + xgbe_napi_disable(pdata, 1); + + phy_stop(pdata->phydev); + + hw_if->exit(pdata); + + return ret; } static void xgbe_stop(struct xgbe_prv_data *pdata) @@ -907,16 +984,21 @@ static void xgbe_stop(struct xgbe_prv_data *pdata) DBGPR("-->xgbe_stop\n"); - phy_stop(pdata->phydev); - netif_tx_stop_all_queues(netdev); - xgbe_napi_disable(pdata, 1); xgbe_stop_tx_timers(pdata); hw_if->disable_tx(pdata); hw_if->disable_rx(pdata); + xgbe_free_irqs(pdata); + + xgbe_napi_disable(pdata, 1); + + phy_stop(pdata->phydev); + + hw_if->exit(pdata); + channel = pdata->channel; for (i = 0; i < pdata->channel_count; i++, channel++) { if (!channel->tx_ring) @@ -931,10 +1013,6 @@ static void xgbe_stop(struct xgbe_prv_data *pdata) static void xgbe_restart_dev(struct xgbe_prv_data *pdata) { - struct xgbe_channel *channel; - struct xgbe_hw_if *hw_if = &pdata->hw_if; - unsigned int i; - DBGPR("-->xgbe_restart_dev\n"); /* If not running, "restart" will happen on open */ @@ -942,19 +1020,10 @@ static void xgbe_restart_dev(struct xgbe_prv_data *pdata) return; xgbe_stop(pdata); - synchronize_irq(pdata->dev_irq); - if (pdata->per_channel_irq) { - channel = pdata->channel; - for (i = 0; i < pdata->channel_count; i++, channel++) - synchronize_irq(channel->dma_irq); - } xgbe_free_tx_data(pdata); xgbe_free_rx_data(pdata); - /* Issue software reset to device */ - hw_if->exit(pdata); - xgbe_start(pdata); DBGPR("<--xgbe_restart_dev\n"); @@ -1283,10 +1352,7 @@ static void xgbe_packet_info(struct xgbe_prv_data *pdata, static int xgbe_open(struct net_device *netdev) { struct xgbe_prv_data *pdata = netdev_priv(netdev); - struct xgbe_hw_if *hw_if = &pdata->hw_if; struct xgbe_desc_if *desc_if = &pdata->desc_if; - struct xgbe_channel *channel = NULL; - unsigned int i = 0; int ret; DBGPR("-->xgbe_open\n"); @@ -1329,55 +1395,14 @@ static int xgbe_open(struct net_device *netdev) INIT_WORK(&pdata->restart_work, xgbe_restart); INIT_WORK(&pdata->tx_tstamp_work, xgbe_tx_tstamp); - /* Request interrupts */ - ret = devm_request_irq(pdata->dev, pdata->dev_irq, xgbe_isr, 0, - netdev->name, pdata); - if (ret) { - netdev_alert(netdev, "error requesting irq %d\n", - pdata->dev_irq); - goto err_rings; - } - - if (pdata->per_channel_irq) { - channel = pdata->channel; - for (i = 0; i < pdata->channel_count; i++, channel++) { - snprintf(channel->dma_irq_name, - sizeof(channel->dma_irq_name) - 1, - "%s-TxRx-%u", netdev_name(netdev), - channel->queue_index); - - ret = devm_request_irq(pdata->dev, channel->dma_irq, - xgbe_dma_isr, 0, - channel->dma_irq_name, channel); - if (ret) { - netdev_alert(netdev, - "error requesting irq %d\n", - channel->dma_irq); - goto err_irq; - } - } - } - ret = xgbe_start(pdata); if (ret) - goto err_start; + goto err_rings; DBGPR("<--xgbe_open\n"); return 0; -err_start: - hw_if->exit(pdata); - -err_irq: - if (pdata->per_channel_irq) { - /* Using an unsigned int, 'i' will go to UINT_MAX and exit */ - for (i--, channel--; i < pdata->channel_count; i--, channel--) - devm_free_irq(pdata->dev, channel->dma_irq, channel); - } - - devm_free_irq(pdata->dev, pdata->dev_irq, pdata); - err_rings: desc_if->free_ring_resources(pdata); @@ -1399,30 +1424,16 @@ err_phy_init: static int xgbe_close(struct net_device *netdev) { struct xgbe_prv_data *pdata = netdev_priv(netdev); - struct xgbe_hw_if *hw_if = &pdata->hw_if; struct xgbe_desc_if *desc_if = &pdata->desc_if; - struct xgbe_channel *channel; - unsigned int i; DBGPR("-->xgbe_close\n"); /* Stop the device */ xgbe_stop(pdata); - /* Issue software reset to device */ - hw_if->exit(pdata); - /* Free the ring descriptors and buffers */ desc_if->free_ring_resources(pdata); - /* Release the interrupts */ - devm_free_irq(pdata->dev, pdata->dev_irq, pdata); - if (pdata->per_channel_irq) { - channel = pdata->channel; - for (i = 0; i < pdata->channel_count; i++, channel++) - devm_free_irq(pdata->dev, channel->dma_irq, channel); - } - /* Free the channel and ring structures */ xgbe_free_channels(pdata); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index 869d97f..b927021 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -593,7 +593,7 @@ static int xgene_enet_reset(struct xgene_enet_pdata *pdata) if (!xgene_ring_mgr_init(pdata)) return -ENODEV; - if (!efi_enabled(EFI_BOOT)) { + if (pdata->clk) { clk_prepare_enable(pdata->clk); clk_disable_unprepare(pdata->clk); clk_prepare_enable(pdata->clk); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 4de62b2..635a83b 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -1025,6 +1025,8 @@ static int xgene_enet_remove(struct platform_device *pdev) #ifdef CONFIG_ACPI static const struct acpi_device_id xgene_enet_acpi_match[] = { { "APMC0D05", }, + { "APMC0D30", }, + { "APMC0D31", }, { } }; MODULE_DEVICE_TABLE(acpi, xgene_enet_acpi_match); @@ -1033,6 +1035,8 @@ MODULE_DEVICE_TABLE(acpi, xgene_enet_acpi_match); #ifdef CONFIG_OF static struct of_device_id xgene_enet_of_match[] = { {.compatible = "apm,xgene-enet",}, + {.compatible = "apm,xgene1-sgenet",}, + {.compatible = "apm,xgene1-xgenet",}, {}, }; diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 21206d3..a7f2cc3 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -486,7 +486,7 @@ static int bcm_enet_poll(struct napi_struct *napi, int budget) { struct bcm_enet_priv *priv; struct net_device *dev; - int tx_work_done, rx_work_done; + int rx_work_done; priv = container_of(napi, struct bcm_enet_priv, napi); dev = priv->net_dev; @@ -498,14 +498,14 @@ static int bcm_enet_poll(struct napi_struct *napi, int budget) ENETDMAC_IR, priv->tx_chan); /* reclaim sent skb */ - tx_work_done = bcm_enet_tx_reclaim(dev, 0); + bcm_enet_tx_reclaim(dev, 0); spin_lock(&priv->rx_lock); rx_work_done = bcm_enet_receive_queue(dev, budget); spin_unlock(&priv->rx_lock); - if (rx_work_done >= budget || tx_work_done > 0) { - /* rx/tx queue is not yet empty/clean */ + if (rx_work_done >= budget) { + /* rx queue is not yet empty/clean */ return rx_work_done; } diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 5b308a4..783543a 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -274,9 +274,9 @@ static const struct bcm_sysport_stats bcm_sysport_gstrings_stats[] = { /* RBUF misc statistics */ STAT_RBUF("rbuf_ovflow_cnt", mib.rbuf_ovflow_cnt, RBUF_OVFL_DISC_CNTR), STAT_RBUF("rbuf_err_cnt", mib.rbuf_err_cnt, RBUF_ERR_PKT_CNTR), - STAT_MIB_RX("alloc_rx_buff_failed", mib.alloc_rx_buff_failed), - STAT_MIB_RX("rx_dma_failed", mib.rx_dma_failed), - STAT_MIB_TX("tx_dma_failed", mib.tx_dma_failed), + STAT_MIB_SOFT("alloc_rx_buff_failed", mib.alloc_rx_buff_failed), + STAT_MIB_SOFT("rx_dma_failed", mib.rx_dma_failed), + STAT_MIB_SOFT("tx_dma_failed", mib.tx_dma_failed), }; #define BCM_SYSPORT_STATS_LEN ARRAY_SIZE(bcm_sysport_gstrings_stats) @@ -345,6 +345,7 @@ static void bcm_sysport_update_mib_counters(struct bcm_sysport_priv *priv) s = &bcm_sysport_gstrings_stats[i]; switch (s->type) { case BCM_SYSPORT_STAT_NETDEV: + case BCM_SYSPORT_STAT_SOFT: continue; case BCM_SYSPORT_STAT_MIB_RX: case BCM_SYSPORT_STAT_MIB_TX: diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h index fc19417..7e3d87a 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.h +++ b/drivers/net/ethernet/broadcom/bcmsysport.h @@ -570,6 +570,7 @@ enum bcm_sysport_stat_type { BCM_SYSPORT_STAT_RUNT, BCM_SYSPORT_STAT_RXCHK, BCM_SYSPORT_STAT_RBUF, + BCM_SYSPORT_STAT_SOFT, }; /* Macros to help define ethtool statistics */ @@ -590,6 +591,7 @@ enum bcm_sysport_stat_type { #define STAT_MIB_RX(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_MIB_RX) #define STAT_MIB_TX(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_MIB_TX) #define STAT_RUNT(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_RUNT) +#define STAT_MIB_SOFT(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_SOFT) #define STAT_RXCHK(str, m, ofs) { \ .stat_string = str, \ diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 676ffe0..0469f72 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -302,9 +302,6 @@ static int bgmac_dma_rx_skb_for_slot(struct bgmac *bgmac, slot->skb = skb; slot->dma_addr = dma_addr; - if (slot->dma_addr & 0xC0000000) - bgmac_warn(bgmac, "DMA address using 0xC0000000 bit(s), it may need translation trick\n"); - return 0; } @@ -505,8 +502,6 @@ static int bgmac_dma_alloc(struct bgmac *bgmac) ring->mmio_base); goto err_dma_free; } - if (ring->dma_base & 0xC0000000) - bgmac_warn(bgmac, "DMA address using 0xC0000000 bit(s), it may need translation trick\n"); ring->unaligned = bgmac_dma_unaligned(bgmac, ring, BGMAC_DMA_RING_TX); @@ -536,8 +531,6 @@ static int bgmac_dma_alloc(struct bgmac *bgmac) err = -ENOMEM; goto err_dma_free; } - if (ring->dma_base & 0xC0000000) - bgmac_warn(bgmac, "DMA address using 0xC0000000 bit(s), it may need translation trick\n"); ring->unaligned = bgmac_dma_unaligned(bgmac, ring, BGMAC_DMA_RING_RX); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 756053c..4085c4b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1811,7 +1811,7 @@ struct bnx2x { int stats_state; /* used for synchronization of concurrent threads statistics handling */ - spinlock_t stats_lock; + struct mutex stats_lock; /* used by dmae command loader */ struct dmae_command stats_dmae; @@ -1935,8 +1935,6 @@ struct bnx2x { int fp_array_size; u32 dump_preset_idx; - bool stats_started; - struct semaphore stats_sema; u8 phys_port_id[ETH_ALEN]; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 7155e1d..1ec635f 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -129,8 +129,8 @@ struct bnx2x_mac_vals { u32 xmac_val; u32 emac_addr; u32 emac_val; - u32 umac_addr; - u32 umac_val; + u32 umac_addr[2]; + u32 umac_val[2]; u32 bmac_addr; u32 bmac_val[2]; }; @@ -7866,6 +7866,20 @@ int bnx2x_init_hw_func_cnic(struct bnx2x *bp) return 0; } +/* previous driver DMAE transaction may have occurred when pre-boot stage ended + * and boot began, or when kdump kernel was loaded. Either case would invalidate + * the addresses of the transaction, resulting in was-error bit set in the pci + * causing all hw-to-host pcie transactions to timeout. If this happened we want + * to clear the interrupt which detected this from the pglueb and the was done + * bit + */ +static void bnx2x_clean_pglue_errors(struct bnx2x *bp) +{ + if (!CHIP_IS_E1x(bp)) + REG_WR(bp, PGLUE_B_REG_WAS_ERROR_PF_7_0_CLR, + 1 << BP_ABS_FUNC(bp)); +} + static int bnx2x_init_hw_func(struct bnx2x *bp) { int port = BP_PORT(bp); @@ -7958,8 +7972,7 @@ static int bnx2x_init_hw_func(struct bnx2x *bp) bnx2x_init_block(bp, BLOCK_PGLUE_B, init_phase); - if (!CHIP_IS_E1x(bp)) - REG_WR(bp, PGLUE_B_REG_WAS_ERROR_PF_7_0_CLR, func); + bnx2x_clean_pglue_errors(bp); bnx2x_init_block(bp, BLOCK_ATC, init_phase); bnx2x_init_block(bp, BLOCK_DMAE, init_phase); @@ -10141,6 +10154,25 @@ static u32 bnx2x_get_pretend_reg(struct bnx2x *bp) return base + (BP_ABS_FUNC(bp)) * stride; } +static bool bnx2x_prev_unload_close_umac(struct bnx2x *bp, + u8 port, u32 reset_reg, + struct bnx2x_mac_vals *vals) +{ + u32 mask = MISC_REGISTERS_RESET_REG_2_UMAC0 << port; + u32 base_addr; + + if (!(mask & reset_reg)) + return false; + + BNX2X_DEV_INFO("Disable umac Rx %02x\n", port); + base_addr = port ? GRCBASE_UMAC1 : GRCBASE_UMAC0; + vals->umac_addr[port] = base_addr + UMAC_REG_COMMAND_CONFIG; + vals->umac_val[port] = REG_RD(bp, vals->umac_addr[port]); + REG_WR(bp, vals->umac_addr[port], 0); + + return true; +} + static void bnx2x_prev_unload_close_mac(struct bnx2x *bp, struct bnx2x_mac_vals *vals) { @@ -10149,10 +10181,7 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp, u8 port = BP_PORT(bp); /* reset addresses as they also mark which values were changed */ - vals->bmac_addr = 0; - vals->umac_addr = 0; - vals->xmac_addr = 0; - vals->emac_addr = 0; + memset(vals, 0, sizeof(*vals)); reset_reg = REG_RD(bp, MISC_REG_RESET_REG_2); @@ -10201,15 +10230,11 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp, REG_WR(bp, vals->xmac_addr, 0); mac_stopped = true; } - mask = MISC_REGISTERS_RESET_REG_2_UMAC0 << port; - if (mask & reset_reg) { - BNX2X_DEV_INFO("Disable umac Rx\n"); - base_addr = BP_PORT(bp) ? GRCBASE_UMAC1 : GRCBASE_UMAC0; - vals->umac_addr = base_addr + UMAC_REG_COMMAND_CONFIG; - vals->umac_val = REG_RD(bp, vals->umac_addr); - REG_WR(bp, vals->umac_addr, 0); - mac_stopped = true; - } + + mac_stopped |= bnx2x_prev_unload_close_umac(bp, 0, + reset_reg, vals); + mac_stopped |= bnx2x_prev_unload_close_umac(bp, 1, + reset_reg, vals); } if (mac_stopped) @@ -10505,8 +10530,11 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) /* Close the MAC Rx to prevent BRB from filling up */ bnx2x_prev_unload_close_mac(bp, &mac_vals); - /* close LLH filters towards the BRB */ + /* close LLH filters for both ports towards the BRB */ + bnx2x_set_rx_filter(&bp->link_params, 0); + bp->link_params.port ^= 1; bnx2x_set_rx_filter(&bp->link_params, 0); + bp->link_params.port ^= 1; /* Check if the UNDI driver was previously loaded */ if (bnx2x_prev_is_after_undi(bp)) { @@ -10553,8 +10581,10 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) if (mac_vals.xmac_addr) REG_WR(bp, mac_vals.xmac_addr, mac_vals.xmac_val); - if (mac_vals.umac_addr) - REG_WR(bp, mac_vals.umac_addr, mac_vals.umac_val); + if (mac_vals.umac_addr[0]) + REG_WR(bp, mac_vals.umac_addr[0], mac_vals.umac_val[0]); + if (mac_vals.umac_addr[1]) + REG_WR(bp, mac_vals.umac_addr[1], mac_vals.umac_val[1]); if (mac_vals.emac_addr) REG_WR(bp, mac_vals.emac_addr, mac_vals.emac_val); if (mac_vals.bmac_addr) { @@ -10571,26 +10601,6 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) return bnx2x_prev_mcp_done(bp); } -/* previous driver DMAE transaction may have occurred when pre-boot stage ended - * and boot began, or when kdump kernel was loaded. Either case would invalidate - * the addresses of the transaction, resulting in was-error bit set in the pci - * causing all hw-to-host pcie transactions to timeout. If this happened we want - * to clear the interrupt which detected this from the pglueb and the was done - * bit - */ -static void bnx2x_prev_interrupted_dmae(struct bnx2x *bp) -{ - if (!CHIP_IS_E1x(bp)) { - u32 val = REG_RD(bp, PGLUE_B_REG_PGLUE_B_INT_STS); - if (val & PGLUE_B_PGLUE_B_INT_STS_REG_WAS_ERROR_ATTN) { - DP(BNX2X_MSG_SP, - "'was error' bit was found to be set in pglueb upon startup. Clearing\n"); - REG_WR(bp, PGLUE_B_REG_WAS_ERROR_PF_7_0_CLR, - 1 << BP_FUNC(bp)); - } - } -} - static int bnx2x_prev_unload(struct bnx2x *bp) { int time_counter = 10; @@ -10600,7 +10610,7 @@ static int bnx2x_prev_unload(struct bnx2x *bp) /* clear hw from errors which may have resulted from an interrupted * dmae transaction. */ - bnx2x_prev_interrupted_dmae(bp); + bnx2x_clean_pglue_errors(bp); /* Release previously held locks */ hw_lock_reg = (BP_FUNC(bp) <= 5) ? @@ -12037,9 +12047,8 @@ static int bnx2x_init_bp(struct bnx2x *bp) mutex_init(&bp->port.phy_mutex); mutex_init(&bp->fw_mb_mutex); mutex_init(&bp->drv_info_mutex); + mutex_init(&bp->stats_lock); bp->drv_info_mng_owner = false; - spin_lock_init(&bp->stats_lock); - sema_init(&bp->stats_sema, 1); INIT_DELAYED_WORK(&bp->sp_task, bnx2x_sp_task); INIT_DELAYED_WORK(&bp->sp_rtnl_task, bnx2x_sp_rtnl_task); @@ -12722,6 +12731,9 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev, pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS, PCICFG_VENDOR_ID_OFFSET); + /* Set PCIe reset type to fundamental for EEH recovery */ + pdev->needs_freset = 1; + /* AER (Advanced Error reporting) configuration */ rc = pci_enable_pcie_error_reporting(pdev); if (!rc) @@ -12766,7 +12778,7 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev, NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_RXCSUM | NETIF_F_LRO | NETIF_F_GRO | NETIF_F_RXHASH | NETIF_F_HW_VLAN_CTAG_TX; - if (!CHIP_IS_E1x(bp)) { + if (!chip_is_e1x) { dev->hw_features |= NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_IPIP | NETIF_F_GSO_SIT; dev->hw_enc_features = @@ -13665,9 +13677,9 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp) cancel_delayed_work_sync(&bp->sp_task); cancel_delayed_work_sync(&bp->period_task); - spin_lock_bh(&bp->stats_lock); + mutex_lock(&bp->stats_lock); bp->stats_state = STATS_STATE_DISABLED; - spin_unlock_bh(&bp->stats_lock); + mutex_unlock(&bp->stats_lock); bnx2x_save_statistics(bp); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c index e5aca2d..cfe3c769 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c @@ -2238,7 +2238,9 @@ int bnx2x_vf_close(struct bnx2x *bp, struct bnx2x_virtf *vf) cookie.vf = vf; cookie.state = VF_ACQUIRED; - bnx2x_stats_safe_exec(bp, bnx2x_set_vf_state, &cookie); + rc = bnx2x_stats_safe_exec(bp, bnx2x_set_vf_state, &cookie); + if (rc) + goto op_err; } DP(BNX2X_MSG_IOV, "set state to acquired\n"); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c index d160829..800ab44 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c @@ -123,36 +123,28 @@ static void bnx2x_dp_stats(struct bnx2x *bp) */ static void bnx2x_storm_stats_post(struct bnx2x *bp) { - if (!bp->stats_pending) { - int rc; + int rc; - spin_lock_bh(&bp->stats_lock); - - if (bp->stats_pending) { - spin_unlock_bh(&bp->stats_lock); - return; - } - - bp->fw_stats_req->hdr.drv_stats_counter = - cpu_to_le16(bp->stats_counter++); + if (bp->stats_pending) + return; - DP(BNX2X_MSG_STATS, "Sending statistics ramrod %d\n", - le16_to_cpu(bp->fw_stats_req->hdr.drv_stats_counter)); + bp->fw_stats_req->hdr.drv_stats_counter = + cpu_to_le16(bp->stats_counter++); - /* adjust the ramrod to include VF queues statistics */ - bnx2x_iov_adjust_stats_req(bp); - bnx2x_dp_stats(bp); + DP(BNX2X_MSG_STATS, "Sending statistics ramrod %d\n", + le16_to_cpu(bp->fw_stats_req->hdr.drv_stats_counter)); - /* send FW stats ramrod */ - rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_STAT_QUERY, 0, - U64_HI(bp->fw_stats_req_mapping), - U64_LO(bp->fw_stats_req_mapping), - NONE_CONNECTION_TYPE); - if (rc == 0) - bp->stats_pending = 1; + /* adjust the ramrod to include VF queues statistics */ + bnx2x_iov_adjust_stats_req(bp); + bnx2x_dp_stats(bp); - spin_unlock_bh(&bp->stats_lock); - } + /* send FW stats ramrod */ + rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_STAT_QUERY, 0, + U64_HI(bp->fw_stats_req_mapping), + U64_LO(bp->fw_stats_req_mapping), + NONE_CONNECTION_TYPE); + if (rc == 0) + bp->stats_pending = 1; } static void bnx2x_hw_stats_post(struct bnx2x *bp) @@ -221,7 +213,7 @@ static void bnx2x_stats_comp(struct bnx2x *bp) */ /* should be called under stats_sema */ -static void __bnx2x_stats_pmf_update(struct bnx2x *bp) +static void bnx2x_stats_pmf_update(struct bnx2x *bp) { struct dmae_command *dmae; u32 opcode; @@ -519,7 +511,7 @@ static void bnx2x_func_stats_init(struct bnx2x *bp) } /* should be called under stats_sema */ -static void __bnx2x_stats_start(struct bnx2x *bp) +static void bnx2x_stats_start(struct bnx2x *bp) { if (IS_PF(bp)) { if (bp->port.pmf) @@ -531,34 +523,13 @@ static void __bnx2x_stats_start(struct bnx2x *bp) bnx2x_hw_stats_post(bp); bnx2x_storm_stats_post(bp); } - - bp->stats_started = true; -} - -static void bnx2x_stats_start(struct bnx2x *bp) -{ - if (down_timeout(&bp->stats_sema, HZ/10)) - BNX2X_ERR("Unable to acquire stats lock\n"); - __bnx2x_stats_start(bp); - up(&bp->stats_sema); } static void bnx2x_stats_pmf_start(struct bnx2x *bp) { - if (down_timeout(&bp->stats_sema, HZ/10)) - BNX2X_ERR("Unable to acquire stats lock\n"); bnx2x_stats_comp(bp); - __bnx2x_stats_pmf_update(bp); - __bnx2x_stats_start(bp); - up(&bp->stats_sema); -} - -static void bnx2x_stats_pmf_update(struct bnx2x *bp) -{ - if (down_timeout(&bp->stats_sema, HZ/10)) - BNX2X_ERR("Unable to acquire stats lock\n"); - __bnx2x_stats_pmf_update(bp); - up(&bp->stats_sema); + bnx2x_stats_pmf_update(bp); + bnx2x_stats_start(bp); } static void bnx2x_stats_restart(struct bnx2x *bp) @@ -568,11 +539,9 @@ static void bnx2x_stats_restart(struct bnx2x *bp) */ if (IS_VF(bp)) return; - if (down_timeout(&bp->stats_sema, HZ/10)) - BNX2X_ERR("Unable to acquire stats lock\n"); + bnx2x_stats_comp(bp); - __bnx2x_stats_start(bp); - up(&bp->stats_sema); + bnx2x_stats_start(bp); } static void bnx2x_bmac_stats_update(struct bnx2x *bp) @@ -1246,18 +1215,12 @@ static void bnx2x_stats_update(struct bnx2x *bp) { u32 *stats_comp = bnx2x_sp(bp, stats_comp); - /* we run update from timer context, so give up - * if somebody is in the middle of transition - */ - if (down_trylock(&bp->stats_sema)) + if (bnx2x_edebug_stats_stopped(bp)) return; - if (bnx2x_edebug_stats_stopped(bp) || !bp->stats_started) - goto out; - if (IS_PF(bp)) { if (*stats_comp != DMAE_COMP_VAL) - goto out; + return; if (bp->port.pmf) bnx2x_hw_stats_update(bp); @@ -1267,7 +1230,7 @@ static void bnx2x_stats_update(struct bnx2x *bp) BNX2X_ERR("storm stats were not updated for 3 times\n"); bnx2x_panic(); } - goto out; + return; } } else { /* vf doesn't collect HW statistics, and doesn't get completions @@ -1281,7 +1244,7 @@ static void bnx2x_stats_update(struct bnx2x *bp) /* vf is done */ if (IS_VF(bp)) - goto out; + return; if (netif_msg_timer(bp)) { struct bnx2x_eth_stats *estats = &bp->eth_stats; @@ -1292,9 +1255,6 @@ static void bnx2x_stats_update(struct bnx2x *bp) bnx2x_hw_stats_post(bp); bnx2x_storm_stats_post(bp); - -out: - up(&bp->stats_sema); } static void bnx2x_port_stats_stop(struct bnx2x *bp) @@ -1358,12 +1318,7 @@ static void bnx2x_port_stats_stop(struct bnx2x *bp) static void bnx2x_stats_stop(struct bnx2x *bp) { - int update = 0; - - if (down_timeout(&bp->stats_sema, HZ/10)) - BNX2X_ERR("Unable to acquire stats lock\n"); - - bp->stats_started = false; + bool update = false; bnx2x_stats_comp(bp); @@ -1381,8 +1336,6 @@ static void bnx2x_stats_stop(struct bnx2x *bp) bnx2x_hw_stats_post(bp); bnx2x_stats_comp(bp); } - - up(&bp->stats_sema); } static void bnx2x_stats_do_nothing(struct bnx2x *bp) @@ -1410,18 +1363,28 @@ static const struct { void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event) { - enum bnx2x_stats_state state; - void (*action)(struct bnx2x *bp); + enum bnx2x_stats_state state = bp->stats_state; + if (unlikely(bp->panic)) return; - spin_lock_bh(&bp->stats_lock); - state = bp->stats_state; + /* Statistics update run from timer context, and we don't want to stop + * that context in case someone is in the middle of a transition. + * For other events, wait a bit until lock is taken. + */ + if (!mutex_trylock(&bp->stats_lock)) { + if (event == STATS_EVENT_UPDATE) + return; + + DP(BNX2X_MSG_STATS, + "Unlikely stats' lock contention [event %d]\n", event); + mutex_lock(&bp->stats_lock); + } + + bnx2x_stats_stm[state][event].action(bp); bp->stats_state = bnx2x_stats_stm[state][event].next_state; - action = bnx2x_stats_stm[state][event].action; - spin_unlock_bh(&bp->stats_lock); - action(bp); + mutex_unlock(&bp->stats_lock); if ((event != STATS_EVENT_UPDATE) || netif_msg_timer(bp)) DP(BNX2X_MSG_STATS, "state %d -> event %d -> state %d\n", @@ -1998,13 +1961,34 @@ void bnx2x_afex_collect_stats(struct bnx2x *bp, void *void_afex_stats, } } -void bnx2x_stats_safe_exec(struct bnx2x *bp, - void (func_to_exec)(void *cookie), - void *cookie){ - if (down_timeout(&bp->stats_sema, HZ/10)) - BNX2X_ERR("Unable to acquire stats lock\n"); +int bnx2x_stats_safe_exec(struct bnx2x *bp, + void (func_to_exec)(void *cookie), + void *cookie) +{ + int cnt = 10, rc = 0; + + /* Wait for statistics to end [while blocking further requests], + * then run supplied function 'safely'. + */ + mutex_lock(&bp->stats_lock); + bnx2x_stats_comp(bp); + while (bp->stats_pending && cnt--) + if (bnx2x_storm_stats_update(bp)) + usleep_range(1000, 2000); + if (bp->stats_pending) { + BNX2X_ERR("Failed to wait for stats pending to clear [possibly FW is stuck]\n"); + rc = -EBUSY; + goto out; + } + func_to_exec(cookie); - __bnx2x_stats_start(bp); - up(&bp->stats_sema); + +out: + /* No need to restart statistics - if they're enabled, the timer + * will restart the statistics. + */ + mutex_unlock(&bp->stats_lock); + + return rc; } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h index 2beceae..965539a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h @@ -539,9 +539,9 @@ struct bnx2x; void bnx2x_memset_stats(struct bnx2x *bp); void bnx2x_stats_init(struct bnx2x *bp); void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event); -void bnx2x_stats_safe_exec(struct bnx2x *bp, - void (func_to_exec)(void *cookie), - void *cookie); +int bnx2x_stats_safe_exec(struct bnx2x *bp, + void (func_to_exec)(void *cookie), + void *cookie); /** * bnx2x_save_statistics - save statistics when unloading. diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index ff83c46b..6befde6 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -487,6 +487,7 @@ enum bcmgenet_stat_type { BCMGENET_STAT_MIB_TX, BCMGENET_STAT_RUNT, BCMGENET_STAT_MISC, + BCMGENET_STAT_SOFT, }; struct bcmgenet_stats { @@ -515,6 +516,7 @@ struct bcmgenet_stats { #define STAT_GENET_MIB_RX(str, m) STAT_GENET_MIB(str, m, BCMGENET_STAT_MIB_RX) #define STAT_GENET_MIB_TX(str, m) STAT_GENET_MIB(str, m, BCMGENET_STAT_MIB_TX) #define STAT_GENET_RUNT(str, m) STAT_GENET_MIB(str, m, BCMGENET_STAT_RUNT) +#define STAT_GENET_SOFT_MIB(str, m) STAT_GENET_MIB(str, m, BCMGENET_STAT_SOFT) #define STAT_GENET_MISC(str, m, offset) { \ .stat_string = str, \ @@ -614,9 +616,9 @@ static const struct bcmgenet_stats bcmgenet_gstrings_stats[] = { UMAC_RBUF_OVFL_CNT), STAT_GENET_MISC("rbuf_err_cnt", mib.rbuf_err_cnt, UMAC_RBUF_ERR_CNT), STAT_GENET_MISC("mdf_err_cnt", mib.mdf_err_cnt, UMAC_MDF_ERR_CNT), - STAT_GENET_MIB_RX("alloc_rx_buff_failed", mib.alloc_rx_buff_failed), - STAT_GENET_MIB_RX("rx_dma_failed", mib.rx_dma_failed), - STAT_GENET_MIB_TX("tx_dma_failed", mib.tx_dma_failed), + STAT_GENET_SOFT_MIB("alloc_rx_buff_failed", mib.alloc_rx_buff_failed), + STAT_GENET_SOFT_MIB("rx_dma_failed", mib.rx_dma_failed), + STAT_GENET_SOFT_MIB("tx_dma_failed", mib.tx_dma_failed), }; #define BCMGENET_STATS_LEN ARRAY_SIZE(bcmgenet_gstrings_stats) @@ -668,6 +670,7 @@ static void bcmgenet_update_mib_counters(struct bcmgenet_priv *priv) s = &bcmgenet_gstrings_stats[i]; switch (s->type) { case BCMGENET_STAT_NETDEV: + case BCMGENET_STAT_SOFT: continue; case BCMGENET_STAT_MIB_RX: case BCMGENET_STAT_MIB_TX: @@ -971,13 +974,14 @@ static inline void bcmgenet_tx_ring_int_disable(struct bcmgenet_priv *priv, } /* Unlocked version of the reclaim routine */ -static void __bcmgenet_tx_reclaim(struct net_device *dev, - struct bcmgenet_tx_ring *ring) +static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev, + struct bcmgenet_tx_ring *ring) { struct bcmgenet_priv *priv = netdev_priv(dev); int last_tx_cn, last_c_index, num_tx_bds; struct enet_cb *tx_cb_ptr; struct netdev_queue *txq; + unsigned int pkts_compl = 0; unsigned int bds_compl; unsigned int c_index; @@ -1005,6 +1009,7 @@ static void __bcmgenet_tx_reclaim(struct net_device *dev, tx_cb_ptr = ring->cbs + last_c_index; bds_compl = 0; if (tx_cb_ptr->skb) { + pkts_compl++; bds_compl = skb_shinfo(tx_cb_ptr->skb)->nr_frags + 1; dev->stats.tx_bytes += tx_cb_ptr->skb->len; dma_unmap_single(&dev->dev, @@ -1028,23 +1033,45 @@ static void __bcmgenet_tx_reclaim(struct net_device *dev, last_c_index &= (num_tx_bds - 1); } - if (ring->free_bds > (MAX_SKB_FRAGS + 1)) - ring->int_disable(priv, ring); - - if (netif_tx_queue_stopped(txq)) - netif_tx_wake_queue(txq); + if (ring->free_bds > (MAX_SKB_FRAGS + 1)) { + if (netif_tx_queue_stopped(txq)) + netif_tx_wake_queue(txq); + } ring->c_index = c_index; + + return pkts_compl; } -static void bcmgenet_tx_reclaim(struct net_device *dev, +static unsigned int bcmgenet_tx_reclaim(struct net_device *dev, struct bcmgenet_tx_ring *ring) { + unsigned int released; unsigned long flags; spin_lock_irqsave(&ring->lock, flags); - __bcmgenet_tx_reclaim(dev, ring); + released = __bcmgenet_tx_reclaim(dev, ring); spin_unlock_irqrestore(&ring->lock, flags); + + return released; +} + +static int bcmgenet_tx_poll(struct napi_struct *napi, int budget) +{ + struct bcmgenet_tx_ring *ring = + container_of(napi, struct bcmgenet_tx_ring, napi); + unsigned int work_done = 0; + + work_done = bcmgenet_tx_reclaim(ring->priv->dev, ring); + + if (work_done == 0) { + napi_complete(napi); + ring->int_enable(ring->priv, ring); + + return 0; + } + + return budget; } static void bcmgenet_tx_reclaim_all(struct net_device *dev) @@ -1302,10 +1329,8 @@ static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev) bcmgenet_tdma_ring_writel(priv, ring->index, ring->prod_index, TDMA_PROD_INDEX); - if (ring->free_bds <= (MAX_SKB_FRAGS + 1)) { + if (ring->free_bds <= (MAX_SKB_FRAGS + 1)) netif_tx_stop_queue(txq); - ring->int_enable(priv, ring); - } out: spin_unlock_irqrestore(&ring->lock, flags); @@ -1621,6 +1646,7 @@ static int init_umac(struct bcmgenet_priv *priv) struct device *kdev = &priv->pdev->dev; int ret; u32 reg, cpu_mask_clear; + int index; dev_dbg(&priv->pdev->dev, "bcmgenet: init_umac\n"); @@ -1647,7 +1673,7 @@ static int init_umac(struct bcmgenet_priv *priv) bcmgenet_intr_disable(priv); - cpu_mask_clear = UMAC_IRQ_RXDMA_BDONE; + cpu_mask_clear = UMAC_IRQ_RXDMA_BDONE | UMAC_IRQ_TXDMA_BDONE; dev_dbg(kdev, "%s:Enabling RXDMA_BDONE interrupt\n", __func__); @@ -1674,6 +1700,10 @@ static int init_umac(struct bcmgenet_priv *priv) bcmgenet_intrl2_0_writel(priv, cpu_mask_clear, INTRL2_CPU_MASK_CLEAR); + for (index = 0; index < priv->hw_params->tx_queues; index++) + bcmgenet_intrl2_1_writel(priv, (1 << index), + INTRL2_CPU_MASK_CLEAR); + /* Enable rx/tx engine.*/ dev_dbg(kdev, "done init umac\n"); @@ -1693,6 +1723,8 @@ static void bcmgenet_init_tx_ring(struct bcmgenet_priv *priv, unsigned int first_bd; spin_lock_init(&ring->lock); + ring->priv = priv; + netif_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, 64); ring->index = index; if (index == DESC_INDEX) { ring->queue = 0; @@ -1738,6 +1770,17 @@ static void bcmgenet_init_tx_ring(struct bcmgenet_priv *priv, TDMA_WRITE_PTR); bcmgenet_tdma_ring_writel(priv, index, end_ptr * words_per_bd - 1, DMA_END_ADDR); + + napi_enable(&ring->napi); +} + +static void bcmgenet_fini_tx_ring(struct bcmgenet_priv *priv, + unsigned int index) +{ + struct bcmgenet_tx_ring *ring = &priv->tx_rings[index]; + + napi_disable(&ring->napi); + netif_napi_del(&ring->napi); } /* Initialize a RDMA ring */ @@ -1907,7 +1950,7 @@ static int bcmgenet_dma_teardown(struct bcmgenet_priv *priv) return ret; } -static void bcmgenet_fini_dma(struct bcmgenet_priv *priv) +static void __bcmgenet_fini_dma(struct bcmgenet_priv *priv) { int i; @@ -1926,6 +1969,18 @@ static void bcmgenet_fini_dma(struct bcmgenet_priv *priv) kfree(priv->tx_cbs); } +static void bcmgenet_fini_dma(struct bcmgenet_priv *priv) +{ + int i; + + bcmgenet_fini_tx_ring(priv, DESC_INDEX); + + for (i = 0; i < priv->hw_params->tx_queues; i++) + bcmgenet_fini_tx_ring(priv, i); + + __bcmgenet_fini_dma(priv); +} + /* init_edma: Initialize DMA control register */ static int bcmgenet_init_dma(struct bcmgenet_priv *priv) { @@ -1952,7 +2007,7 @@ static int bcmgenet_init_dma(struct bcmgenet_priv *priv) priv->tx_cbs = kcalloc(priv->num_tx_bds, sizeof(struct enet_cb), GFP_KERNEL); if (!priv->tx_cbs) { - bcmgenet_fini_dma(priv); + __bcmgenet_fini_dma(priv); return -ENOMEM; } @@ -1975,9 +2030,6 @@ static int bcmgenet_poll(struct napi_struct *napi, int budget) struct bcmgenet_priv, napi); unsigned int work_done; - /* tx reclaim */ - bcmgenet_tx_reclaim(priv->dev, &priv->tx_rings[DESC_INDEX]); - work_done = bcmgenet_desc_rx(priv, budget); /* Advancing our consumer index*/ @@ -2022,28 +2074,34 @@ static void bcmgenet_irq_task(struct work_struct *work) static irqreturn_t bcmgenet_isr1(int irq, void *dev_id) { struct bcmgenet_priv *priv = dev_id; + struct bcmgenet_tx_ring *ring; unsigned int index; /* Save irq status for bottom-half processing. */ priv->irq1_stat = bcmgenet_intrl2_1_readl(priv, INTRL2_CPU_STAT) & - ~priv->int1_mask; + ~bcmgenet_intrl2_1_readl(priv, INTRL2_CPU_MASK_STATUS); /* clear interrupts */ bcmgenet_intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR); netif_dbg(priv, intr, priv->dev, "%s: IRQ=0x%x\n", __func__, priv->irq1_stat); + /* Check the MBDONE interrupts. * packet is done, reclaim descriptors */ - if (priv->irq1_stat & 0x0000ffff) { - index = 0; - for (index = 0; index < 16; index++) { - if (priv->irq1_stat & (1 << index)) - bcmgenet_tx_reclaim(priv->dev, - &priv->tx_rings[index]); + for (index = 0; index < priv->hw_params->tx_queues; index++) { + if (!(priv->irq1_stat & BIT(index))) + continue; + + ring = &priv->tx_rings[index]; + + if (likely(napi_schedule_prep(&ring->napi))) { + ring->int_disable(priv, ring); + __napi_schedule(&ring->napi); } } + return IRQ_HANDLED; } @@ -2075,8 +2133,12 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id) } if (priv->irq0_stat & (UMAC_IRQ_TXDMA_BDONE | UMAC_IRQ_TXDMA_PDONE)) { - /* Tx reclaim */ - bcmgenet_tx_reclaim(priv->dev, &priv->tx_rings[DESC_INDEX]); + struct bcmgenet_tx_ring *ring = &priv->tx_rings[DESC_INDEX]; + + if (likely(napi_schedule_prep(&ring->napi))) { + ring->int_disable(priv, ring); + __napi_schedule(&ring->napi); + } } if (priv->irq0_stat & (UMAC_IRQ_PHY_DET_R | UMAC_IRQ_PHY_DET_F | diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index b36ddec..0d370d1 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -520,6 +520,7 @@ struct bcmgenet_hw_params { struct bcmgenet_tx_ring { spinlock_t lock; /* ring lock */ + struct napi_struct napi; /* NAPI per tx queue */ unsigned int index; /* ring index */ unsigned int queue; /* queue index */ struct enet_cb *cbs; /* tx ring buffer control block*/ @@ -534,6 +535,7 @@ struct bcmgenet_tx_ring { struct bcmgenet_tx_ring *); void (*int_disable)(struct bcmgenet_priv *priv, struct bcmgenet_tx_ring *); + struct bcmgenet_priv *priv; }; /* device context */ diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c b/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c index 149a0d7..b971229 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c @@ -73,15 +73,17 @@ int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE)) return -EINVAL; + reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); if (wol->wolopts & WAKE_MAGICSECURE) { bcmgenet_umac_writel(priv, get_unaligned_be16(&wol->sopass[0]), UMAC_MPD_PW_MS); bcmgenet_umac_writel(priv, get_unaligned_be32(&wol->sopass[2]), UMAC_MPD_PW_LS); - reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); reg |= MPD_PW_EN; - bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); + } else { + reg &= ~MPD_PW_EN; } + bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); /* Flag the device and relevant IRQ as wakeup capable */ if (wol->wolopts) { diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index ad76b8e..81d4153 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -2113,17 +2113,17 @@ static const struct net_device_ops macb_netdev_ops = { }; #if defined(CONFIG_OF) -static struct macb_config pc302gem_config = { +static const struct macb_config pc302gem_config = { .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE, .dma_burst_length = 16, }; -static struct macb_config sama5d3_config = { +static const struct macb_config sama5d3_config = { .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE, .dma_burst_length = 16, }; -static struct macb_config sama5d4_config = { +static const struct macb_config sama5d4_config = { .caps = 0, .dma_burst_length = 4, }; @@ -2154,7 +2154,7 @@ static void macb_configure_caps(struct macb *bp) if (bp->pdev->dev.of_node) { match = of_match_node(macb_dt_ids, bp->pdev->dev.of_node); if (match && match->data) { - config = (const struct macb_config *)match->data; + config = match->data; bp->caps = config->caps; /* diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 31dc080..ff85619 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -351,7 +351,7 @@ /* Bitfields in MID */ #define MACB_IDNUM_OFFSET 16 -#define MACB_IDNUM_SIZE 16 +#define MACB_IDNUM_SIZE 12 #define MACB_REV_OFFSET 0 #define MACB_REV_SIZE 16 diff --git a/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c b/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c index 9062a84..c308429 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c +++ b/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c @@ -35,10 +35,10 @@ static inline unsigned int ipv6_clip_hash(struct clip_tbl *d, const u32 *key) } static unsigned int clip_addr_hash(struct clip_tbl *ctbl, const u32 *addr, - int addr_len) + u8 v6) { - return addr_len == 4 ? ipv4_clip_hash(ctbl, addr) : - ipv6_clip_hash(ctbl, addr); + return v6 ? ipv6_clip_hash(ctbl, addr) : + ipv4_clip_hash(ctbl, addr); } static int clip6_get_mbox(const struct net_device *dev, @@ -78,23 +78,22 @@ int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6) struct clip_entry *ce, *cte; u32 *addr = (u32 *)lip; int hash; - int addr_len; - int ret = 0; + int ret = -1; if (!ctbl) return 0; - if (v6) - addr_len = 16; - else - addr_len = 4; - - hash = clip_addr_hash(ctbl, addr, addr_len); + hash = clip_addr_hash(ctbl, addr, v6); read_lock_bh(&ctbl->lock); list_for_each_entry(cte, &ctbl->hash_list[hash], list) { - if (addr_len == cte->addr_len && - memcmp(lip, cte->addr, cte->addr_len) == 0) { + if (cte->addr6.sin6_family == AF_INET6 && v6) + ret = memcmp(lip, cte->addr6.sin6_addr.s6_addr, + sizeof(struct in6_addr)); + else if (cte->addr.sin_family == AF_INET && !v6) + ret = memcmp(lip, (char *)(&cte->addr.sin_addr), + sizeof(struct in_addr)); + if (!ret) { ce = cte; read_unlock_bh(&ctbl->lock); goto found; @@ -111,15 +110,20 @@ int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6) spin_lock_init(&ce->lock); atomic_set(&ce->refcnt, 0); atomic_dec(&ctbl->nfree); - ce->addr_len = addr_len; - memcpy(ce->addr, lip, addr_len); list_add_tail(&ce->list, &ctbl->hash_list[hash]); if (v6) { + ce->addr6.sin6_family = AF_INET6; + memcpy(ce->addr6.sin6_addr.s6_addr, + lip, sizeof(struct in6_addr)); ret = clip6_get_mbox(dev, (const struct in6_addr *)lip); if (ret) { write_unlock_bh(&ctbl->lock); return ret; } + } else { + ce->addr.sin_family = AF_INET; + memcpy((char *)(&ce->addr.sin_addr), lip, + sizeof(struct in_addr)); } } else { write_unlock_bh(&ctbl->lock); @@ -140,19 +144,19 @@ void cxgb4_clip_release(const struct net_device *dev, const u32 *lip, u8 v6) struct clip_entry *ce, *cte; u32 *addr = (u32 *)lip; int hash; - int addr_len; - - if (v6) - addr_len = 16; - else - addr_len = 4; + int ret = -1; - hash = clip_addr_hash(ctbl, addr, addr_len); + hash = clip_addr_hash(ctbl, addr, v6); read_lock_bh(&ctbl->lock); list_for_each_entry(cte, &ctbl->hash_list[hash], list) { - if (addr_len == cte->addr_len && - memcmp(lip, cte->addr, cte->addr_len) == 0) { + if (cte->addr6.sin6_family == AF_INET6 && v6) + ret = memcmp(lip, cte->addr6.sin6_addr.s6_addr, + sizeof(struct in6_addr)); + else if (cte->addr.sin_family == AF_INET && !v6) + ret = memcmp(lip, (char *)(&cte->addr.sin_addr), + sizeof(struct in_addr)); + if (!ret) { ce = cte; read_unlock_bh(&ctbl->lock); goto found; @@ -249,10 +253,7 @@ int clip_tbl_show(struct seq_file *seq, void *v) for (i = 0 ; i < ctbl->clipt_size; ++i) { list_for_each_entry(ce, &ctbl->hash_list[i], list) { ip[0] = '\0'; - if (ce->addr_len == 16) - sprintf(ip, "%pI6c", ce->addr); - else - sprintf(ip, "%pI4c", ce->addr); + sprintf(ip, "%pISc", &ce->addr); seq_printf(seq, "%-25s %u\n", ip, atomic_read(&ce->refcnt)); } diff --git a/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.h b/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.h index 2eaba01..35eb43c 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.h +++ b/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.h @@ -14,8 +14,10 @@ struct clip_entry { spinlock_t lock; /* Hold while modifying clip reference */ atomic_t refcnt; struct list_head list; - u32 addr[4]; - int addr_len; + union { + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + }; }; struct clip_tbl { diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index d6cda17..c6ff489 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -376,8 +376,6 @@ enum { enum { INGQ_EXTRAS = 2, /* firmware event queue and */ /* forwarded interrupts */ - MAX_EGRQ = MAX_ETH_QSETS*2 + MAX_OFLD_QSETS*2 - + MAX_CTRL_QUEUES + MAX_RDMA_QUEUES + MAX_ISCSI_QUEUES, MAX_INGQ = MAX_ETH_QSETS + MAX_OFLD_QSETS + MAX_RDMA_QUEUES + MAX_RDMA_CIQS + MAX_ISCSI_QUEUES + INGQ_EXTRAS, }; @@ -616,11 +614,13 @@ struct sge { unsigned int idma_qid[2]; /* SGE IDMA Hung Ingress Queue ID */ unsigned int egr_start; + unsigned int egr_sz; unsigned int ingr_start; - void *egr_map[MAX_EGRQ]; /* qid->queue egress queue map */ - struct sge_rspq *ingr_map[MAX_INGQ]; /* qid->queue ingress queue map */ - DECLARE_BITMAP(starving_fl, MAX_EGRQ); - DECLARE_BITMAP(txq_maperr, MAX_EGRQ); + unsigned int ingr_sz; + void **egr_map; /* qid->queue egress queue map */ + struct sge_rspq **ingr_map; /* qid->queue ingress queue map */ + unsigned long *starving_fl; + unsigned long *txq_maperr; struct timer_list rx_timer; /* refills starving FLs */ struct timer_list tx_timer; /* checks Tx queues */ }; @@ -1103,7 +1103,7 @@ int t4_restart_aneg(struct adapter *adap, unsigned int mbox, unsigned int port); #define T4_MEMORY_WRITE 0 #define T4_MEMORY_READ 1 int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, u32 len, - __be32 *buf, int dir); + void *buf, int dir); static inline int t4_memory_write(struct adapter *adap, int mtype, u32 addr, u32 len, __be32 *buf) { @@ -1136,6 +1136,8 @@ int cxgb4_t4_bar2_sge_qregs(struct adapter *adapter, unsigned int qtimer_val(const struct adapter *adap, const struct sge_rspq *q); + +int t4_init_devlog_params(struct adapter *adapter); int t4_init_sge_params(struct adapter *adapter); int t4_init_tp_params(struct adapter *adap); int t4_filter_field_shift(const struct adapter *adap, int filter_sel); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index 78854ce..dcb0479 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -670,9 +670,13 @@ static int cctrl_tbl_show(struct seq_file *seq, void *v) "0.9375" }; int i; - u16 incr[NMTUS][NCCTRL_WIN]; + u16 (*incr)[NCCTRL_WIN]; struct adapter *adap = seq->private; + incr = kmalloc(sizeof(*incr) * NMTUS, GFP_KERNEL); + if (!incr) + return -ENOMEM; + t4_read_cong_tbl(adap, incr); for (i = 0; i < NCCTRL_WIN; ++i) { @@ -685,6 +689,8 @@ static int cctrl_tbl_show(struct seq_file *seq, void *v) adap->params.a_wnd[i], dec_fac[adap->params.b_wnd[i]]); } + + kfree(incr); return 0; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index a22cf93..d929951 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -920,7 +920,7 @@ static void quiesce_rx(struct adapter *adap) { int i; - for (i = 0; i < ARRAY_SIZE(adap->sge.ingr_map); i++) { + for (i = 0; i < adap->sge.ingr_sz; i++) { struct sge_rspq *q = adap->sge.ingr_map[i]; if (q && q->handler) { @@ -934,6 +934,21 @@ static void quiesce_rx(struct adapter *adap) } } +/* Disable interrupt and napi handler */ +static void disable_interrupts(struct adapter *adap) +{ + if (adap->flags & FULL_INIT_DONE) { + t4_intr_disable(adap); + if (adap->flags & USING_MSIX) { + free_msix_queue_irqs(adap); + free_irq(adap->msix_info[0].vec, adap); + } else { + free_irq(adap->pdev->irq, adap); + } + quiesce_rx(adap); + } +} + /* * Enable NAPI scheduling and interrupt generation for all Rx queues. */ @@ -941,7 +956,7 @@ static void enable_rx(struct adapter *adap) { int i; - for (i = 0; i < ARRAY_SIZE(adap->sge.ingr_map); i++) { + for (i = 0; i < adap->sge.ingr_sz; i++) { struct sge_rspq *q = adap->sge.ingr_map[i]; if (!q) @@ -970,8 +985,8 @@ static int setup_sge_queues(struct adapter *adap) int err, msi_idx, i, j; struct sge *s = &adap->sge; - bitmap_zero(s->starving_fl, MAX_EGRQ); - bitmap_zero(s->txq_maperr, MAX_EGRQ); + bitmap_zero(s->starving_fl, s->egr_sz); + bitmap_zero(s->txq_maperr, s->egr_sz); if (adap->flags & USING_MSIX) msi_idx = 1; /* vector 0 is for non-queue interrupts */ @@ -983,6 +998,19 @@ static int setup_sge_queues(struct adapter *adap) msi_idx = -((int)s->intrq.abs_id + 1); } + /* NOTE: If you add/delete any Ingress/Egress Queue allocations in here, + * don't forget to update the following which need to be + * synchronized to and changes here. + * + * 1. The calculations of MAX_INGQ in cxgb4.h. + * + * 2. Update enable_msix/name_msix_vecs/request_msix_queue_irqs + * to accommodate any new/deleted Ingress Queues + * which need MSI-X Vectors. + * + * 3. Update sge_qinfo_show() to include information on the + * new/deleted queues. + */ err = t4_sge_alloc_rxq(adap, &s->fw_evtq, true, adap->port[0], msi_idx, NULL, fwevtq_handler); if (err) { @@ -4244,19 +4272,12 @@ static int cxgb_up(struct adapter *adap) static void cxgb_down(struct adapter *adapter) { - t4_intr_disable(adapter); cancel_work_sync(&adapter->tid_release_task); cancel_work_sync(&adapter->db_full_task); cancel_work_sync(&adapter->db_drop_task); adapter->tid_release_task_busy = false; adapter->tid_release_head = NULL; - if (adapter->flags & USING_MSIX) { - free_msix_queue_irqs(adapter); - free_irq(adapter->msix_info[0].vec, adapter); - } else - free_irq(adapter->pdev->irq, adapter); - quiesce_rx(adapter); t4_sge_stop(adapter); t4_free_sge_resources(adapter); adapter->flags &= ~FULL_INIT_DONE; @@ -4733,8 +4754,9 @@ static int adap_init1(struct adapter *adap, struct fw_caps_config_cmd *c) if (ret < 0) return ret; - ret = t4_cfg_pfvf(adap, adap->fn, adap->fn, 0, MAX_EGRQ, 64, MAX_INGQ, - 0, 0, 4, 0xf, 0xf, 16, FW_CMD_CAP_PF, FW_CMD_CAP_PF); + ret = t4_cfg_pfvf(adap, adap->fn, adap->fn, 0, adap->sge.egr_sz, 64, + MAX_INGQ, 0, 0, 4, 0xf, 0xf, 16, FW_CMD_CAP_PF, + FW_CMD_CAP_PF); if (ret < 0) return ret; @@ -5088,10 +5110,15 @@ static int adap_init0(struct adapter *adap) enum dev_state state; u32 params[7], val[7]; struct fw_caps_config_cmd caps_cmd; - struct fw_devlog_cmd devlog_cmd; - u32 devlog_meminfo; int reset = 1; + /* Grab Firmware Device Log parameters as early as possible so we have + * access to it for debugging, etc. + */ + ret = t4_init_devlog_params(adap); + if (ret < 0) + return ret; + /* Contact FW, advertising Master capability */ ret = t4_fw_hello(adap, adap->mbox, adap->mbox, MASTER_MAY, &state); if (ret < 0) { @@ -5169,30 +5196,6 @@ static int adap_init0(struct adapter *adap) if (ret < 0) goto bye; - /* Read firmware device log parameters. We really need to find a way - * to get these parameters initialized with some default values (which - * are likely to be correct) for the case where we either don't - * attache to the firmware or it's crashed when we probe the adapter. - * That way we'll still be able to perform early firmware startup - * debugging ... If the request to get the Firmware's Device Log - * parameters fails, we'll live so we don't make that a fatal error. - */ - memset(&devlog_cmd, 0, sizeof(devlog_cmd)); - devlog_cmd.op_to_write = htonl(FW_CMD_OP_V(FW_DEVLOG_CMD) | - FW_CMD_REQUEST_F | FW_CMD_READ_F); - devlog_cmd.retval_len16 = htonl(FW_LEN16(devlog_cmd)); - ret = t4_wr_mbox(adap, adap->mbox, &devlog_cmd, sizeof(devlog_cmd), - &devlog_cmd); - if (ret == 0) { - devlog_meminfo = - ntohl(devlog_cmd.memtype_devlog_memaddr16_devlog); - adap->params.devlog.memtype = - FW_DEVLOG_CMD_MEMTYPE_DEVLOG_G(devlog_meminfo); - adap->params.devlog.start = - FW_DEVLOG_CMD_MEMADDR16_DEVLOG_G(devlog_meminfo) << 4; - adap->params.devlog.size = ntohl(devlog_cmd.memsize_devlog); - } - /* * Find out what ports are available to us. Note that we need to do * this before calling adap_init0_no_config() since it needs nports @@ -5293,6 +5296,51 @@ static int adap_init0(struct adapter *adap) adap->tids.nftids = val[4] - val[3] + 1; adap->sge.ingr_start = val[5]; + /* qids (ingress/egress) returned from firmware can be anywhere + * in the range from EQ(IQFLINT)_START to EQ(IQFLINT)_END. + * Hence driver needs to allocate memory for this range to + * store the queue info. Get the highest IQFLINT/EQ index returned + * in FW_EQ_*_CMD.alloc command. + */ + params[0] = FW_PARAM_PFVF(EQ_END); + params[1] = FW_PARAM_PFVF(IQFLINT_END); + ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 2, params, val); + if (ret < 0) + goto bye; + adap->sge.egr_sz = val[0] - adap->sge.egr_start + 1; + adap->sge.ingr_sz = val[1] - adap->sge.ingr_start + 1; + + adap->sge.egr_map = kcalloc(adap->sge.egr_sz, + sizeof(*adap->sge.egr_map), GFP_KERNEL); + if (!adap->sge.egr_map) { + ret = -ENOMEM; + goto bye; + } + + adap->sge.ingr_map = kcalloc(adap->sge.ingr_sz, + sizeof(*adap->sge.ingr_map), GFP_KERNEL); + if (!adap->sge.ingr_map) { + ret = -ENOMEM; + goto bye; + } + + /* Allocate the memory for the vaious egress queue bitmaps + * ie starving_fl and txq_maperr. + */ + adap->sge.starving_fl = kcalloc(BITS_TO_LONGS(adap->sge.egr_sz), + sizeof(long), GFP_KERNEL); + if (!adap->sge.starving_fl) { + ret = -ENOMEM; + goto bye; + } + + adap->sge.txq_maperr = kcalloc(BITS_TO_LONGS(adap->sge.egr_sz), + sizeof(long), GFP_KERNEL); + if (!adap->sge.txq_maperr) { + ret = -ENOMEM; + goto bye; + } + params[0] = FW_PARAM_PFVF(CLIP_START); params[1] = FW_PARAM_PFVF(CLIP_END); ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 2, params, val); @@ -5501,6 +5549,10 @@ static int adap_init0(struct adapter *adap) * happened to HW/FW, stop issuing commands. */ bye: + kfree(adap->sge.egr_map); + kfree(adap->sge.ingr_map); + kfree(adap->sge.starving_fl); + kfree(adap->sge.txq_maperr); if (ret != -ETIMEDOUT && ret != -EIO) t4_fw_bye(adap, adap->mbox); return ret; @@ -5528,6 +5580,7 @@ static pci_ers_result_t eeh_err_detected(struct pci_dev *pdev, netif_carrier_off(dev); } spin_unlock(&adap->stats_lock); + disable_interrupts(adap); if (adap->flags & FULL_INIT_DONE) cxgb_down(adap); rtnl_unlock(); @@ -5912,6 +5965,10 @@ static void free_some_resources(struct adapter *adapter) t4_free_mem(adapter->l2t); t4_free_mem(adapter->tids.tid_tab); + kfree(adapter->sge.egr_map); + kfree(adapter->sge.ingr_map); + kfree(adapter->sge.starving_fl); + kfree(adapter->sge.txq_maperr); disable_msi(adapter); for_each_port(adapter, i) @@ -6237,6 +6294,8 @@ static void remove_one(struct pci_dev *pdev) if (is_offload(adapter)) detach_ulds(adapter); + disable_interrupts(adapter); + for_each_port(adapter, i) if (adapter->port[i]->reg_state == NETREG_REGISTERED) unregister_netdev(adapter->port[i]); diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index b4b9f60..b688b32 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -2171,7 +2171,7 @@ static void sge_rx_timer_cb(unsigned long data) struct adapter *adap = (struct adapter *)data; struct sge *s = &adap->sge; - for (i = 0; i < ARRAY_SIZE(s->starving_fl); i++) + for (i = 0; i < BITS_TO_LONGS(s->egr_sz); i++) for (m = s->starving_fl[i]; m; m &= m - 1) { struct sge_eth_rxq *rxq; unsigned int id = __ffs(m) + i * BITS_PER_LONG; @@ -2259,7 +2259,7 @@ static void sge_tx_timer_cb(unsigned long data) struct adapter *adap = (struct adapter *)data; struct sge *s = &adap->sge; - for (i = 0; i < ARRAY_SIZE(s->txq_maperr); i++) + for (i = 0; i < BITS_TO_LONGS(s->egr_sz); i++) for (m = s->txq_maperr[i]; m; m &= m - 1) { unsigned long id = __ffs(m) + i * BITS_PER_LONG; struct sge_ofld_txq *txq = s->egr_map[id]; @@ -2741,7 +2741,8 @@ void t4_free_sge_resources(struct adapter *adap) free_rspq_fl(adap, &adap->sge.intrq, NULL); /* clear the reverse egress queue map */ - memset(adap->sge.egr_map, 0, sizeof(adap->sge.egr_map)); + memset(adap->sge.egr_map, 0, + adap->sge.egr_sz * sizeof(*adap->sge.egr_map)); } void t4_sge_start(struct adapter *adap) diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 4d643b6..ee394dc 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -449,7 +449,7 @@ int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *ecc) * @mtype: memory type: MEM_EDC0, MEM_EDC1 or MEM_MC * @addr: address within indicated memory type * @len: amount of memory to transfer - * @buf: host memory buffer + * @hbuf: host memory buffer * @dir: direction of transfer T4_MEMORY_READ (1) or T4_MEMORY_WRITE (0) * * Reads/writes an [almost] arbitrary memory region in the firmware: the @@ -460,15 +460,17 @@ int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *ecc) * caller's responsibility to perform appropriate byte order conversions. */ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, - u32 len, __be32 *buf, int dir) + u32 len, void *hbuf, int dir) { u32 pos, offset, resid, memoffset; u32 edc_size, mc_size, win_pf, mem_reg, mem_aperture, mem_base; + u32 *buf; /* Argument sanity checks ... */ - if (addr & 0x3) + if (addr & 0x3 || (uintptr_t)hbuf & 0x3) return -EINVAL; + buf = (u32 *)hbuf; /* It's convenient to be able to handle lengths which aren't a * multiple of 32-bits because we often end up transferring files to @@ -532,14 +534,45 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, /* Transfer data to/from the adapter as long as there's an integral * number of 32-bit transfers to complete. + * + * A note on Endianness issues: + * + * The "register" reads and writes below from/to the PCI-E Memory + * Window invoke the standard adapter Big-Endian to PCI-E Link + * Little-Endian "swizzel." As a result, if we have the following + * data in adapter memory: + * + * Memory: ... | b0 | b1 | b2 | b3 | ... + * Address: i+0 i+1 i+2 i+3 + * + * Then a read of the adapter memory via the PCI-E Memory Window + * will yield: + * + * x = readl(i) + * 31 0 + * [ b3 | b2 | b1 | b0 ] + * + * If this value is stored into local memory on a Little-Endian system + * it will show up correctly in local memory as: + * + * ( ..., b0, b1, b2, b3, ... ) + * + * But on a Big-Endian system, the store will show up in memory + * incorrectly swizzled as: + * + * ( ..., b3, b2, b1, b0, ... ) + * + * So we need to account for this in the reads and writes to the + * PCI-E Memory Window below by undoing the register read/write + * swizzels. */ while (len > 0) { if (dir == T4_MEMORY_READ) - *buf++ = (__force __be32) t4_read_reg(adap, - mem_base + offset); + *buf++ = le32_to_cpu((__force __le32)t4_read_reg(adap, + mem_base + offset)); else t4_write_reg(adap, mem_base + offset, - (__force u32) *buf++); + (__force u32)cpu_to_le32(*buf++)); offset += sizeof(__be32); len -= sizeof(__be32); @@ -568,15 +601,16 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, */ if (resid) { union { - __be32 word; + u32 word; char byte[4]; } last; unsigned char *bp; int i; if (dir == T4_MEMORY_READ) { - last.word = (__force __be32) t4_read_reg(adap, - mem_base + offset); + last.word = le32_to_cpu( + (__force __le32)t4_read_reg(adap, + mem_base + offset)); for (bp = (unsigned char *)buf, i = resid; i < 4; i++) bp[i] = last.byte[i]; } else { @@ -584,7 +618,7 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, for (i = resid; i < 4; i++) last.byte[i] = 0; t4_write_reg(adap, mem_base + offset, - (__force u32) last.word); + (__force u32)cpu_to_le32(last.word)); } } @@ -1086,7 +1120,7 @@ int t4_prep_fw(struct adapter *adap, struct fw_info *fw_info, } /* Installed successfully, update the cached header too. */ - memcpy(card_fw, fs_fw, sizeof(*card_fw)); + *card_fw = *fs_fw; card_fw_usable = 1; *reset = 0; /* already reset as part of load_fw */ } @@ -4425,6 +4459,59 @@ int cxgb4_t4_bar2_sge_qregs(struct adapter *adapter, } /** + * t4_init_devlog_params - initialize adapter->params.devlog + * @adap: the adapter + * + * Initialize various fields of the adapter's Firmware Device Log + * Parameters structure. + */ +int t4_init_devlog_params(struct adapter *adap) +{ + struct devlog_params *dparams = &adap->params.devlog; + u32 pf_dparams; + unsigned int devlog_meminfo; + struct fw_devlog_cmd devlog_cmd; + int ret; + + /* If we're dealing with newer firmware, the Device Log Paramerters + * are stored in a designated register which allows us to access the + * Device Log even if we can't talk to the firmware. + */ + pf_dparams = + t4_read_reg(adap, PCIE_FW_REG(PCIE_FW_PF_A, PCIE_FW_PF_DEVLOG)); + if (pf_dparams) { + unsigned int nentries, nentries128; + + dparams->memtype = PCIE_FW_PF_DEVLOG_MEMTYPE_G(pf_dparams); + dparams->start = PCIE_FW_PF_DEVLOG_ADDR16_G(pf_dparams) << 4; + + nentries128 = PCIE_FW_PF_DEVLOG_NENTRIES128_G(pf_dparams); + nentries = (nentries128 + 1) * 128; + dparams->size = nentries * sizeof(struct fw_devlog_e); + + return 0; + } + + /* Otherwise, ask the firmware for it's Device Log Parameters. + */ + memset(&devlog_cmd, 0, sizeof(devlog_cmd)); + devlog_cmd.op_to_write = htonl(FW_CMD_OP_V(FW_DEVLOG_CMD) | + FW_CMD_REQUEST_F | FW_CMD_READ_F); + devlog_cmd.retval_len16 = htonl(FW_LEN16(devlog_cmd)); + ret = t4_wr_mbox(adap, adap->mbox, &devlog_cmd, sizeof(devlog_cmd), + &devlog_cmd); + if (ret) + return ret; + + devlog_meminfo = ntohl(devlog_cmd.memtype_devlog_memaddr16_devlog); + dparams->memtype = FW_DEVLOG_CMD_MEMTYPE_DEVLOG_G(devlog_meminfo); + dparams->start = FW_DEVLOG_CMD_MEMADDR16_DEVLOG_G(devlog_meminfo) << 4; + dparams->size = ntohl(devlog_cmd.memsize_devlog); + + return 0; +} + +/** * t4_init_sge_params - initialize adap->params.sge * @adapter: the adapter * diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index 231a725..326674b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -63,6 +63,8 @@ #define MC_BIST_STATUS_REG(reg_addr, idx) ((reg_addr) + (idx) * 4) #define EDC_BIST_STATUS_REG(reg_addr, idx) ((reg_addr) + (idx) * 4) +#define PCIE_FW_REG(reg_addr, idx) ((reg_addr) + (idx) * 4) + #define SGE_PF_KDOORBELL_A 0x0 #define QID_S 15 @@ -707,6 +709,7 @@ #define PFNUM_V(x) ((x) << PFNUM_S) #define PCIE_FW_A 0x30b8 +#define PCIE_FW_PF_A 0x30bc #define PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS_A 0x5908 diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h index 9b353a8..a4a19e0 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h @@ -101,7 +101,7 @@ enum fw_wr_opcodes { FW_RI_BIND_MW_WR = 0x18, FW_RI_FR_NSMR_WR = 0x19, FW_RI_INV_LSTAG_WR = 0x1a, - FW_LASTC2E_WR = 0x40 + FW_LASTC2E_WR = 0x70 }; struct fw_wr_hdr { @@ -993,6 +993,7 @@ enum fw_memtype_cf { FW_MEMTYPE_CF_EXTMEM = 0x2, FW_MEMTYPE_CF_FLASH = 0x4, FW_MEMTYPE_CF_INTERNAL = 0x5, + FW_MEMTYPE_CF_EXTMEM1 = 0x6, }; struct fw_caps_config_cmd { @@ -1035,6 +1036,7 @@ enum fw_params_mnem { FW_PARAMS_MNEM_PFVF = 2, /* function params */ FW_PARAMS_MNEM_REG = 3, /* limited register access */ FW_PARAMS_MNEM_DMAQ = 4, /* dma queue params */ + FW_PARAMS_MNEM_CHNET = 5, /* chnet params */ FW_PARAMS_MNEM_LAST }; @@ -3102,7 +3104,8 @@ enum fw_devlog_facility { FW_DEVLOG_FACILITY_FCOE = 0x2E, FW_DEVLOG_FACILITY_FOISCSI = 0x30, FW_DEVLOG_FACILITY_FOFCOE = 0x32, - FW_DEVLOG_FACILITY_MAX = 0x32, + FW_DEVLOG_FACILITY_CHNET = 0x34, + FW_DEVLOG_FACILITY_MAX = 0x34, }; /* log message format */ @@ -3139,4 +3142,36 @@ struct fw_devlog_cmd { (((x) >> FW_DEVLOG_CMD_MEMADDR16_DEVLOG_S) & \ FW_DEVLOG_CMD_MEMADDR16_DEVLOG_M) +/* P C I E F W P F 7 R E G I S T E R */ + +/* PF7 stores the Firmware Device Log parameters which allows Host Drivers to + * access the "devlog" which needing to contact firmware. The encoding is + * mostly the same as that returned by the DEVLOG command except for the size + * which is encoded as the number of entries in multiples-1 of 128 here rather + * than the memory size as is done in the DEVLOG command. Thus, 0 means 128 + * and 15 means 2048. This of course in turn constrains the allowed values + * for the devlog size ... + */ +#define PCIE_FW_PF_DEVLOG 7 + +#define PCIE_FW_PF_DEVLOG_NENTRIES128_S 28 +#define PCIE_FW_PF_DEVLOG_NENTRIES128_M 0xf +#define PCIE_FW_PF_DEVLOG_NENTRIES128_V(x) \ + ((x) << PCIE_FW_PF_DEVLOG_NENTRIES128_S) +#define PCIE_FW_PF_DEVLOG_NENTRIES128_G(x) \ + (((x) >> PCIE_FW_PF_DEVLOG_NENTRIES128_S) & \ + PCIE_FW_PF_DEVLOG_NENTRIES128_M) + +#define PCIE_FW_PF_DEVLOG_ADDR16_S 4 +#define PCIE_FW_PF_DEVLOG_ADDR16_M 0xffffff +#define PCIE_FW_PF_DEVLOG_ADDR16_V(x) ((x) << PCIE_FW_PF_DEVLOG_ADDR16_S) +#define PCIE_FW_PF_DEVLOG_ADDR16_G(x) \ + (((x) >> PCIE_FW_PF_DEVLOG_ADDR16_S) & PCIE_FW_PF_DEVLOG_ADDR16_M) + +#define PCIE_FW_PF_DEVLOG_MEMTYPE_S 0 +#define PCIE_FW_PF_DEVLOG_MEMTYPE_M 0xf +#define PCIE_FW_PF_DEVLOG_MEMTYPE_V(x) ((x) << PCIE_FW_PF_DEVLOG_MEMTYPE_S) +#define PCIE_FW_PF_DEVLOG_MEMTYPE_G(x) \ + (((x) >> PCIE_FW_PF_DEVLOG_MEMTYPE_S) & PCIE_FW_PF_DEVLOG_MEMTYPE_M) + #endif /* _T4FW_INTERFACE_H_ */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_version.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_version.h index e2bd3f7..b9d1cba 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_version.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_version.h @@ -36,13 +36,13 @@ #define __T4FW_VERSION_H__ #define T4FW_VERSION_MAJOR 0x01 -#define T4FW_VERSION_MINOR 0x0C -#define T4FW_VERSION_MICRO 0x19 +#define T4FW_VERSION_MINOR 0x0D +#define T4FW_VERSION_MICRO 0x20 #define T4FW_VERSION_BUILD 0x00 #define T5FW_VERSION_MAJOR 0x01 -#define T5FW_VERSION_MINOR 0x0C -#define T5FW_VERSION_MICRO 0x19 +#define T5FW_VERSION_MINOR 0x0D +#define T5FW_VERSION_MICRO 0x20 #define T5FW_VERSION_BUILD 0x00 #endif diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index 0545f0d..e0d7110 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -1004,7 +1004,7 @@ static inline void ring_tx_db(struct adapter *adapter, struct sge_txq *tq, ? (tq->pidx - 1) : (tq->size - 1)); __be64 *src = (__be64 *)&tq->desc[index]; - __be64 __iomem *dst = (__be64 *)(tq->bar2_addr + + __be64 __iomem *dst = (__be64 __iomem *)(tq->bar2_addr + SGE_UDB_WCDOORBELL); unsigned int count = EQ_UNIT / sizeof(__be64); @@ -1018,7 +1018,11 @@ static inline void ring_tx_db(struct adapter *adapter, struct sge_txq *tq, * DMA. */ while (count) { - writeq(*src, dst); + /* the (__force u64) is because the compiler + * doesn't understand the endian swizzling + * going on + */ + writeq((__force u64)*src, dst); src++; dst++; count--; @@ -1252,8 +1256,8 @@ int t4vf_eth_xmit(struct sk_buff *skb, struct net_device *dev) BUG_ON(DIV_ROUND_UP(ETHTXQ_MAX_HDR, TXD_PER_EQ_UNIT) > 1); wr = (void *)&txq->q.desc[txq->q.pidx]; wr->equiq_to_len16 = cpu_to_be32(wr_mid); - wr->r3[0] = cpu_to_be64(0); - wr->r3[1] = cpu_to_be64(0); + wr->r3[0] = cpu_to_be32(0); + wr->r3[1] = cpu_to_be32(0); skb_copy_from_linear_data(skb, (void *)wr->ethmacdst, fw_hdr_copy_len); end = (u64 *)wr + flits; diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c index 1b5506d..280b4a2 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c @@ -210,10 +210,10 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size, if (rpl) { /* request bit in high-order BE word */ - WARN_ON((be32_to_cpu(*(const u32 *)cmd) + WARN_ON((be32_to_cpu(*(const __be32 *)cmd) & FW_CMD_REQUEST_F) == 0); get_mbox_rpl(adapter, rpl, size, mbox_data); - WARN_ON((be32_to_cpu(*(u32 *)rpl) + WARN_ON((be32_to_cpu(*(__be32 *)rpl) & FW_CMD_REQUEST_F) != 0); } t4_write_reg(adapter, mbox_ctl, @@ -484,7 +484,7 @@ int t4_bar2_sge_qregs(struct adapter *adapter, * o The BAR2 Queue ID. * o The BAR2 Queue ID Offset into the BAR2 page. */ - bar2_page_offset = ((qid >> qpp_shift) << page_shift); + bar2_page_offset = ((u64)(qid >> qpp_shift) << page_shift); bar2_qid = qid & qpp_mask; bar2_qid_offset = bar2_qid * SGE_UDB_SIZE; diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 9cbe038..a5179bf 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -272,8 +272,8 @@ static irqreturn_t enic_isr_legacy(int irq, void *data) } if (ENIC_TEST_INTR(pba, notify_intr)) { - vnic_intr_return_all_credits(&enic->intr[notify_intr]); enic_notify_check(enic); + vnic_intr_return_all_credits(&enic->intr[notify_intr]); } if (ENIC_TEST_INTR(pba, err_intr)) { @@ -346,8 +346,8 @@ static irqreturn_t enic_isr_msix_notify(int irq, void *data) struct enic *enic = data; unsigned int intr = enic_msix_notify_intr(enic); - vnic_intr_return_all_credits(&enic->intr[intr]); enic_notify_check(enic); + vnic_intr_return_all_credits(&enic->intr[intr]); return IRQ_HANDLED; } diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c index 3b42556..ed41559 100644 --- a/drivers/net/ethernet/dec/tulip/tulip_core.c +++ b/drivers/net/ethernet/dec/tulip/tulip_core.c @@ -589,7 +589,7 @@ static void tulip_tx_timeout(struct net_device *dev) (unsigned int)tp->rx_ring[i].buffer1, (unsigned int)tp->rx_ring[i].buffer2, buf[0], buf[1], buf[2]); - for (j = 0; buf[j] != 0xee && j < 1600; j++) + for (j = 0; ((j < 1600) && buf[j] != 0xee); j++) if (j < 100) pr_cont(" %02x", buf[j]); pr_cont(" j=%d\n", j); diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 27de37a..27b9fe9 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -354,6 +354,7 @@ struct be_vf_cfg { u16 vlan_tag; u32 tx_rate; u32 plink_tracking; + u32 privileges; }; enum vf_state { @@ -423,6 +424,7 @@ struct be_adapter { u8 __iomem *csr; /* CSR BAR used only for BE2/3 */ u8 __iomem *db; /* Door Bell */ + u8 __iomem *pcicfg; /* On SH,BEx only. Shadow of PCI config space */ struct mutex mbox_lock; /* For serializing mbox cmds to BE card */ struct be_dma_mem mbox_mem; diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index 36916cf..7f05f30 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -1902,15 +1902,11 @@ int be_cmd_modify_eqd(struct be_adapter *adapter, struct be_set_eqd *set_eqd, { int num_eqs, i = 0; - if (lancer_chip(adapter) && num > 8) { - while (num) { - num_eqs = min(num, 8); - __be_cmd_modify_eqd(adapter, &set_eqd[i], num_eqs); - i += num_eqs; - num -= num_eqs; - } - } else { - __be_cmd_modify_eqd(adapter, set_eqd, num); + while (num) { + num_eqs = min(num, 8); + __be_cmd_modify_eqd(adapter, &set_eqd[i], num_eqs); + i += num_eqs; + num -= num_eqs; } return 0; @@ -1918,7 +1914,7 @@ int be_cmd_modify_eqd(struct be_adapter *adapter, struct be_set_eqd *set_eqd, /* Uses sycnhronous mcc */ int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array, - u32 num) + u32 num, u32 domain) { struct be_mcc_wrb *wrb; struct be_cmd_req_vlan_config *req; @@ -1936,6 +1932,7 @@ int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array, be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_NTWK_VLAN_CONFIG, sizeof(*req), wrb, NULL); + req->hdr.domain = domain; req->interface_id = if_id; req->untagged = BE_IF_FLAGS_UNTAGGED & be_if_cap_flags(adapter) ? 1 : 0; diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index db761e8e..a7634a3 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -2256,7 +2256,7 @@ int lancer_cmd_get_pport_stats(struct be_adapter *adapter, int be_cmd_get_fw_ver(struct be_adapter *adapter); int be_cmd_modify_eqd(struct be_adapter *adapter, struct be_set_eqd *, int num); int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array, - u32 num); + u32 num, u32 domain); int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 status); int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc); int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc); diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 0a81685..e6b790f 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -1171,7 +1171,7 @@ static int be_vid_config(struct be_adapter *adapter) for_each_set_bit(i, adapter->vids, VLAN_N_VID) vids[num++] = cpu_to_le16(i); - status = be_cmd_vlan_config(adapter, adapter->if_handle, vids, num); + status = be_cmd_vlan_config(adapter, adapter->if_handle, vids, num, 0); if (status) { dev_err(dev, "Setting HW VLAN filtering failed\n"); /* Set to VLAN promisc mode as setting VLAN filter failed */ @@ -1380,11 +1380,67 @@ static int be_get_vf_config(struct net_device *netdev, int vf, return 0; } +static int be_set_vf_tvt(struct be_adapter *adapter, int vf, u16 vlan) +{ + struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf]; + u16 vids[BE_NUM_VLANS_SUPPORTED]; + int vf_if_id = vf_cfg->if_handle; + int status; + + /* Enable Transparent VLAN Tagging */ + status = be_cmd_set_hsw_config(adapter, vlan, vf + 1, vf_if_id, 0); + if (status) + return status; + + /* Clear pre-programmed VLAN filters on VF if any, if TVT is enabled */ + vids[0] = 0; + status = be_cmd_vlan_config(adapter, vf_if_id, vids, 1, vf + 1); + if (!status) + dev_info(&adapter->pdev->dev, + "Cleared guest VLANs on VF%d", vf); + + /* After TVT is enabled, disallow VFs to program VLAN filters */ + if (vf_cfg->privileges & BE_PRIV_FILTMGMT) { + status = be_cmd_set_fn_privileges(adapter, vf_cfg->privileges & + ~BE_PRIV_FILTMGMT, vf + 1); + if (!status) + vf_cfg->privileges &= ~BE_PRIV_FILTMGMT; + } + return 0; +} + +static int be_clear_vf_tvt(struct be_adapter *adapter, int vf) +{ + struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf]; + struct device *dev = &adapter->pdev->dev; + int status; + + /* Reset Transparent VLAN Tagging. */ + status = be_cmd_set_hsw_config(adapter, BE_RESET_VLAN_TAG_ID, vf + 1, + vf_cfg->if_handle, 0); + if (status) + return status; + + /* Allow VFs to program VLAN filtering */ + if (!(vf_cfg->privileges & BE_PRIV_FILTMGMT)) { + status = be_cmd_set_fn_privileges(adapter, vf_cfg->privileges | + BE_PRIV_FILTMGMT, vf + 1); + if (!status) { + vf_cfg->privileges |= BE_PRIV_FILTMGMT; + dev_info(dev, "VF%d: FILTMGMT priv enabled", vf); + } + } + + dev_info(dev, + "Disable/re-enable i/f in VM to clear Transparent VLAN tag"); + return 0; +} + static int be_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos) { struct be_adapter *adapter = netdev_priv(netdev); struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf]; - int status = 0; + int status; if (!sriov_enabled(adapter)) return -EPERM; @@ -1394,24 +1450,19 @@ static int be_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos) if (vlan || qos) { vlan |= qos << VLAN_PRIO_SHIFT; - if (vf_cfg->vlan_tag != vlan) - status = be_cmd_set_hsw_config(adapter, vlan, vf + 1, - vf_cfg->if_handle, 0); + status = be_set_vf_tvt(adapter, vf, vlan); } else { - /* Reset Transparent Vlan Tagging. */ - status = be_cmd_set_hsw_config(adapter, BE_RESET_VLAN_TAG_ID, - vf + 1, vf_cfg->if_handle, 0); + status = be_clear_vf_tvt(adapter, vf); } if (status) { dev_err(&adapter->pdev->dev, - "VLAN %d config on VF %d failed : %#x\n", vlan, - vf, status); + "VLAN %d config on VF %d failed : %#x\n", vlan, vf, + status); return be_cmd_status(status); } vf_cfg->vlan_tag = vlan; - return 0; } @@ -2772,14 +2823,12 @@ void be_detect_error(struct be_adapter *adapter) } } } else { - pci_read_config_dword(adapter->pdev, - PCICFG_UE_STATUS_LOW, &ue_lo); - pci_read_config_dword(adapter->pdev, - PCICFG_UE_STATUS_HIGH, &ue_hi); - pci_read_config_dword(adapter->pdev, - PCICFG_UE_STATUS_LOW_MASK, &ue_lo_mask); - pci_read_config_dword(adapter->pdev, - PCICFG_UE_STATUS_HI_MASK, &ue_hi_mask); + ue_lo = ioread32(adapter->pcicfg + PCICFG_UE_STATUS_LOW); + ue_hi = ioread32(adapter->pcicfg + PCICFG_UE_STATUS_HIGH); + ue_lo_mask = ioread32(adapter->pcicfg + + PCICFG_UE_STATUS_LOW_MASK); + ue_hi_mask = ioread32(adapter->pcicfg + + PCICFG_UE_STATUS_HI_MASK); ue_lo = (ue_lo & ~ue_lo_mask); ue_hi = (ue_hi & ~ue_hi_mask); @@ -3339,7 +3388,6 @@ static int be_if_create(struct be_adapter *adapter, u32 *if_handle, u32 cap_flags, u32 vf) { u32 en_flags; - int status; en_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST | BE_IF_FLAGS_MULTICAST | BE_IF_FLAGS_PASS_L3L4_ERRORS | @@ -3347,10 +3395,7 @@ static int be_if_create(struct be_adapter *adapter, u32 *if_handle, en_flags &= cap_flags; - status = be_cmd_if_create(adapter, cap_flags, en_flags, - if_handle, vf); - - return status; + return be_cmd_if_create(adapter, cap_flags, en_flags, if_handle, vf); } static int be_vfs_if_create(struct be_adapter *adapter) @@ -3368,8 +3413,13 @@ static int be_vfs_if_create(struct be_adapter *adapter) if (!BE3_chip(adapter)) { status = be_cmd_get_profile_config(adapter, &res, vf + 1); - if (!status) + if (!status) { cap_flags = res.if_cap_flags; + /* Prevent VFs from enabling VLAN promiscuous + * mode + */ + cap_flags &= ~BE_IF_FLAGS_VLAN_PROMISCUOUS; + } } status = be_if_create(adapter, &vf_cfg->if_handle, @@ -3403,7 +3453,6 @@ static int be_vf_setup(struct be_adapter *adapter) struct device *dev = &adapter->pdev->dev; struct be_vf_cfg *vf_cfg; int status, old_vfs, vf; - u32 privileges; old_vfs = pci_num_vf(adapter->pdev); @@ -3433,15 +3482,18 @@ static int be_vf_setup(struct be_adapter *adapter) for_all_vfs(adapter, vf_cfg, vf) { /* Allow VFs to programs MAC/VLAN filters */ - status = be_cmd_get_fn_privileges(adapter, &privileges, vf + 1); - if (!status && !(privileges & BE_PRIV_FILTMGMT)) { + status = be_cmd_get_fn_privileges(adapter, &vf_cfg->privileges, + vf + 1); + if (!status && !(vf_cfg->privileges & BE_PRIV_FILTMGMT)) { status = be_cmd_set_fn_privileges(adapter, - privileges | + vf_cfg->privileges | BE_PRIV_FILTMGMT, vf + 1); - if (!status) + if (!status) { + vf_cfg->privileges |= BE_PRIV_FILTMGMT; dev_info(dev, "VF%d has FILTMGMT privilege\n", vf); + } } /* Allow full available bandwidth */ @@ -4820,24 +4872,37 @@ static int be_roce_map_pci_bars(struct be_adapter *adapter) static int be_map_pci_bars(struct be_adapter *adapter) { + struct pci_dev *pdev = adapter->pdev; u8 __iomem *addr; if (BEx_chip(adapter) && be_physfn(adapter)) { - adapter->csr = pci_iomap(adapter->pdev, 2, 0); + adapter->csr = pci_iomap(pdev, 2, 0); if (!adapter->csr) return -ENOMEM; } - addr = pci_iomap(adapter->pdev, db_bar(adapter), 0); + addr = pci_iomap(pdev, db_bar(adapter), 0); if (!addr) goto pci_map_err; adapter->db = addr; + if (skyhawk_chip(adapter) || BEx_chip(adapter)) { + if (be_physfn(adapter)) { + /* PCICFG is the 2nd BAR in BE2 */ + addr = pci_iomap(pdev, BE2_chip(adapter) ? 1 : 0, 0); + if (!addr) + goto pci_map_err; + adapter->pcicfg = addr; + } else { + adapter->pcicfg = adapter->db + SRIOV_VF_PCICFG_OFFSET; + } + } + be_roce_map_pci_bars(adapter); return 0; pci_map_err: - dev_err(&adapter->pdev->dev, "Error in mapping PCI BARs\n"); + dev_err(&pdev->dev, "Error in mapping PCI BARs\n"); be_unmap_pci_bars(adapter); return -ENOMEM; } diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 9bb6220..f6a3a7a 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1189,13 +1189,12 @@ static void fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) { struct fec_enet_private *fep; - struct bufdesc *bdp, *bdp_t; + struct bufdesc *bdp; unsigned short status; struct sk_buff *skb; struct fec_enet_priv_tx_q *txq; struct netdev_queue *nq; int index = 0; - int i, bdnum; int entries_free; fep = netdev_priv(ndev); @@ -1216,29 +1215,18 @@ fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) if (bdp == txq->cur_tx) break; - bdp_t = bdp; - bdnum = 1; - index = fec_enet_get_bd_index(txq->tx_bd_base, bdp_t, fep); - skb = txq->tx_skbuff[index]; - while (!skb) { - bdp_t = fec_enet_get_nextdesc(bdp_t, fep, queue_id); - index = fec_enet_get_bd_index(txq->tx_bd_base, bdp_t, fep); - skb = txq->tx_skbuff[index]; - bdnum++; - } - if (skb_shinfo(skb)->nr_frags && - (status = bdp_t->cbd_sc) & BD_ENET_TX_READY) - break; + index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep); - for (i = 0; i < bdnum; i++) { - if (!IS_TSO_HEADER(txq, bdp->cbd_bufaddr)) - dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, - bdp->cbd_datlen, DMA_TO_DEVICE); - bdp->cbd_bufaddr = 0; - if (i < bdnum - 1) - bdp = fec_enet_get_nextdesc(bdp, fep, queue_id); - } + skb = txq->tx_skbuff[index]; txq->tx_skbuff[index] = NULL; + if (!IS_TSO_HEADER(txq, bdp->cbd_bufaddr)) + dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, + bdp->cbd_datlen, DMA_TO_DEVICE); + bdp->cbd_bufaddr = 0; + if (!skb) { + bdp = fec_enet_get_nextdesc(bdp, fep, queue_id); + continue; + } /* Check for errors. */ if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | @@ -1479,8 +1467,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) vlan_packet_rcvd = true; - skb_copy_to_linear_data_offset(skb, VLAN_HLEN, - data, (2 * ETH_ALEN)); + memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2); skb_pull(skb, VLAN_HLEN); } @@ -1597,7 +1584,7 @@ fec_enet_interrupt(int irq, void *dev_id) writel(int_events, fep->hwp + FEC_IEVENT); fec_enet_collect_events(fep, int_events); - if (fep->work_tx || fep->work_rx) { + if ((fep->work_tx || fep->work_rx) && fep->link) { ret = IRQ_HANDLED; if (napi_schedule_prep(&fep->napi)) { @@ -1967,6 +1954,7 @@ static int fec_enet_mii_init(struct platform_device *pdev) struct fec_enet_private *fep = netdev_priv(ndev); struct device_node *node; int err = -ENXIO, i; + u32 mii_speed, holdtime; /* * The i.MX28 dual fec interfaces are not equal. @@ -2004,10 +1992,33 @@ static int fec_enet_mii_init(struct platform_device *pdev) * Reference Manual has an error on this, and gets fixed on i.MX6Q * document. */ - fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000); + mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000); if (fep->quirks & FEC_QUIRK_ENET_MAC) - fep->phy_speed--; - fep->phy_speed <<= 1; + mii_speed--; + if (mii_speed > 63) { + dev_err(&pdev->dev, + "fec clock (%lu) to fast to get right mii speed\n", + clk_get_rate(fep->clk_ipg)); + err = -EINVAL; + goto err_out; + } + + /* + * The i.MX28 and i.MX6 types have another filed in the MSCR (aka + * MII_SPEED) register that defines the MDIO output hold time. Earlier + * versions are RAZ there, so just ignore the difference and write the + * register always. + * The minimal hold time according to IEE802.3 (clause 22) is 10 ns. + * HOLDTIME + 1 is the number of clk cycles the fec is holding the + * output. + * The HOLDTIME bitfield takes values between 0 and 7 (inclusive). + * Given that ceil(clkrate / 5000000) <= 64, the calculation for + * holdtime cannot result in a value greater than 3. + */ + holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1; + + fep->phy_speed = mii_speed << 1 | holdtime << 8; + writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); fep->mii_bus = mdiobus_alloc(); @@ -3383,7 +3394,6 @@ fec_drv_remove(struct platform_device *pdev) regulator_disable(fep->reg_phy); if (fep->ptp_clock) ptp_clock_unregister(fep->ptp_clock); - fec_enet_clk_enable(ndev, false); of_node_put(fep->phy_node); free_netdev(ndev); diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 43df788..7bf3682 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -747,6 +747,18 @@ static int gfar_parse_group(struct device_node *np, return 0; } +static int gfar_of_group_count(struct device_node *np) +{ + struct device_node *child; + int num = 0; + + for_each_available_child_of_node(np, child) + if (!of_node_cmp(child->name, "queue-group")) + num++; + + return num; +} + static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) { const char *model; @@ -784,7 +796,7 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) num_rx_qs = 1; } else { /* MQ_MG_MODE */ /* get the actual number of supported groups */ - unsigned int num_grps = of_get_available_child_count(np); + unsigned int num_grps = gfar_of_group_count(np); if (num_grps == 0 || num_grps > MAXGROUPS) { dev_err(&ofdev->dev, "Invalid # of int groups(%d)\n", @@ -851,7 +863,10 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) /* Parse and initialize group specific information */ if (priv->mode == MQ_MG_MODE) { - for_each_child_of_node(np, child) { + for_each_available_child_of_node(np, child) { + if (of_node_cmp(child->name, "queue-group")) + continue; + err = gfar_parse_group(child, priv, model); if (err) goto err_grp_init; @@ -3162,8 +3177,8 @@ static void adjust_link(struct net_device *dev) struct phy_device *phydev = priv->phydev; if (unlikely(phydev->link != priv->oldlink || - phydev->duplex != priv->oldduplex || - phydev->speed != priv->oldspeed)) + (phydev->link && (phydev->duplex != priv->oldduplex || + phydev->speed != priv->oldspeed)))) gfar_update_link_state(priv); } diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 357e8b57..56b774d 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -3893,6 +3893,9 @@ static int ucc_geth_probe(struct platform_device* ofdev) ugeth->phy_interface = phy_interface; ugeth->max_speed = max_speed; + /* Carrier starts down, phylib will bring it up */ + netif_carrier_off(dev); + err = register_netdev(dev); if (err) { if (netif_msg_probe(ugeth)) diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index e8a1adb..c05e507 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -3262,6 +3262,139 @@ static void ehea_remove_device_sysfs(struct platform_device *dev) device_remove_file(&dev->dev, &dev_attr_remove_port); } +static int ehea_reboot_notifier(struct notifier_block *nb, + unsigned long action, void *unused) +{ + if (action == SYS_RESTART) { + pr_info("Reboot: freeing all eHEA resources\n"); + ibmebus_unregister_driver(&ehea_driver); + } + return NOTIFY_DONE; +} + +static struct notifier_block ehea_reboot_nb = { + .notifier_call = ehea_reboot_notifier, +}; + +static int ehea_mem_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + int ret = NOTIFY_BAD; + struct memory_notify *arg = data; + + mutex_lock(&dlpar_mem_lock); + + switch (action) { + case MEM_CANCEL_OFFLINE: + pr_info("memory offlining canceled"); + /* Fall through: re-add canceled memory block */ + + case MEM_ONLINE: + pr_info("memory is going online"); + set_bit(__EHEA_STOP_XFER, &ehea_driver_flags); + if (ehea_add_sect_bmap(arg->start_pfn, arg->nr_pages)) + goto out_unlock; + ehea_rereg_mrs(); + break; + + case MEM_GOING_OFFLINE: + pr_info("memory is going offline"); + set_bit(__EHEA_STOP_XFER, &ehea_driver_flags); + if (ehea_rem_sect_bmap(arg->start_pfn, arg->nr_pages)) + goto out_unlock; + ehea_rereg_mrs(); + break; + + default: + break; + } + + ehea_update_firmware_handles(); + ret = NOTIFY_OK; + +out_unlock: + mutex_unlock(&dlpar_mem_lock); + return ret; +} + +static struct notifier_block ehea_mem_nb = { + .notifier_call = ehea_mem_notifier, +}; + +static void ehea_crash_handler(void) +{ + int i; + + if (ehea_fw_handles.arr) + for (i = 0; i < ehea_fw_handles.num_entries; i++) + ehea_h_free_resource(ehea_fw_handles.arr[i].adh, + ehea_fw_handles.arr[i].fwh, + FORCE_FREE); + + if (ehea_bcmc_regs.arr) + for (i = 0; i < ehea_bcmc_regs.num_entries; i++) + ehea_h_reg_dereg_bcmc(ehea_bcmc_regs.arr[i].adh, + ehea_bcmc_regs.arr[i].port_id, + ehea_bcmc_regs.arr[i].reg_type, + ehea_bcmc_regs.arr[i].macaddr, + 0, H_DEREG_BCMC); +} + +static atomic_t ehea_memory_hooks_registered; + +/* Register memory hooks on probe of first adapter */ +static int ehea_register_memory_hooks(void) +{ + int ret = 0; + + if (atomic_inc_and_test(&ehea_memory_hooks_registered)) + return 0; + + ret = ehea_create_busmap(); + if (ret) { + pr_info("ehea_create_busmap failed\n"); + goto out; + } + + ret = register_reboot_notifier(&ehea_reboot_nb); + if (ret) { + pr_info("register_reboot_notifier failed\n"); + goto out; + } + + ret = register_memory_notifier(&ehea_mem_nb); + if (ret) { + pr_info("register_memory_notifier failed\n"); + goto out2; + } + + ret = crash_shutdown_register(ehea_crash_handler); + if (ret) { + pr_info("crash_shutdown_register failed\n"); + goto out3; + } + + return 0; + +out3: + unregister_memory_notifier(&ehea_mem_nb); +out2: + unregister_reboot_notifier(&ehea_reboot_nb); +out: + return ret; +} + +static void ehea_unregister_memory_hooks(void) +{ + if (atomic_read(&ehea_memory_hooks_registered)) + return; + + unregister_reboot_notifier(&ehea_reboot_nb); + if (crash_shutdown_unregister(ehea_crash_handler)) + pr_info("failed unregistering crash handler\n"); + unregister_memory_notifier(&ehea_mem_nb); +} + static int ehea_probe_adapter(struct platform_device *dev) { struct ehea_adapter *adapter; @@ -3269,6 +3402,10 @@ static int ehea_probe_adapter(struct platform_device *dev) int ret; int i; + ret = ehea_register_memory_hooks(); + if (ret) + return ret; + if (!dev || !dev->dev.of_node) { pr_err("Invalid ibmebus device probed\n"); return -EINVAL; @@ -3392,81 +3529,6 @@ static int ehea_remove(struct platform_device *dev) return 0; } -static void ehea_crash_handler(void) -{ - int i; - - if (ehea_fw_handles.arr) - for (i = 0; i < ehea_fw_handles.num_entries; i++) - ehea_h_free_resource(ehea_fw_handles.arr[i].adh, - ehea_fw_handles.arr[i].fwh, - FORCE_FREE); - - if (ehea_bcmc_regs.arr) - for (i = 0; i < ehea_bcmc_regs.num_entries; i++) - ehea_h_reg_dereg_bcmc(ehea_bcmc_regs.arr[i].adh, - ehea_bcmc_regs.arr[i].port_id, - ehea_bcmc_regs.arr[i].reg_type, - ehea_bcmc_regs.arr[i].macaddr, - 0, H_DEREG_BCMC); -} - -static int ehea_mem_notifier(struct notifier_block *nb, - unsigned long action, void *data) -{ - int ret = NOTIFY_BAD; - struct memory_notify *arg = data; - - mutex_lock(&dlpar_mem_lock); - - switch (action) { - case MEM_CANCEL_OFFLINE: - pr_info("memory offlining canceled"); - /* Readd canceled memory block */ - case MEM_ONLINE: - pr_info("memory is going online"); - set_bit(__EHEA_STOP_XFER, &ehea_driver_flags); - if (ehea_add_sect_bmap(arg->start_pfn, arg->nr_pages)) - goto out_unlock; - ehea_rereg_mrs(); - break; - case MEM_GOING_OFFLINE: - pr_info("memory is going offline"); - set_bit(__EHEA_STOP_XFER, &ehea_driver_flags); - if (ehea_rem_sect_bmap(arg->start_pfn, arg->nr_pages)) - goto out_unlock; - ehea_rereg_mrs(); - break; - default: - break; - } - - ehea_update_firmware_handles(); - ret = NOTIFY_OK; - -out_unlock: - mutex_unlock(&dlpar_mem_lock); - return ret; -} - -static struct notifier_block ehea_mem_nb = { - .notifier_call = ehea_mem_notifier, -}; - -static int ehea_reboot_notifier(struct notifier_block *nb, - unsigned long action, void *unused) -{ - if (action == SYS_RESTART) { - pr_info("Reboot: freeing all eHEA resources\n"); - ibmebus_unregister_driver(&ehea_driver); - } - return NOTIFY_DONE; -} - -static struct notifier_block ehea_reboot_nb = { - .notifier_call = ehea_reboot_notifier, -}; - static int check_module_parm(void) { int ret = 0; @@ -3520,26 +3582,10 @@ static int __init ehea_module_init(void) if (ret) goto out; - ret = ehea_create_busmap(); - if (ret) - goto out; - - ret = register_reboot_notifier(&ehea_reboot_nb); - if (ret) - pr_info("failed registering reboot notifier\n"); - - ret = register_memory_notifier(&ehea_mem_nb); - if (ret) - pr_info("failed registering memory remove notifier\n"); - - ret = crash_shutdown_register(ehea_crash_handler); - if (ret) - pr_info("failed registering crash handler\n"); - ret = ibmebus_register_driver(&ehea_driver); if (ret) { pr_err("failed registering eHEA device driver on ebus\n"); - goto out2; + goto out; } ret = driver_create_file(&ehea_driver.driver, @@ -3547,32 +3593,22 @@ static int __init ehea_module_init(void) if (ret) { pr_err("failed to register capabilities attribute, ret=%d\n", ret); - goto out3; + goto out2; } return ret; -out3: - ibmebus_unregister_driver(&ehea_driver); out2: - unregister_memory_notifier(&ehea_mem_nb); - unregister_reboot_notifier(&ehea_reboot_nb); - crash_shutdown_unregister(ehea_crash_handler); + ibmebus_unregister_driver(&ehea_driver); out: return ret; } static void __exit ehea_module_exit(void) { - int ret; - driver_remove_file(&ehea_driver.driver, &driver_attr_capabilities); ibmebus_unregister_driver(&ehea_driver); - unregister_reboot_notifier(&ehea_reboot_nb); - ret = crash_shutdown_unregister(ehea_crash_handler); - if (ret) - pr_info("failed unregistering crash handler\n"); - unregister_memory_notifier(&ehea_mem_nb); + ehea_unregister_memory_hooks(); kfree(ehea_fw_handles.arr); kfree(ehea_bcmc_regs.arr); ehea_destroy_busmap(); diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index 21978cc..cd7675a 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -1136,6 +1136,8 @@ restart_poll: ibmveth_replenish_task(adapter); if (frames_processed < budget) { + napi_complete(napi); + /* We think we are done - reenable interrupts, * then check once more to make sure we are done. */ @@ -1144,8 +1146,6 @@ restart_poll: BUG_ON(lpar_rc != H_SUCCESS); - napi_complete(napi); - if (ibmveth_rxq_pending_buffer(adapter) && napi_reschedule(napi)) { lpar_rc = h_vio_signal(adapter->vdev->unit_address, @@ -1327,6 +1327,28 @@ static unsigned long ibmveth_get_desired_dma(struct vio_dev *vdev) return ret; } +static int ibmveth_set_mac_addr(struct net_device *dev, void *p) +{ + struct ibmveth_adapter *adapter = netdev_priv(dev); + struct sockaddr *addr = p; + u64 mac_address; + int rc; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + mac_address = ibmveth_encode_mac_addr(addr->sa_data); + rc = h_change_logical_lan_mac(adapter->vdev->unit_address, mac_address); + if (rc) { + netdev_err(adapter->netdev, "h_change_logical_lan_mac failed with rc=%d\n", rc); + return rc; + } + + ether_addr_copy(dev->dev_addr, addr->sa_data); + + return 0; +} + static const struct net_device_ops ibmveth_netdev_ops = { .ndo_open = ibmveth_open, .ndo_stop = ibmveth_close, @@ -1337,7 +1359,7 @@ static const struct net_device_ops ibmveth_netdev_ops = { .ndo_fix_features = ibmveth_fix_features, .ndo_set_features = ibmveth_set_features, .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, + .ndo_set_mac_address = ibmveth_set_mac_addr, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ibmveth_poll_controller, #endif diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 11a9ffe..6aea65d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -868,8 +868,9 @@ i40e_status i40e_pf_reset(struct i40e_hw *hw) * The grst delay value is in 100ms units, and we'll wait a * couple counts longer to be sure we don't just miss the end. */ - grst_del = rd32(hw, I40E_GLGEN_RSTCTL) & I40E_GLGEN_RSTCTL_GRSTDEL_MASK - >> I40E_GLGEN_RSTCTL_GRSTDEL_SHIFT; + grst_del = (rd32(hw, I40E_GLGEN_RSTCTL) & + I40E_GLGEN_RSTCTL_GRSTDEL_MASK) >> + I40E_GLGEN_RSTCTL_GRSTDEL_SHIFT; for (cnt = 0; cnt < grst_del + 2; cnt++) { reg = rd32(hw, I40E_GLGEN_RSTAT); if (!(reg & I40E_GLGEN_RSTAT_DEVSTATE_MASK)) @@ -2846,7 +2847,7 @@ i40e_status i40e_aq_add_udp_tunnel(struct i40e_hw *hw, status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); - if (!status) + if (!status && filter_index) *filter_index = resp->index; return status; diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c index 183dcb6..a11c70c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c @@ -40,7 +40,7 @@ static void i40e_get_pfc_delay(struct i40e_hw *hw, u16 *delay) u32 val; val = rd32(hw, I40E_PRTDCB_GENC); - *delay = (u16)(val & I40E_PRTDCB_GENC_PFCLDA_MASK >> + *delay = (u16)((val & I40E_PRTDCB_GENC_PFCLDA_MASK) >> I40E_PRTDCB_GENC_PFCLDA_SHIFT); } diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index 61236f9..c17ee77 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -989,8 +989,10 @@ static ssize_t i40e_dbg_command_write(struct file *filp, if (!cmd_buf) return count; bytes_not_copied = copy_from_user(cmd_buf, buffer, count); - if (bytes_not_copied < 0) + if (bytes_not_copied < 0) { + kfree(cmd_buf); return bytes_not_copied; + } if (bytes_not_copied > 0) count -= bytes_not_copied; cmd_buf[count] = '\0'; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index cbe281b..dadda3c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1512,7 +1512,12 @@ static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi, vsi->tc_config.numtc = numtc; vsi->tc_config.enabled_tc = enabled_tc ? enabled_tc : 1; /* Number of queues per enabled TC */ - num_tc_qps = vsi->alloc_queue_pairs/numtc; + /* In MFP case we can have a much lower count of MSIx + * vectors available and so we need to lower the used + * q count. + */ + qcount = min_t(int, vsi->alloc_queue_pairs, pf->num_lan_msix); + num_tc_qps = qcount / numtc; num_tc_qps = min_t(int, num_tc_qps, I40E_MAX_QUEUES_PER_TC); /* Setup queue offset/count for all TCs for given VSI */ @@ -2684,8 +2689,15 @@ static void i40e_vsi_config_dcb_rings(struct i40e_vsi *vsi) u16 qoffset, qcount; int i, n; - if (!(vsi->back->flags & I40E_FLAG_DCB_ENABLED)) - return; + if (!(vsi->back->flags & I40E_FLAG_DCB_ENABLED)) { + /* Reset the TC information */ + for (i = 0; i < vsi->num_queue_pairs; i++) { + rx_ring = vsi->rx_rings[i]; + tx_ring = vsi->tx_rings[i]; + rx_ring->dcb_tc = 0; + tx_ring->dcb_tc = 0; + } + } for (n = 0; n < I40E_MAX_TRAFFIC_CLASS; n++) { if (!(vsi->tc_config.enabled_tc & (1 << n))) @@ -3830,6 +3842,12 @@ static void i40e_clear_interrupt_scheme(struct i40e_pf *pf) { int i; + i40e_stop_misc_vector(pf); + if (pf->flags & I40E_FLAG_MSIX_ENABLED) { + synchronize_irq(pf->msix_entries[0].vector); + free_irq(pf->msix_entries[0].vector, pf); + } + i40e_put_lump(pf->irq_pile, 0, I40E_PILE_VALID_BIT-1); for (i = 0; i < pf->num_alloc_vsi; i++) if (pf->vsi[i]) @@ -5254,8 +5272,14 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf, /* Wait for the PF's Tx queues to be disabled */ ret = i40e_pf_wait_txq_disabled(pf); - if (!ret) + if (ret) { + /* Schedule PF reset to recover */ + set_bit(__I40E_PF_RESET_REQUESTED, &pf->state); + i40e_service_event_schedule(pf); + } else { i40e_pf_unquiesce_all_vsi(pf); + } + exit: return ret; } @@ -5587,7 +5611,8 @@ static void i40e_check_hang_subtask(struct i40e_pf *pf) int i, v; /* If we're down or resetting, just bail */ - if (test_bit(__I40E_CONFIG_BUSY, &pf->state)) + if (test_bit(__I40E_DOWN, &pf->state) || + test_bit(__I40E_CONFIG_BUSY, &pf->state)) return; /* for each VSI/netdev @@ -9533,6 +9558,7 @@ static void i40e_remove(struct pci_dev *pdev) set_bit(__I40E_DOWN, &pf->state); del_timer_sync(&pf->service_timer); cancel_work_sync(&pf->service_task); + i40e_fdir_teardown(pf); if (pf->flags & I40E_FLAG_SRIOV_ENABLED) { i40e_free_vfs(pf); @@ -9559,12 +9585,6 @@ static void i40e_remove(struct pci_dev *pdev) if (pf->vsi[pf->lan_vsi]) i40e_vsi_release(pf->vsi[pf->lan_vsi]); - i40e_stop_misc_vector(pf); - if (pf->flags & I40E_FLAG_MSIX_ENABLED) { - synchronize_irq(pf->msix_entries[0].vector); - free_irq(pf->msix_entries[0].vector, pf); - } - /* shutdown and destroy the HMC */ if (pf->hw.hmc.hmc_obj) { ret_code = i40e_shutdown_lan_hmc(&pf->hw); @@ -9718,6 +9738,8 @@ static void i40e_shutdown(struct pci_dev *pdev) wr32(hw, I40E_PFPM_APM, (pf->wol_en ? I40E_PFPM_APM_APME_MASK : 0)); wr32(hw, I40E_PFPM_WUFC, (pf->wol_en ? I40E_PFPM_WUFC_MAG_MASK : 0)); + i40e_clear_interrupt_scheme(pf); + if (system_state == SYSTEM_POWER_OFF) { pci_wake_from_d3(pdev, pf->wol_en); pci_set_power_state(pdev, PCI_D3hot); diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index 3e70f2e..5defe0d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -679,9 +679,11 @@ static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw, { i40e_status status; enum i40e_nvmupd_cmd upd_cmd; + bool retry_attempt = false; upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno); +retry: switch (upd_cmd) { case I40E_NVMUPD_WRITE_CON: status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); @@ -725,6 +727,39 @@ static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw, *errno = -ESRCH; break; } + + /* In some circumstances, a multi-write transaction takes longer + * than the default 3 minute timeout on the write semaphore. If + * the write failed with an EBUSY status, this is likely the problem, + * so here we try to reacquire the semaphore then retry the write. + * We only do one retry, then give up. + */ + if (status && (hw->aq.asq_last_status == I40E_AQ_RC_EBUSY) && + !retry_attempt) { + i40e_status old_status = status; + u32 old_asq_status = hw->aq.asq_last_status; + u32 gtime; + + gtime = rd32(hw, I40E_GLVFGEN_TIMER); + if (gtime >= hw->nvm.hw_semaphore_timeout) { + i40e_debug(hw, I40E_DEBUG_ALL, + "NVMUPD: write semaphore expired (%d >= %lld), retrying\n", + gtime, hw->nvm.hw_semaphore_timeout); + i40e_release_nvm(hw); + status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); + if (status) { + i40e_debug(hw, I40E_DEBUG_ALL, + "NVMUPD: write semaphore reacquire failed aq_err = %d\n", + hw->aq.asq_last_status); + status = old_status; + hw->aq.asq_last_status = old_asq_status; + } else { + retry_attempt = true; + goto retry; + } + } + } + return status; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 2206d2d..bbf1b12 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -586,6 +586,20 @@ void i40e_free_tx_resources(struct i40e_ring *tx_ring) } /** + * i40e_get_head - Retrieve head from head writeback + * @tx_ring: tx ring to fetch head of + * + * Returns value of Tx ring head based on value stored + * in head write-back location + **/ +static inline u32 i40e_get_head(struct i40e_ring *tx_ring) +{ + void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count; + + return le32_to_cpu(*(volatile __le32 *)head); +} + +/** * i40e_get_tx_pending - how many tx descriptors not processed * @tx_ring: the ring of descriptors * @@ -594,10 +608,16 @@ void i40e_free_tx_resources(struct i40e_ring *tx_ring) **/ static u32 i40e_get_tx_pending(struct i40e_ring *ring) { - u32 ntu = ((ring->next_to_clean <= ring->next_to_use) - ? ring->next_to_use - : ring->next_to_use + ring->count); - return ntu - ring->next_to_clean; + u32 head, tail; + + head = i40e_get_head(ring); + tail = readl(ring->tail); + + if (head != tail) + return (head < tail) ? + tail - head : (tail + ring->count - head); + + return 0; } /** @@ -606,6 +626,8 @@ static u32 i40e_get_tx_pending(struct i40e_ring *ring) **/ static bool i40e_check_tx_hang(struct i40e_ring *tx_ring) { + u32 tx_done = tx_ring->stats.packets; + u32 tx_done_old = tx_ring->tx_stats.tx_done_old; u32 tx_pending = i40e_get_tx_pending(tx_ring); struct i40e_pf *pf = tx_ring->vsi->back; bool ret = false; @@ -623,41 +645,25 @@ static bool i40e_check_tx_hang(struct i40e_ring *tx_ring) * run the check_tx_hang logic with a transmit completion * pending but without time to complete it yet. */ - if ((tx_ring->tx_stats.tx_done_old == tx_ring->stats.packets) && - (tx_pending >= I40E_MIN_DESC_PENDING)) { + if ((tx_done_old == tx_done) && tx_pending) { /* make sure it is true for two checks in a row */ ret = test_and_set_bit(__I40E_HANG_CHECK_ARMED, &tx_ring->state); - } else if ((tx_ring->tx_stats.tx_done_old == tx_ring->stats.packets) && - (tx_pending < I40E_MIN_DESC_PENDING) && - (tx_pending > 0)) { + } else if (tx_done_old == tx_done && + (tx_pending < I40E_MIN_DESC_PENDING) && (tx_pending > 0)) { if (I40E_DEBUG_FLOW & pf->hw.debug_mask) dev_info(tx_ring->dev, "HW needs some more descs to do a cacheline flush. tx_pending %d, queue %d", tx_pending, tx_ring->queue_index); pf->tx_sluggish_count++; } else { /* update completed stats and disarm the hang check */ - tx_ring->tx_stats.tx_done_old = tx_ring->stats.packets; + tx_ring->tx_stats.tx_done_old = tx_done; clear_bit(__I40E_HANG_CHECK_ARMED, &tx_ring->state); } return ret; } -/** - * i40e_get_head - Retrieve head from head writeback - * @tx_ring: tx ring to fetch head of - * - * Returns value of Tx ring head based on value stored - * in head write-back location - **/ -static inline u32 i40e_get_head(struct i40e_ring *tx_ring) -{ - void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count; - - return le32_to_cpu(*(volatile __le32 *)head); -} - #define WB_STRIDE 0x3 /** @@ -2140,6 +2146,67 @@ static int i40e_maybe_stop_tx(struct i40e_ring *tx_ring, int size) } /** + * i40e_chk_linearize - Check if there are more than 8 fragments per packet + * @skb: send buffer + * @tx_flags: collected send information + * @hdr_len: size of the packet header + * + * Note: Our HW can't scatter-gather more than 8 fragments to build + * a packet on the wire and so we need to figure out the cases where we + * need to linearize the skb. + **/ +static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags, + const u8 hdr_len) +{ + struct skb_frag_struct *frag; + bool linearize = false; + unsigned int size = 0; + u16 num_frags; + u16 gso_segs; + + num_frags = skb_shinfo(skb)->nr_frags; + gso_segs = skb_shinfo(skb)->gso_segs; + + if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) { + u16 j = 1; + + if (num_frags < (I40E_MAX_BUFFER_TXD)) + goto linearize_chk_done; + /* try the simple math, if we have too many frags per segment */ + if (DIV_ROUND_UP((num_frags + gso_segs), gso_segs) > + I40E_MAX_BUFFER_TXD) { + linearize = true; + goto linearize_chk_done; + } + frag = &skb_shinfo(skb)->frags[0]; + size = hdr_len; + /* we might still have more fragments per segment */ + do { + size += skb_frag_size(frag); + frag++; j++; + if (j == I40E_MAX_BUFFER_TXD) { + if (size < skb_shinfo(skb)->gso_size) { + linearize = true; + break; + } + j = 1; + size -= skb_shinfo(skb)->gso_size; + if (size) + j++; + size += hdr_len; + } + num_frags--; + } while (num_frags); + } else { + if (num_frags >= I40E_MAX_BUFFER_TXD) + linearize = true; + } + +linearize_chk_done: + return linearize; +} + +/** * i40e_tx_map - Build the Tx descriptor * @tx_ring: ring to send buffer on * @skb: send buffer @@ -2396,6 +2463,10 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, if (tsyn) tx_flags |= I40E_TX_FLAGS_TSYN; + if (i40e_chk_linearize(skb, tx_flags, hdr_len)) + if (skb_linearize(skb)) + goto out_drop; + skb_tx_timestamp(skb); /* always enable CRC insertion offload */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index 18b0023..dff0bae 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -112,6 +112,7 @@ enum i40e_dyn_idx_t { #define i40e_rx_desc i40e_32byte_rx_desc +#define I40E_MAX_BUFFER_TXD 8 #define I40E_MIN_TX_LEN 17 #define I40E_MAX_DATA_PER_TXD 8192 diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 2900438..7088915 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -126,6 +126,20 @@ void i40evf_free_tx_resources(struct i40e_ring *tx_ring) } /** + * i40e_get_head - Retrieve head from head writeback + * @tx_ring: tx ring to fetch head of + * + * Returns value of Tx ring head based on value stored + * in head write-back location + **/ +static inline u32 i40e_get_head(struct i40e_ring *tx_ring) +{ + void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count; + + return le32_to_cpu(*(volatile __le32 *)head); +} + +/** * i40e_get_tx_pending - how many tx descriptors not processed * @tx_ring: the ring of descriptors * @@ -134,10 +148,16 @@ void i40evf_free_tx_resources(struct i40e_ring *tx_ring) **/ static u32 i40e_get_tx_pending(struct i40e_ring *ring) { - u32 ntu = ((ring->next_to_clean <= ring->next_to_use) - ? ring->next_to_use - : ring->next_to_use + ring->count); - return ntu - ring->next_to_clean; + u32 head, tail; + + head = i40e_get_head(ring); + tail = readl(ring->tail); + + if (head != tail) + return (head < tail) ? + tail - head : (tail + ring->count - head); + + return 0; } /** @@ -146,6 +166,8 @@ static u32 i40e_get_tx_pending(struct i40e_ring *ring) **/ static bool i40e_check_tx_hang(struct i40e_ring *tx_ring) { + u32 tx_done = tx_ring->stats.packets; + u32 tx_done_old = tx_ring->tx_stats.tx_done_old; u32 tx_pending = i40e_get_tx_pending(tx_ring); bool ret = false; @@ -162,36 +184,20 @@ static bool i40e_check_tx_hang(struct i40e_ring *tx_ring) * run the check_tx_hang logic with a transmit completion * pending but without time to complete it yet. */ - if ((tx_ring->tx_stats.tx_done_old == tx_ring->stats.packets) && - (tx_pending >= I40E_MIN_DESC_PENDING)) { + if ((tx_done_old == tx_done) && tx_pending) { /* make sure it is true for two checks in a row */ ret = test_and_set_bit(__I40E_HANG_CHECK_ARMED, &tx_ring->state); - } else if (!(tx_ring->tx_stats.tx_done_old == tx_ring->stats.packets) || - !(tx_pending < I40E_MIN_DESC_PENDING) || - !(tx_pending > 0)) { + } else if (tx_done_old == tx_done && + (tx_pending < I40E_MIN_DESC_PENDING) && (tx_pending > 0)) { /* update completed stats and disarm the hang check */ - tx_ring->tx_stats.tx_done_old = tx_ring->stats.packets; + tx_ring->tx_stats.tx_done_old = tx_done; clear_bit(__I40E_HANG_CHECK_ARMED, &tx_ring->state); } return ret; } -/** - * i40e_get_head - Retrieve head from head writeback - * @tx_ring: tx ring to fetch head of - * - * Returns value of Tx ring head based on value stored - * in head write-back location - **/ -static inline u32 i40e_get_head(struct i40e_ring *tx_ring) -{ - void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count; - - return le32_to_cpu(*(volatile __le32 *)head); -} - #define WB_STRIDE 0x3 /** @@ -1206,17 +1212,16 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb, if (err < 0) return err; - if (protocol == htons(ETH_P_IP)) { - iph = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb); + iph = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb); + ipv6h = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb); + + if (iph->version == 4) { tcph = skb->encapsulation ? inner_tcp_hdr(skb) : tcp_hdr(skb); iph->tot_len = 0; iph->check = 0; tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, 0, IPPROTO_TCP, 0); - } else if (skb_is_gso_v6(skb)) { - - ipv6h = skb->encapsulation ? inner_ipv6_hdr(skb) - : ipv6_hdr(skb); + } else if (ipv6h->version == 6) { tcph = skb->encapsulation ? inner_tcp_hdr(skb) : tcp_hdr(skb); ipv6h->payload_len = 0; tcph->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, @@ -1274,13 +1279,9 @@ static void i40e_tx_enable_csum(struct sk_buff *skb, u32 tx_flags, I40E_TX_CTX_EXT_IP_IPV4_NO_CSUM; } } else if (tx_flags & I40E_TX_FLAGS_IPV6) { - if (tx_flags & I40E_TX_FLAGS_TSO) { - *cd_tunneling |= I40E_TX_CTX_EXT_IP_IPV6; + *cd_tunneling |= I40E_TX_CTX_EXT_IP_IPV6; + if (tx_flags & I40E_TX_FLAGS_TSO) ip_hdr(skb)->check = 0; - } else { - *cd_tunneling |= - I40E_TX_CTX_EXT_IP_IPV4_NO_CSUM; - } } /* Now set the ctx descriptor fields */ @@ -1290,6 +1291,11 @@ static void i40e_tx_enable_csum(struct sk_buff *skb, u32 tx_flags, ((skb_inner_network_offset(skb) - skb_transport_offset(skb)) >> 1) << I40E_TXD_CTX_QW0_NATLEN_SHIFT; + if (this_ip_hdr->version == 6) { + tx_flags &= ~I40E_TX_FLAGS_IPV4; + tx_flags |= I40E_TX_FLAGS_IPV6; + } + } else { network_hdr_len = skb_network_header_len(skb); @@ -1380,6 +1386,67 @@ static void i40e_create_tx_ctx(struct i40e_ring *tx_ring, context_desc->type_cmd_tso_mss = cpu_to_le64(cd_type_cmd_tso_mss); } + /** + * i40e_chk_linearize - Check if there are more than 8 fragments per packet + * @skb: send buffer + * @tx_flags: collected send information + * @hdr_len: size of the packet header + * + * Note: Our HW can't scatter-gather more than 8 fragments to build + * a packet on the wire and so we need to figure out the cases where we + * need to linearize the skb. + **/ +static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags, + const u8 hdr_len) +{ + struct skb_frag_struct *frag; + bool linearize = false; + unsigned int size = 0; + u16 num_frags; + u16 gso_segs; + + num_frags = skb_shinfo(skb)->nr_frags; + gso_segs = skb_shinfo(skb)->gso_segs; + + if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) { + u16 j = 1; + + if (num_frags < (I40E_MAX_BUFFER_TXD)) + goto linearize_chk_done; + /* try the simple math, if we have too many frags per segment */ + if (DIV_ROUND_UP((num_frags + gso_segs), gso_segs) > + I40E_MAX_BUFFER_TXD) { + linearize = true; + goto linearize_chk_done; + } + frag = &skb_shinfo(skb)->frags[0]; + size = hdr_len; + /* we might still have more fragments per segment */ + do { + size += skb_frag_size(frag); + frag++; j++; + if (j == I40E_MAX_BUFFER_TXD) { + if (size < skb_shinfo(skb)->gso_size) { + linearize = true; + break; + } + j = 1; + size -= skb_shinfo(skb)->gso_size; + if (size) + j++; + size += hdr_len; + } + num_frags--; + } while (num_frags); + } else { + if (num_frags >= I40E_MAX_BUFFER_TXD) + linearize = true; + } + +linearize_chk_done: + return linearize; +} + /** * i40e_tx_map - Build the Tx descriptor * @tx_ring: ring to send buffer on @@ -1654,6 +1721,10 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, else if (tso) tx_flags |= I40E_TX_FLAGS_TSO; + if (i40e_chk_linearize(skb, tx_flags, hdr_len)) + if (skb_linearize(skb)) + goto out_drop; + skb_tx_timestamp(skb); /* always enable CRC insertion offload */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 4e15903..c950a03 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -112,6 +112,7 @@ enum i40e_dyn_idx_t { #define i40e_rx_desc i40e_32byte_rx_desc +#define I40E_MAX_BUFFER_TXD 8 #define I40E_MIN_TX_LEN 17 #define I40E_MAX_DATA_PER_TXD 8192 diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 96208f1..2db6532 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -2658,16 +2658,11 @@ static int mvneta_stop(struct net_device *dev) static int mvneta_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct mvneta_port *pp = netdev_priv(dev); - int ret; if (!pp->phy_dev) return -ENOTSUPP; - ret = phy_mii_ioctl(pp->phy_dev, ifr, cmd); - if (!ret) - mvneta_adjust_link(dev); - - return ret; + return phy_mii_ioctl(pp->phy_dev, ifr, cmd); } /* Ethtool methods */ diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index a681d7c..546ca42 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -724,7 +724,8 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param, * on the host, we deprecate the error message for this * specific command/input_mod/opcode_mod/fw-status to be debug. */ - if (op == MLX4_CMD_SET_PORT && in_modifier == 1 && + if (op == MLX4_CMD_SET_PORT && + (in_modifier == 1 || in_modifier == 2) && op_modifier == 0 && context->fw_status == CMD_STAT_BAD_SIZE) mlx4_dbg(dev, "command 0x%x failed: fw status = 0x%x\n", op, context->fw_status); @@ -1993,7 +1994,6 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd, goto reset_slave; slave_state[slave].vhcr_dma = ((u64) param) << 48; priv->mfunc.master.slave_state[slave].cookie = 0; - mutex_init(&priv->mfunc.master.gen_eqe_mutex[slave]); break; case MLX4_COMM_CMD_VHCR1: if (slave_state[slave].last_cmd != MLX4_COMM_CMD_VHCR0) @@ -2225,6 +2225,7 @@ int mlx4_multi_func_init(struct mlx4_dev *dev) for (i = 0; i < dev->num_slaves; ++i) { s_state = &priv->mfunc.master.slave_state[i]; s_state->last_cmd = MLX4_COMM_CMD_RESET; + mutex_init(&priv->mfunc.master.gen_eqe_mutex[i]); for (j = 0; j < MLX4_EVENT_TYPES_NUM; ++j) s_state->event_eq[j].eqn = -1; __raw_writel((__force u32) 0, diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 2a210c4..3485acf 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -1698,8 +1698,6 @@ int mlx4_en_start_port(struct net_device *dev) /* Schedule multicast task to populate multicast list */ queue_work(mdev->workqueue, &priv->rx_mode_task); - mlx4_set_stats_bitmap(mdev->dev, &priv->stats_bitmap); - #ifdef CONFIG_MLX4_EN_VXLAN if (priv->mdev->dev->caps.tunnel_offload_mode == MLX4_TUNNEL_OFFLOAD_MODE_VXLAN) vxlan_get_rx_port(dev); @@ -2807,13 +2805,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, netif_carrier_off(dev); mlx4_en_set_default_moderation(priv); - err = register_netdev(dev); - if (err) { - en_err(priv, "Netdev registration failed for port %d\n", port); - goto out; - } - priv->registered = 1; - en_warn(priv, "Using %d TX rings\n", prof->tx_ring_num); en_warn(priv, "Using %d RX rings\n", prof->rx_ring_num); @@ -2853,6 +2844,16 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, queue_delayed_work(mdev->workqueue, &priv->service_task, SERVICE_TASK_DELAY); + mlx4_set_stats_bitmap(mdev->dev, &priv->stats_bitmap); + + err = register_netdev(dev); + if (err) { + en_err(priv, "Netdev registration failed for port %d\n", port); + goto out; + } + + priv->registered = 1; + return 0; out: diff --git a/drivers/net/ethernet/mellanox/mlx4/en_selftest.c b/drivers/net/ethernet/mellanox/mlx4/en_selftest.c index 2d8ee66..a61009f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_selftest.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_selftest.c @@ -81,12 +81,14 @@ static int mlx4_en_test_loopback(struct mlx4_en_priv *priv) { u32 loopback_ok = 0; int i; - + bool gro_enabled; priv->loopback_ok = 0; priv->validate_loopback = 1; + gro_enabled = priv->dev->features & NETIF_F_GRO; mlx4_en_update_loopback_state(priv->dev, priv->dev->features); + priv->dev->features &= ~NETIF_F_GRO; /* xmit */ if (mlx4_en_test_loopback_xmit(priv)) { @@ -108,6 +110,10 @@ static int mlx4_en_test_loopback(struct mlx4_en_priv *priv) mlx4_en_test_loopback_exit: priv->validate_loopback = 0; + + if (gro_enabled) + priv->dev->features |= NETIF_F_GRO; + mlx4_en_update_loopback_state(priv->dev, priv->dev->features); return !loopback_ok; } diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 264bc15..6e70ffe 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -153,12 +153,10 @@ void mlx4_gen_slave_eqe(struct work_struct *work) /* All active slaves need to receive the event */ if (slave == ALL_SLAVES) { - for (i = 0; i < dev->num_slaves; i++) { - if (i != dev->caps.function && - master->slave_state[i].active) - if (mlx4_GEN_EQE(dev, i, eqe)) - mlx4_warn(dev, "Failed to generate event for slave %d\n", - i); + for (i = 0; i <= dev->persist->num_vfs; i++) { + if (mlx4_GEN_EQE(dev, i, eqe)) + mlx4_warn(dev, "Failed to generate event for slave %d\n", + i); } } else { if (mlx4_GEN_EQE(dev, slave, eqe)) @@ -203,13 +201,11 @@ static void mlx4_slave_event(struct mlx4_dev *dev, int slave, struct mlx4_eqe *eqe) { struct mlx4_priv *priv = mlx4_priv(dev); - struct mlx4_slave_state *s_slave = - &priv->mfunc.master.slave_state[slave]; - if (!s_slave->active) { - /*mlx4_warn(dev, "Trying to pass event to inactive slave\n");*/ + if (slave < 0 || slave > dev->persist->num_vfs || + slave == dev->caps.function || + !priv->mfunc.master.slave_state[slave].active) return; - } slave_event(dev, slave, eqe); } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 2a8268e..ebbe244 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -453,7 +453,7 @@ struct mlx4_en_port_stats { unsigned long rx_chksum_none; unsigned long rx_chksum_complete; unsigned long tx_chksum_offload; -#define NUM_PORT_STATS 9 +#define NUM_PORT_STATS 10 }; struct mlx4_en_perf_stats { diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 2bb8553..eda29db 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -412,7 +412,6 @@ err_icm: EXPORT_SYMBOL_GPL(mlx4_qp_alloc); -#define MLX4_UPDATE_QP_SUPPORTED_ATTRS MLX4_UPDATE_QP_SMAC int mlx4_update_qp(struct mlx4_dev *dev, u32 qpn, enum mlx4_update_qp_attr attr, struct mlx4_update_qp_params *params) diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 486e3d2..6e413ac 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -713,7 +713,7 @@ static int update_vport_qp_param(struct mlx4_dev *dev, struct mlx4_vport_oper_state *vp_oper; struct mlx4_priv *priv; u32 qp_type; - int port; + int port, err = 0; port = (qpc->pri_path.sched_queue & 0x40) ? 2 : 1; priv = mlx4_priv(dev); @@ -738,7 +738,9 @@ static int update_vport_qp_param(struct mlx4_dev *dev, } else { struct mlx4_update_qp_params params = {.flags = 0}; - mlx4_update_qp(dev, qpn, MLX4_UPDATE_QP_VSD, ¶ms); + err = mlx4_update_qp(dev, qpn, MLX4_UPDATE_QP_VSD, ¶ms); + if (err) + goto out; } } @@ -773,7 +775,8 @@ static int update_vport_qp_param(struct mlx4_dev *dev, qpc->pri_path.feup |= MLX4_FSM_FORCE_ETH_SRC_MAC; qpc->pri_path.grh_mylmc = (0x80 & qpc->pri_path.grh_mylmc) + vp_oper->mac_idx; } - return 0; +out: + return err; } static int mpt_mask(struct mlx4_dev *dev) @@ -3092,6 +3095,12 @@ int mlx4_GEN_EQE(struct mlx4_dev *dev, int slave, struct mlx4_eqe *eqe) if (!priv->mfunc.master.slave_state) return -EINVAL; + /* check for slave valid, slave not PF, and slave active */ + if (slave < 0 || slave > dev->persist->num_vfs || + slave == dev->caps.function || + !priv->mfunc.master.slave_state[slave].active) + return 0; + event_eq = &priv->mfunc.master.slave_state[slave].event_eq[eqe->type]; /* Create the event only if the slave is registered */ diff --git a/drivers/net/ethernet/pasemi/pasemi_mac.c b/drivers/net/ethernet/pasemi/pasemi_mac.c index 44e8d7d..57a6e6c 100644 --- a/drivers/net/ethernet/pasemi/pasemi_mac.c +++ b/drivers/net/ethernet/pasemi/pasemi_mac.c @@ -1239,11 +1239,9 @@ static int pasemi_mac_open(struct net_device *dev) if (mac->phydev) phy_start(mac->phydev); - init_timer(&mac->tx->clean_timer); - mac->tx->clean_timer.function = pasemi_mac_tx_timer; - mac->tx->clean_timer.data = (unsigned long)mac->tx; - mac->tx->clean_timer.expires = jiffies+HZ; - add_timer(&mac->tx->clean_timer); + setup_timer(&mac->tx->clean_timer, pasemi_mac_tx_timer, + (unsigned long)mac->tx); + mod_timer(&mac->tx->clean_timer, jiffies + HZ); return 0; diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h index 6e426ae..0a5e204 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h @@ -354,7 +354,7 @@ struct cmd_desc_type0 { } __attribute__ ((aligned(64))); -/* Note: sizeof(rcv_desc) should always be a mutliple of 2 */ +/* Note: sizeof(rcv_desc) should always be a multiple of 2 */ struct rcv_desc { __le16 reference_handle; __le16 reserved; @@ -499,7 +499,7 @@ struct uni_data_desc{ #define NETXEN_IMAGE_START 0x43000 /* compressed image */ #define NETXEN_SECONDARY_START 0x200000 /* backup images */ #define NETXEN_PXE_START 0x3E0000 /* PXE boot rom */ -#define NETXEN_USER_START 0x3E8000 /* Firmare info */ +#define NETXEN_USER_START 0x3E8000 /* Firmware info */ #define NETXEN_FIXED_START 0x3F0000 /* backup of crbinit */ #define NETXEN_USER_START_OLD NETXEN_PXE_START /* very old flash */ diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index fa43176..f221126 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -314,7 +314,7 @@ struct qlcnic_fdt { #define QLCNIC_BRDCFG_START 0x4000 /* board config */ #define QLCNIC_BOOTLD_START 0x10000 /* bootld */ #define QLCNIC_IMAGE_START 0x43000 /* compressed image */ -#define QLCNIC_USER_START 0x3E8000 /* Firmare info */ +#define QLCNIC_USER_START 0x3E8000 /* Firmware info */ #define QLCNIC_FW_VERSION_OFFSET (QLCNIC_USER_START+0x408) #define QLCNIC_FW_SIZE_OFFSET (QLCNIC_USER_START+0x40c) diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index ad0020a..c70ab40 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -2561,7 +2561,7 @@ static int rtl_check_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw) int rc = -EINVAL; if (!rtl_fw_format_ok(tp, rtl_fw)) { - netif_err(tp, ifup, dev, "invalid firwmare\n"); + netif_err(tp, ifup, dev, "invalid firmware\n"); goto out; } @@ -5067,8 +5067,6 @@ static void rtl_hw_reset(struct rtl8169_private *tp) RTL_W8(ChipCmd, CmdReset); rtl_udelay_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100); - - netdev_reset_queue(tp->dev); } static void rtl_request_uncached_firmware(struct rtl8169_private *tp) @@ -7049,7 +7047,6 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, u32 status, len; u32 opts[2]; int frags; - bool stop_queue; if (unlikely(!TX_FRAGS_READY_FOR(tp, skb_shinfo(skb)->nr_frags))) { netif_err(tp, drv, dev, "BUG! Tx Ring full when queue awake!\n"); @@ -7090,8 +7087,6 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, txd->opts2 = cpu_to_le32(opts[1]); - netdev_sent_queue(dev, skb->len); - skb_tx_timestamp(skb); /* Force memory writes to complete before releasing descriptor */ @@ -7106,16 +7101,11 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, tp->cur_tx += frags + 1; - stop_queue = !TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS); + RTL_W8(TxPoll, NPQ); - if (!skb->xmit_more || stop_queue || - netif_xmit_stopped(netdev_get_tx_queue(dev, 0))) { - RTL_W8(TxPoll, NPQ); - - mmiowb(); - } + mmiowb(); - if (stop_queue) { + if (!TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) { /* Avoid wrongly optimistic queue wake-up: rtl_tx thread must * not miss a ring update when it notices a stopped queue. */ @@ -7198,7 +7188,6 @@ static void rtl8169_pcierr_interrupt(struct net_device *dev) static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp) { unsigned int dirty_tx, tx_left; - unsigned int bytes_compl = 0, pkts_compl = 0; dirty_tx = tp->dirty_tx; smp_rmb(); @@ -7222,8 +7211,10 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp) rtl8169_unmap_tx_skb(&tp->pci_dev->dev, tx_skb, tp->TxDescArray + entry); if (status & LastFrag) { - pkts_compl++; - bytes_compl += tx_skb->skb->len; + u64_stats_update_begin(&tp->tx_stats.syncp); + tp->tx_stats.packets++; + tp->tx_stats.bytes += tx_skb->skb->len; + u64_stats_update_end(&tp->tx_stats.syncp); dev_kfree_skb_any(tx_skb->skb); tx_skb->skb = NULL; } @@ -7232,13 +7223,6 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp) } if (tp->dirty_tx != dirty_tx) { - netdev_completed_queue(tp->dev, pkts_compl, bytes_compl); - - u64_stats_update_begin(&tp->tx_stats.syncp); - tp->tx_stats.packets += pkts_compl; - tp->tx_stats.bytes += bytes_compl; - u64_stats_update_end(&tp->tx_stats.syncp); - tp->dirty_tx = dirty_tx; /* Sync with rtl8169_start_xmit: * - publish dirty_tx ring index (write barrier) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 4da8bd2..736d5d1 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -508,7 +508,6 @@ static struct sh_eth_cpu_data r8a779x_data = { .tpauser = 1, .hw_swap = 1, .rmiimode = 1, - .shift_rd0 = 1, }; static void sh_eth_set_rate_sh7724(struct net_device *ndev) @@ -1392,6 +1391,9 @@ static void sh_eth_dev_exit(struct net_device *ndev) msleep(2); /* max frame time at 10 Mbps < 1250 us */ sh_eth_get_stats(ndev); sh_eth_reset(ndev); + + /* Set MAC address again */ + update_mac_address(ndev); } /* free Tx skb function */ @@ -1407,6 +1409,8 @@ static int sh_eth_txfree(struct net_device *ndev) txdesc = &mdp->tx_ring[entry]; if (txdesc->status & cpu_to_edmac(mdp, TD_TACT)) break; + /* TACT bit must be checked before all the following reads */ + rmb(); /* Free the original skb. */ if (mdp->tx_skbuff[entry]) { dma_unmap_single(&ndev->dev, txdesc->addr, @@ -1444,6 +1448,8 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) limit = boguscnt; rxdesc = &mdp->rx_ring[entry]; while (!(rxdesc->status & cpu_to_edmac(mdp, RD_RACT))) { + /* RACT bit must be checked before all the following reads */ + rmb(); desc_status = edmac_to_cpu(mdp, rxdesc->status); pkt_len = rxdesc->frame_length; @@ -1455,8 +1461,8 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) /* In case of almost all GETHER/ETHERs, the Receive Frame State * (RFS) bits in the Receive Descriptor 0 are from bit 9 to - * bit 0. However, in case of the R8A7740, R8A779x, and - * R7S72100 the RFS bits are from bit 25 to bit 16. So, the + * bit 0. However, in case of the R8A7740 and R7S72100 + * the RFS bits are from bit 25 to bit 16. So, the * driver needs right shifting by 16. */ if (mdp->cd->shift_rd0) @@ -1523,6 +1529,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) skb_checksum_none_assert(skb); rxdesc->addr = dma_addr; } + wmb(); /* RACT bit must be set after all the above writes */ if (entry >= mdp->num_rx_ring - 1) rxdesc->status |= cpu_to_edmac(mdp, RD_RACT | RD_RFP | RD_RDEL); @@ -1535,7 +1542,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) /* If we don't need to check status, don't. -KDU */ if (!(sh_eth_read(ndev, EDRRR) & EDRRR_R)) { /* fix the values for the next receiving if RDE is set */ - if (intr_status & EESR_RDE) { + if (intr_status & EESR_RDE && mdp->reg_offset[RDFAR] != 0) { u32 count = (sh_eth_read(ndev, RDFAR) - sh_eth_read(ndev, RDLAR)) >> 4; @@ -2174,7 +2181,7 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev) } spin_unlock_irqrestore(&mdp->lock, flags); - if (skb_padto(skb, ETH_ZLEN)) + if (skb_put_padto(skb, ETH_ZLEN)) return NETDEV_TX_OK; entry = mdp->cur_tx % mdp->num_tx_ring; @@ -2192,6 +2199,7 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev) } txdesc->buffer_length = skb->len; + wmb(); /* TACT bit must be set after all the above writes */ if (entry >= mdp->num_tx_ring - 1) txdesc->status |= cpu_to_edmac(mdp, TD_TACT | TD_TDLE); else diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index 34389b6a..5cecec2 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -1257,9 +1257,9 @@ static void rocker_port_set_enable(struct rocker_port *rocker_port, bool enable) u64 val = rocker_read64(rocker_port->rocker, PORT_PHYS_ENABLE); if (enable) - val |= 1 << rocker_port->lport; + val |= 1ULL << rocker_port->lport; else - val &= ~(1 << rocker_port->lport); + val &= ~(1ULL << rocker_port->lport); rocker_write64(rocker_port->rocker, PORT_PHYS_ENABLE, val); } @@ -4201,6 +4201,8 @@ static int rocker_probe_ports(struct rocker *rocker) alloc_size = sizeof(struct rocker_port *) * rocker->port_count; rocker->ports = kmalloc(alloc_size, GFP_KERNEL); + if (!rocker->ports) + return -ENOMEM; for (i = 0; i < rocker->port_count; i++) { err = rocker_probe_port(rocker, i); if (err) @@ -4466,10 +4468,16 @@ static int rocker_port_master_changed(struct net_device *dev) struct net_device *master = netdev_master_upper_dev_get(dev); int err = 0; + /* There are currently three cases handled here: + * 1. Joining a bridge + * 2. Leaving a previously joined bridge + * 3. Other, e.g. being added to or removed from a bond or openvswitch, + * in which case nothing is done + */ if (master && master->rtnl_link_ops && !strcmp(master->rtnl_link_ops->kind, "bridge")) err = rocker_port_bridge_join(rocker_port, master); - else + else if (rocker_port_is_bridged(rocker_port)) err = rocker_port_bridge_leave(rocker_port); return err; diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c index 6b33127..3449893 100644 --- a/drivers/net/ethernet/smsc/smc91c92_cs.c +++ b/drivers/net/ethernet/smsc/smc91c92_cs.c @@ -1070,11 +1070,8 @@ static int smc_open(struct net_device *dev) smc->packets_waiting = 0; smc_reset(dev); - init_timer(&smc->media); - smc->media.function = media_check; - smc->media.data = (u_long) dev; - smc->media.expires = jiffies + HZ; - add_timer(&smc->media); + setup_timer(&smc->media, media_check, (u_long)dev); + mod_timer(&smc->media, jiffies + HZ); return 0; } /* smc_open */ diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 88a55f9..8678e39 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -91,6 +91,11 @@ static const char version[] = #include "smc91x.h" +#if defined(CONFIG_ASSABET_NEPONSET) +#include <mach/assabet.h> +#include <mach/neponset.h> +#endif + #ifndef SMC_NOWAIT # define SMC_NOWAIT 0 #endif @@ -2243,10 +2248,9 @@ static int smc_drv_probe(struct platform_device *pdev) const struct of_device_id *match = NULL; struct smc_local *lp; struct net_device *ndev; - struct resource *res; + struct resource *res, *ires; unsigned int __iomem *addr; unsigned long irq_flags = SMC_IRQ_FLAGS; - unsigned long irq_resflags; int ret; ndev = alloc_etherdev(sizeof(struct smc_local)); @@ -2338,25 +2342,23 @@ static int smc_drv_probe(struct platform_device *pdev) goto out_free_netdev; } - ndev->irq = platform_get_irq(pdev, 0); - if (ndev->irq <= 0) { + ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!ires) { ret = -ENODEV; goto out_release_io; } - /* - * If this platform does not specify any special irqflags, or if - * the resource supplies a trigger, override the irqflags with - * the trigger flags from the resource. - */ - irq_resflags = irqd_get_trigger_type(irq_get_irq_data(ndev->irq)); - if (irq_flags == -1 || irq_resflags & IRQF_TRIGGER_MASK) - irq_flags = irq_resflags & IRQF_TRIGGER_MASK; + + ndev->irq = ires->start; + + if (irq_flags == -1 || ires->flags & IRQF_TRIGGER_MASK) + irq_flags = ires->flags & IRQF_TRIGGER_MASK; ret = smc_request_attrib(pdev, ndev); if (ret) goto out_release_io; -#if defined(CONFIG_SA1100_ASSABET) - neponset_ncr_set(NCR_ENET_OSC_EN); +#if defined(CONFIG_ASSABET_NEPONSET) + if (machine_is_assabet() && machine_has_neponset()) + neponset_ncr_set(NCR_ENET_OSC_EN); #endif platform_set_drvdata(pdev, ndev); ret = smc_enable_device(pdev); diff --git a/drivers/net/ethernet/smsc/smc91x.h b/drivers/net/ethernet/smsc/smc91x.h index be67baf..3a18501 100644 --- a/drivers/net/ethernet/smsc/smc91x.h +++ b/drivers/net/ethernet/smsc/smc91x.h @@ -39,14 +39,7 @@ * Define your architecture specific bus configuration parameters here. */ -#if defined(CONFIG_ARCH_LUBBOCK) ||\ - defined(CONFIG_MACH_MAINSTONE) ||\ - defined(CONFIG_MACH_ZYLONITE) ||\ - defined(CONFIG_MACH_LITTLETON) ||\ - defined(CONFIG_MACH_ZYLONITE2) ||\ - defined(CONFIG_ARCH_VIPER) ||\ - defined(CONFIG_MACH_STARGATE2) ||\ - defined(CONFIG_ARCH_VERSATILE) +#if defined(CONFIG_ARM) #include <asm/mach-types.h> @@ -74,95 +67,8 @@ /* We actually can't write halfwords properly if not word aligned */ static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg) { - if ((machine_is_mainstone() || machine_is_stargate2()) && reg & 2) { - unsigned int v = val << 16; - v |= readl(ioaddr + (reg & ~2)) & 0xffff; - writel(v, ioaddr + (reg & ~2)); - } else { - writew(val, ioaddr + reg); - } -} - -#elif defined(CONFIG_SA1100_PLEB) -/* We can only do 16-bit reads and writes in the static memory space. */ -#define SMC_CAN_USE_8BIT 1 -#define SMC_CAN_USE_16BIT 1 -#define SMC_CAN_USE_32BIT 0 -#define SMC_IO_SHIFT 0 -#define SMC_NOWAIT 1 - -#define SMC_inb(a, r) readb((a) + (r)) -#define SMC_insb(a, r, p, l) readsb((a) + (r), p, (l)) -#define SMC_inw(a, r) readw((a) + (r)) -#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l) -#define SMC_outb(v, a, r) writeb(v, (a) + (r)) -#define SMC_outsb(a, r, p, l) writesb((a) + (r), p, (l)) -#define SMC_outw(v, a, r) writew(v, (a) + (r)) -#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l) - -#define SMC_IRQ_FLAGS (-1) - -#elif defined(CONFIG_SA1100_ASSABET) - -#include <mach/neponset.h> - -/* We can only do 8-bit reads and writes in the static memory space. */ -#define SMC_CAN_USE_8BIT 1 -#define SMC_CAN_USE_16BIT 0 -#define SMC_CAN_USE_32BIT 0 -#define SMC_NOWAIT 1 - -/* The first two address lines aren't connected... */ -#define SMC_IO_SHIFT 2 - -#define SMC_inb(a, r) readb((a) + (r)) -#define SMC_outb(v, a, r) writeb(v, (a) + (r)) -#define SMC_insb(a, r, p, l) readsb((a) + (r), p, (l)) -#define SMC_outsb(a, r, p, l) writesb((a) + (r), p, (l)) -#define SMC_IRQ_FLAGS (-1) /* from resource */ - -#elif defined(CONFIG_MACH_LOGICPD_PXA270) || \ - defined(CONFIG_MACH_NOMADIK_8815NHK) - -#define SMC_CAN_USE_8BIT 0 -#define SMC_CAN_USE_16BIT 1 -#define SMC_CAN_USE_32BIT 0 -#define SMC_IO_SHIFT 0 -#define SMC_NOWAIT 1 - -#define SMC_inw(a, r) readw((a) + (r)) -#define SMC_outw(v, a, r) writew(v, (a) + (r)) -#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l) -#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l) - -#elif defined(CONFIG_ARCH_INNOKOM) || \ - defined(CONFIG_ARCH_PXA_IDP) || \ - defined(CONFIG_ARCH_RAMSES) || \ - defined(CONFIG_ARCH_PCM027) - -#define SMC_CAN_USE_8BIT 1 -#define SMC_CAN_USE_16BIT 1 -#define SMC_CAN_USE_32BIT 1 -#define SMC_IO_SHIFT 0 -#define SMC_NOWAIT 1 -#define SMC_USE_PXA_DMA 1 - -#define SMC_inb(a, r) readb((a) + (r)) -#define SMC_inw(a, r) readw((a) + (r)) -#define SMC_inl(a, r) readl((a) + (r)) -#define SMC_outb(v, a, r) writeb(v, (a) + (r)) -#define SMC_outl(v, a, r) writel(v, (a) + (r)) -#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l) -#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l) -#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l) -#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l) -#define SMC_IRQ_FLAGS (-1) /* from resource */ - -/* We actually can't write halfwords properly if not word aligned */ -static inline void -SMC_outw(u16 val, void __iomem *ioaddr, int reg) -{ - if (reg & 2) { + if ((machine_is_mainstone() || machine_is_stargate2() || + machine_is_pxa_idp()) && reg & 2) { unsigned int v = val << 16; v |= readl(ioaddr + (reg & ~2)) & 0xffff; writel(v, ioaddr + (reg & ~2)); @@ -237,20 +143,6 @@ SMC_outw(u16 val, void __iomem *ioaddr, int reg) #define RPC_LSA_DEFAULT RPC_LED_100_10 #define RPC_LSB_DEFAULT RPC_LED_TX_RX -#elif defined(CONFIG_ARCH_MSM) - -#define SMC_CAN_USE_8BIT 0 -#define SMC_CAN_USE_16BIT 1 -#define SMC_CAN_USE_32BIT 0 -#define SMC_NOWAIT 1 - -#define SMC_inw(a, r) readw((a) + (r)) -#define SMC_outw(v, a, r) writew(v, (a) + (r)) -#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l) -#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l) - -#define SMC_IRQ_FLAGS IRQF_TRIGGER_HIGH - #elif defined(CONFIG_COLDFIRE) #define SMC_CAN_USE_8BIT 0 diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 55e89b3..a0ea84f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -310,11 +310,11 @@ bool stmmac_eee_init(struct stmmac_priv *priv) spin_lock_irqsave(&priv->lock, flags); if (!priv->eee_active) { priv->eee_active = 1; - init_timer(&priv->eee_ctrl_timer); - priv->eee_ctrl_timer.function = stmmac_eee_ctrl_timer; - priv->eee_ctrl_timer.data = (unsigned long)priv; - priv->eee_ctrl_timer.expires = STMMAC_LPI_T(eee_timer); - add_timer(&priv->eee_ctrl_timer); + setup_timer(&priv->eee_ctrl_timer, + stmmac_eee_ctrl_timer, + (unsigned long)priv); + mod_timer(&priv->eee_ctrl_timer, + STMMAC_LPI_T(eee_timer)); priv->hw->mac->set_eee_timer(priv->hw, STMMAC_DEFAULT_LIT_LS, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index fb846eb..f9b42f1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -272,6 +272,37 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) struct stmmac_priv *priv = NULL; struct plat_stmmacenet_data *plat_dat = NULL; const char *mac = NULL; + int irq, wol_irq, lpi_irq; + + /* Get IRQ information early to have an ability to ask for deferred + * probe if needed before we went too far with resource allocation. + */ + irq = platform_get_irq_byname(pdev, "macirq"); + if (irq < 0) { + if (irq != -EPROBE_DEFER) { + dev_err(dev, + "MAC IRQ configuration information not found\n"); + } + return irq; + } + + /* On some platforms e.g. SPEAr the wake up irq differs from the mac irq + * The external wake up irq can be passed through the platform code + * named as "eth_wake_irq" + * + * In case the wake up interrupt is not passed from the platform + * so the driver will continue to use the mac irq (ndev->irq) + */ + wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq"); + if (wol_irq < 0) { + if (wol_irq == -EPROBE_DEFER) + return -EPROBE_DEFER; + wol_irq = irq; + } + + lpi_irq = platform_get_irq_byname(pdev, "eth_lpi"); + if (lpi_irq == -EPROBE_DEFER) + return -EPROBE_DEFER; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); addr = devm_ioremap_resource(dev, res); @@ -323,39 +354,15 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) return PTR_ERR(priv); } + /* Copy IRQ values to priv structure which is now avaialble */ + priv->dev->irq = irq; + priv->wol_irq = wol_irq; + priv->lpi_irq = lpi_irq; + /* Get MAC address if available (DT) */ if (mac) memcpy(priv->dev->dev_addr, mac, ETH_ALEN); - /* Get the MAC information */ - priv->dev->irq = platform_get_irq_byname(pdev, "macirq"); - if (priv->dev->irq < 0) { - if (priv->dev->irq != -EPROBE_DEFER) { - netdev_err(priv->dev, - "MAC IRQ configuration information not found\n"); - } - return priv->dev->irq; - } - - /* - * On some platforms e.g. SPEAr the wake up irq differs from the mac irq - * The external wake up irq can be passed through the platform code - * named as "eth_wake_irq" - * - * In case the wake up interrupt is not passed from the platform - * so the driver will continue to use the mac irq (ndev->irq) - */ - priv->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq"); - if (priv->wol_irq < 0) { - if (priv->wol_irq == -EPROBE_DEFER) - return -EPROBE_DEFER; - priv->wol_irq = priv->dev->irq; - } - - priv->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi"); - if (priv->lpi_irq == -EPROBE_DEFER) - return -EPROBE_DEFER; - platform_set_drvdata(pdev, priv->dev); pr_debug("STMMAC platform driver registration completed"); diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index 4b51f90..0c5842a 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -6989,10 +6989,10 @@ static int niu_class_to_ethflow(u64 class, int *flow_type) *flow_type = IP_USER_FLOW; break; default: - return 0; + return -EINVAL; } - return 1; + return 0; } static int niu_ethflow_to_class(int flow_type, u64 *class) @@ -7198,11 +7198,9 @@ static int niu_get_ethtool_tcam_entry(struct niu *np, class = (tp->key[0] & TCAM_V4KEY0_CLASS_CODE) >> TCAM_V4KEY0_CLASS_CODE_SHIFT; ret = niu_class_to_ethflow(class, &fsp->flow_type); - if (ret < 0) { netdev_info(np->dev, "niu%d: niu_class_to_ethflow failed\n", parent->index); - ret = -EINVAL; goto out; } diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 7d8dd0d..a1bbaf6 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1103,7 +1103,7 @@ static inline void cpsw_add_dual_emac_def_ale_entries( cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, port_mask, ALE_VLAN, slave->port_vlan, 0); cpsw_ale_add_ucast(priv->ale, priv->mac_addr, - priv->host_port, ALE_VLAN, slave->port_vlan); + priv->host_port, ALE_VLAN | ALE_SECURE, slave->port_vlan); } static void soft_reset_slave(struct cpsw_slave *slave) @@ -2466,6 +2466,7 @@ static int cpsw_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP static int cpsw_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -2518,11 +2519,9 @@ static int cpsw_resume(struct device *dev) } return 0; } +#endif -static const struct dev_pm_ops cpsw_pm_ops = { - .suspend = cpsw_suspend, - .resume = cpsw_resume, -}; +static SIMPLE_DEV_PM_OPS(cpsw_pm_ops, cpsw_suspend, cpsw_resume); static const struct of_device_id cpsw_of_mtable[] = { { .compatible = "ti,cpsw", }, diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index 98655b4..c00084d 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -423,6 +423,7 @@ static int davinci_mdio_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP static int davinci_mdio_suspend(struct device *dev) { struct davinci_mdio_data *data = dev_get_drvdata(dev); @@ -464,10 +465,10 @@ static int davinci_mdio_resume(struct device *dev) return 0; } +#endif static const struct dev_pm_ops davinci_mdio_pm_ops = { - .suspend_late = davinci_mdio_suspend, - .resume_early = davinci_mdio_resume, + SET_LATE_SYSTEM_SLEEP_PM_OPS(davinci_mdio_suspend, davinci_mdio_resume) }; #if IS_ENABLED(CONFIG_OF) diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c index a495931..0e0fbb5 100644 --- a/drivers/net/ethernet/wiznet/w5100.c +++ b/drivers/net/ethernet/wiznet/w5100.c @@ -498,9 +498,9 @@ static int w5100_napi_poll(struct napi_struct *napi, int budget) } if (rx_count < budget) { + napi_complete(napi); w5100_write(priv, W5100_IMR, IR_S0); mmiowb(); - napi_complete(napi); } return rx_count; diff --git a/drivers/net/ethernet/wiznet/w5300.c b/drivers/net/ethernet/wiznet/w5300.c index 09322d9..4b31000 100644 --- a/drivers/net/ethernet/wiznet/w5300.c +++ b/drivers/net/ethernet/wiznet/w5300.c @@ -418,9 +418,9 @@ static int w5300_napi_poll(struct napi_struct *napi, int budget) } if (rx_count < budget) { + napi_complete(napi); w5300_write(priv, W5300_IMR, IR_S0); mmiowb(); - napi_complete(napi); } return rx_count; diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index f7e0f0f..9e16a28 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -938,7 +938,7 @@ static void eth_set_mcast_list(struct net_device *dev) int i; static const u8 allmulti[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; - if (dev->flags & IFF_ALLMULTI) { + if ((dev->flags & IFF_ALLMULTI) && !(dev->flags & IFF_PROMISC)) { for (i = 0; i < ETH_ALEN; i++) { __raw_writel(allmulti[i], &port->regs->mcast_addr[i]); __raw_writel(allmulti[i], &port->regs->mcast_mask[i]); diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h index 924ea98..54549a6 100644 --- a/drivers/net/ipvlan/ipvlan.h +++ b/drivers/net/ipvlan/ipvlan.h @@ -114,7 +114,9 @@ unsigned int ipvlan_mac_hash(const unsigned char *addr); rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb); int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev); void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr); -bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6); +struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan, + const void *iaddr, bool is_v6); +bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6); struct ipvl_addr *ipvlan_ht_addr_lookup(const struct ipvl_port *port, const void *iaddr, bool is_v6); void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync); diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index 2a17500..b7877a1 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -81,19 +81,20 @@ void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr) hash = (addr->atype == IPVL_IPV6) ? ipvlan_get_v6_hash(&addr->ip6addr) : ipvlan_get_v4_hash(&addr->ip4addr); - hlist_add_head_rcu(&addr->hlnode, &port->hlhead[hash]); + if (hlist_unhashed(&addr->hlnode)) + hlist_add_head_rcu(&addr->hlnode, &port->hlhead[hash]); } void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync) { - hlist_del_rcu(&addr->hlnode); + hlist_del_init_rcu(&addr->hlnode); if (sync) synchronize_rcu(); } -bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6) +struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan, + const void *iaddr, bool is_v6) { - struct ipvl_port *port = ipvlan->port; struct ipvl_addr *addr; list_for_each_entry(addr, &ipvlan->addrs, anode) { @@ -101,12 +102,21 @@ bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6) ipv6_addr_equal(&addr->ip6addr, iaddr)) || (!is_v6 && addr->atype == IPVL_IPV4 && addr->ip4addr.s_addr == ((struct in_addr *)iaddr)->s_addr)) - return true; + return addr; } + return NULL; +} + +bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6) +{ + struct ipvl_dev *ipvlan; - if (ipvlan_ht_addr_lookup(port, iaddr, is_v6)) - return true; + ASSERT_RTNL(); + list_for_each_entry(ipvlan, &port->ipvlans, pnode) { + if (ipvlan_find_addr(ipvlan, iaddr, is_v6)) + return true; + } return false; } @@ -192,7 +202,8 @@ static void ipvlan_multicast_frame(struct ipvl_port *port, struct sk_buff *skb, if (skb->protocol == htons(ETH_P_PAUSE)) return; - list_for_each_entry(ipvlan, &port->ipvlans, pnode) { + rcu_read_lock(); + list_for_each_entry_rcu(ipvlan, &port->ipvlans, pnode) { if (local && (ipvlan == in_dev)) continue; @@ -219,6 +230,7 @@ static void ipvlan_multicast_frame(struct ipvl_port *port, struct sk_buff *skb, mcast_acct: ipvlan_count_rx(ipvlan, len, ret == NET_RX_SUCCESS, true); } + rcu_read_unlock(); /* Locally generated? ...Forward a copy to the main-device as * well. On the RX side we'll ignore it (wont give it to any diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index 4f4099d..4fa1420 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -505,7 +505,7 @@ static void ipvlan_link_delete(struct net_device *dev, struct list_head *head) if (ipvlan->ipv6cnt > 0 || ipvlan->ipv4cnt > 0) { list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) { ipvlan_ht_addr_del(addr, !dev->dismantle); - list_del_rcu(&addr->anode); + list_del(&addr->anode); } } list_del_rcu(&ipvlan->pnode); @@ -607,7 +607,7 @@ static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr) { struct ipvl_addr *addr; - if (ipvlan_addr_busy(ipvlan, ip6_addr, true)) { + if (ipvlan_addr_busy(ipvlan->port, ip6_addr, true)) { netif_err(ipvlan, ifup, ipvlan->dev, "Failed to add IPv6=%pI6c addr for %s intf\n", ip6_addr, ipvlan->dev->name); @@ -620,9 +620,13 @@ static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr) addr->master = ipvlan; memcpy(&addr->ip6addr, ip6_addr, sizeof(struct in6_addr)); addr->atype = IPVL_IPV6; - list_add_tail_rcu(&addr->anode, &ipvlan->addrs); + list_add_tail(&addr->anode, &ipvlan->addrs); ipvlan->ipv6cnt++; - ipvlan_ht_addr_add(ipvlan, addr); + /* If the interface is not up, the address will be added to the hash + * list by ipvlan_open. + */ + if (netif_running(ipvlan->dev)) + ipvlan_ht_addr_add(ipvlan, addr); return 0; } @@ -631,12 +635,12 @@ static void ipvlan_del_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr) { struct ipvl_addr *addr; - addr = ipvlan_ht_addr_lookup(ipvlan->port, ip6_addr, true); + addr = ipvlan_find_addr(ipvlan, ip6_addr, true); if (!addr) return; ipvlan_ht_addr_del(addr, true); - list_del_rcu(&addr->anode); + list_del(&addr->anode); ipvlan->ipv6cnt--; WARN_ON(ipvlan->ipv6cnt < 0); kfree_rcu(addr, rcu); @@ -675,7 +679,7 @@ static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr) { struct ipvl_addr *addr; - if (ipvlan_addr_busy(ipvlan, ip4_addr, false)) { + if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false)) { netif_err(ipvlan, ifup, ipvlan->dev, "Failed to add IPv4=%pI4 on %s intf.\n", ip4_addr, ipvlan->dev->name); @@ -688,9 +692,13 @@ static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr) addr->master = ipvlan; memcpy(&addr->ip4addr, ip4_addr, sizeof(struct in_addr)); addr->atype = IPVL_IPV4; - list_add_tail_rcu(&addr->anode, &ipvlan->addrs); + list_add_tail(&addr->anode, &ipvlan->addrs); ipvlan->ipv4cnt++; - ipvlan_ht_addr_add(ipvlan, addr); + /* If the interface is not up, the address will be added to the hash + * list by ipvlan_open. + */ + if (netif_running(ipvlan->dev)) + ipvlan_ht_addr_add(ipvlan, addr); ipvlan_set_broadcast_mac_filter(ipvlan, true); return 0; @@ -700,12 +708,12 @@ static void ipvlan_del_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr) { struct ipvl_addr *addr; - addr = ipvlan_ht_addr_lookup(ipvlan->port, ip4_addr, false); + addr = ipvlan_find_addr(ipvlan, ip4_addr, false); if (!addr) return; ipvlan_ht_addr_del(addr, true); - list_del_rcu(&addr->anode); + list_del(&addr->anode); ipvlan->ipv4cnt--; WARN_ON(ipvlan->ipv4cnt < 0); if (!ipvlan->ipv4cnt) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index e40fdfc..27ecc5c 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -654,11 +654,14 @@ static void macvtap_skb_to_vnet_hdr(struct macvtap_queue *q, } /* else everything is zero */ } +/* Neighbour code has some assumptions on HH_DATA_MOD alignment */ +#define MACVTAP_RESERVE HH_DATA_OFF(ETH_HLEN) + /* Get packet from user space buffer */ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, struct iov_iter *from, int noblock) { - int good_linear = SKB_MAX_HEAD(NET_IP_ALIGN); + int good_linear = SKB_MAX_HEAD(MACVTAP_RESERVE); struct sk_buff *skb; struct macvlan_dev *vlan; unsigned long total_len = iov_iter_count(from); @@ -722,7 +725,7 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, linear = macvtap16_to_cpu(q, vnet_hdr.hdr_len); } - skb = macvtap_alloc_skb(&q->sk, NET_IP_ALIGN, copylen, + skb = macvtap_alloc_skb(&q->sk, MACVTAP_RESERVE, copylen, linear, noblock, &err); if (!skb) goto err; diff --git a/drivers/net/phy/amd-xgbe-phy.c b/drivers/net/phy/amd-xgbe-phy.c index 9e3af54..32efbd4 100644 --- a/drivers/net/phy/amd-xgbe-phy.c +++ b/drivers/net/phy/amd-xgbe-phy.c @@ -92,6 +92,8 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver"); #define XGBE_PHY_CDR_RATE_PROPERTY "amd,serdes-cdr-rate" #define XGBE_PHY_PQ_SKEW_PROPERTY "amd,serdes-pq-skew" #define XGBE_PHY_TX_AMP_PROPERTY "amd,serdes-tx-amp" +#define XGBE_PHY_DFE_CFG_PROPERTY "amd,serdes-dfe-tap-config" +#define XGBE_PHY_DFE_ENA_PROPERTY "amd,serdes-dfe-tap-enable" #define XGBE_PHY_SPEEDS 3 #define XGBE_PHY_SPEED_1000 0 @@ -177,10 +179,12 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver"); #define SPEED_10000_BLWC 0 #define SPEED_10000_CDR 0x7 #define SPEED_10000_PLL 0x1 -#define SPEED_10000_PQ 0x1e +#define SPEED_10000_PQ 0x12 #define SPEED_10000_RATE 0x0 #define SPEED_10000_TXAMP 0xa #define SPEED_10000_WORD 0x7 +#define SPEED_10000_DFE_TAP_CONFIG 0x1 +#define SPEED_10000_DFE_TAP_ENABLE 0x7f #define SPEED_2500_BLWC 1 #define SPEED_2500_CDR 0x2 @@ -189,6 +193,8 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver"); #define SPEED_2500_RATE 0x1 #define SPEED_2500_TXAMP 0xf #define SPEED_2500_WORD 0x1 +#define SPEED_2500_DFE_TAP_CONFIG 0x3 +#define SPEED_2500_DFE_TAP_ENABLE 0x0 #define SPEED_1000_BLWC 1 #define SPEED_1000_CDR 0x2 @@ -197,16 +203,25 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver"); #define SPEED_1000_RATE 0x3 #define SPEED_1000_TXAMP 0xf #define SPEED_1000_WORD 0x1 +#define SPEED_1000_DFE_TAP_CONFIG 0x3 +#define SPEED_1000_DFE_TAP_ENABLE 0x0 /* SerDes RxTx register offsets */ +#define RXTX_REG6 0x0018 #define RXTX_REG20 0x0050 +#define RXTX_REG22 0x0058 #define RXTX_REG114 0x01c8 +#define RXTX_REG129 0x0204 /* SerDes RxTx register entry bit positions and sizes */ +#define RXTX_REG6_RESETB_RXD_INDEX 8 +#define RXTX_REG6_RESETB_RXD_WIDTH 1 #define RXTX_REG20_BLWC_ENA_INDEX 2 #define RXTX_REG20_BLWC_ENA_WIDTH 1 #define RXTX_REG114_PQ_REG_INDEX 9 #define RXTX_REG114_PQ_REG_WIDTH 7 +#define RXTX_REG129_RXDFE_CONFIG_INDEX 14 +#define RXTX_REG129_RXDFE_CONFIG_WIDTH 2 /* Bit setting and getting macros * The get macro will extract the current bit field value from within @@ -333,6 +348,18 @@ static const u32 amd_xgbe_phy_serdes_tx_amp[] = { SPEED_10000_TXAMP, }; +static const u32 amd_xgbe_phy_serdes_dfe_tap_cfg[] = { + SPEED_1000_DFE_TAP_CONFIG, + SPEED_2500_DFE_TAP_CONFIG, + SPEED_10000_DFE_TAP_CONFIG, +}; + +static const u32 amd_xgbe_phy_serdes_dfe_tap_ena[] = { + SPEED_1000_DFE_TAP_ENABLE, + SPEED_2500_DFE_TAP_ENABLE, + SPEED_10000_DFE_TAP_ENABLE, +}; + enum amd_xgbe_phy_an { AMD_XGBE_AN_READY = 0, AMD_XGBE_AN_PAGE_RECEIVED, @@ -393,6 +420,8 @@ struct amd_xgbe_phy_priv { u32 serdes_cdr_rate[XGBE_PHY_SPEEDS]; u32 serdes_pq_skew[XGBE_PHY_SPEEDS]; u32 serdes_tx_amp[XGBE_PHY_SPEEDS]; + u32 serdes_dfe_tap_cfg[XGBE_PHY_SPEEDS]; + u32 serdes_dfe_tap_ena[XGBE_PHY_SPEEDS]; /* Auto-negotiation state machine support */ struct mutex an_mutex; @@ -481,11 +510,16 @@ static void amd_xgbe_phy_serdes_complete_ratechange(struct phy_device *phydev) status = XSIR0_IOREAD(priv, SIR0_STATUS); if (XSIR_GET_BITS(status, SIR0_STATUS, RX_READY) && XSIR_GET_BITS(status, SIR0_STATUS, TX_READY)) - return; + goto rx_reset; } netdev_dbg(phydev->attached_dev, "SerDes rx/tx not ready (%#hx)\n", status); + +rx_reset: + /* Perform Rx reset for the DFE changes */ + XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RESETB_RXD, 0); + XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RESETB_RXD, 1); } static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev) @@ -534,6 +568,10 @@ static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev) priv->serdes_blwc[XGBE_PHY_SPEED_10000]); XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, priv->serdes_pq_skew[XGBE_PHY_SPEED_10000]); + XRXTX_IOWRITE_BITS(priv, RXTX_REG129, RXDFE_CONFIG, + priv->serdes_dfe_tap_cfg[XGBE_PHY_SPEED_10000]); + XRXTX_IOWRITE(priv, RXTX_REG22, + priv->serdes_dfe_tap_ena[XGBE_PHY_SPEED_10000]); amd_xgbe_phy_serdes_complete_ratechange(phydev); @@ -586,6 +624,10 @@ static int amd_xgbe_phy_gmii_2500_mode(struct phy_device *phydev) priv->serdes_blwc[XGBE_PHY_SPEED_2500]); XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, priv->serdes_pq_skew[XGBE_PHY_SPEED_2500]); + XRXTX_IOWRITE_BITS(priv, RXTX_REG129, RXDFE_CONFIG, + priv->serdes_dfe_tap_cfg[XGBE_PHY_SPEED_2500]); + XRXTX_IOWRITE(priv, RXTX_REG22, + priv->serdes_dfe_tap_ena[XGBE_PHY_SPEED_2500]); amd_xgbe_phy_serdes_complete_ratechange(phydev); @@ -638,6 +680,10 @@ static int amd_xgbe_phy_gmii_mode(struct phy_device *phydev) priv->serdes_blwc[XGBE_PHY_SPEED_1000]); XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, priv->serdes_pq_skew[XGBE_PHY_SPEED_1000]); + XRXTX_IOWRITE_BITS(priv, RXTX_REG129, RXDFE_CONFIG, + priv->serdes_dfe_tap_cfg[XGBE_PHY_SPEED_1000]); + XRXTX_IOWRITE(priv, RXTX_REG22, + priv->serdes_dfe_tap_ena[XGBE_PHY_SPEED_1000]); amd_xgbe_phy_serdes_complete_ratechange(phydev); @@ -1668,6 +1714,38 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev) sizeof(priv->serdes_tx_amp)); } + if (device_property_present(phy_dev, XGBE_PHY_DFE_CFG_PROPERTY)) { + ret = device_property_read_u32_array(phy_dev, + XGBE_PHY_DFE_CFG_PROPERTY, + priv->serdes_dfe_tap_cfg, + XGBE_PHY_SPEEDS); + if (ret) { + dev_err(dev, "invalid %s property\n", + XGBE_PHY_DFE_CFG_PROPERTY); + goto err_sir1; + } + } else { + memcpy(priv->serdes_dfe_tap_cfg, + amd_xgbe_phy_serdes_dfe_tap_cfg, + sizeof(priv->serdes_dfe_tap_cfg)); + } + + if (device_property_present(phy_dev, XGBE_PHY_DFE_ENA_PROPERTY)) { + ret = device_property_read_u32_array(phy_dev, + XGBE_PHY_DFE_ENA_PROPERTY, + priv->serdes_dfe_tap_ena, + XGBE_PHY_SPEEDS); + if (ret) { + dev_err(dev, "invalid %s property\n", + XGBE_PHY_DFE_ENA_PROPERTY); + goto err_sir1; + } + } else { + memcpy(priv->serdes_dfe_tap_ena, + amd_xgbe_phy_serdes_dfe_tap_ena, + sizeof(priv->serdes_dfe_tap_ena)); + } + phydev->priv = priv; if (!priv->adev || acpi_disabled) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index cdcac6a..52cd8db 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -236,6 +236,25 @@ static inline unsigned int phy_find_valid(unsigned int idx, u32 features) } /** + * phy_check_valid - check if there is a valid PHY setting which matches + * speed, duplex, and feature mask + * @speed: speed to match + * @duplex: duplex to match + * @features: A mask of the valid settings + * + * Description: Returns true if there is a valid setting, false otherwise. + */ +static inline bool phy_check_valid(int speed, int duplex, u32 features) +{ + unsigned int idx; + + idx = phy_find_valid(phy_find_setting(speed, duplex), features); + + return settings[idx].speed == speed && settings[idx].duplex == duplex && + (settings[idx].setting & features); +} + +/** * phy_sanitize_settings - make sure the PHY is set to supported speed and duplex * @phydev: the target phy_device struct * @@ -1045,7 +1064,6 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) int eee_lp, eee_cap, eee_adv; u32 lp, cap, adv; int status; - unsigned int idx; /* Read phy status to properly get the right settings */ status = phy_read_status(phydev); @@ -1077,8 +1095,7 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) adv = mmd_eee_adv_to_ethtool_adv_t(eee_adv); lp = mmd_eee_adv_to_ethtool_adv_t(eee_lp); - idx = phy_find_setting(phydev->speed, phydev->duplex); - if (!(lp & adv & settings[idx].setting)) + if (!phy_check_valid(phydev->speed, phydev->duplex, lp & adv)) goto eee_exit_err; if (clk_stop_enable) { diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 0e62274..7d39484 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -43,9 +43,7 @@ static struct team_port *team_port_get_rcu(const struct net_device *dev) { - struct team_port *port = rcu_dereference(dev->rx_handler_data); - - return team_port_exists(dev) ? port : NULL; + return rcu_dereference(dev->rx_handler_data); } static struct team_port *team_port_get_rtnl(const struct net_device *dev) @@ -1732,11 +1730,11 @@ static int team_set_mac_address(struct net_device *dev, void *p) if (dev->type == ARPHRD_ETHER && !is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - rcu_read_lock(); - list_for_each_entry_rcu(port, &team->port_list, list) + mutex_lock(&team->lock); + list_for_each_entry(port, &team->port_list, list) if (team->ops.port_change_dev_addr) team->ops.port_change_dev_addr(team, port); - rcu_read_unlock(); + mutex_unlock(&team->lock); return 0; } diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 3bd9678..7ba8d08 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -161,6 +161,7 @@ config USB_NET_AX8817X * Linksys USB200M * Netgear FA120 * Sitecom LN-029 + * Sitecom LN-028 * Intellinet USB 2.0 Ethernet * ST Lab USB 2.0 Ethernet * TrendNet TU2-ET100 diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 5c55f11..75d6f26 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -188,6 +188,8 @@ struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes)); skb_put(skb, sizeof(padbytes)); } + + usbnet_set_skb_tx_stats(skb, 1, 0); return skb; } diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index bf49792..1173a24 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -979,6 +979,10 @@ static const struct usb_device_id products [] = { USB_DEVICE (0x0df6, 0x0056), .driver_info = (unsigned long) &ax88178_info, }, { + // Sitecom LN-028 "USB 2.0 10/100/1000 Ethernet adapter" + USB_DEVICE (0x0df6, 0x061c), + .driver_info = (unsigned long) &ax88178_info, +}, { // corega FEther USB2-TX USB_DEVICE (0x07aa, 0x0017), .driver_info = (unsigned long) &ax8817x_info, diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 9311a08..4545e78 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -522,6 +522,7 @@ static const struct driver_info wwan_info = { #define DELL_VENDOR_ID 0x413C #define REALTEK_VENDOR_ID 0x0bda #define SAMSUNG_VENDOR_ID 0x04e8 +#define LENOVO_VENDOR_ID 0x17ef static const struct usb_device_id products[] = { /* BLACKLIST !! @@ -702,6 +703,13 @@ static const struct usb_device_id products[] = { .driver_info = 0, }, +/* Lenovo Thinkpad USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */ +{ + USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x7205, USB_CLASS_COMM, + USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), + .driver_info = 0, +}, + /* WHITELIST!!! * * CDC Ether uses two interfaces, not necessarily consecutive. diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 80a844e..c3e4da9 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -1172,17 +1172,17 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) /* return skb */ ctx->tx_curr_skb = NULL; - dev->net->stats.tx_packets += ctx->tx_curr_frame_num; /* keep private stats: framing overhead and number of NTBs */ ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload; ctx->tx_ntbs++; - /* usbnet has already counted all the framing overhead. + /* usbnet will count all the framing overhead by default. * Adjust the stats so that the tx_bytes counter show real * payload data instead. */ - dev->net->stats.tx_bytes -= skb_out->len - ctx->tx_curr_frame_payload; + usbnet_set_skb_tx_stats(skb_out, n, + ctx->tx_curr_frame_payload - skb_out->len); return skb_out; diff --git a/drivers/net/usb/cx82310_eth.c b/drivers/net/usb/cx82310_eth.c index 3eed708..1762ad3 100644 --- a/drivers/net/usb/cx82310_eth.c +++ b/drivers/net/usb/cx82310_eth.c @@ -46,8 +46,7 @@ enum cx82310_status { }; #define CMD_PACKET_SIZE 64 -/* first command after power on can take around 8 seconds */ -#define CMD_TIMEOUT 15000 +#define CMD_TIMEOUT 100 #define CMD_REPLY_RETRY 5 #define CX82310_MTU 1514 @@ -78,8 +77,9 @@ static int cx82310_cmd(struct usbnet *dev, enum cx82310_cmd cmd, bool reply, ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, CMD_EP), buf, CMD_PACKET_SIZE, &actual_len, CMD_TIMEOUT); if (ret < 0) { - dev_err(&dev->udev->dev, "send command %#x: error %d\n", - cmd, ret); + if (cmd != CMD_GET_LINK_STATUS) + dev_err(&dev->udev->dev, "send command %#x: error %d\n", + cmd, ret); goto end; } @@ -90,8 +90,10 @@ static int cx82310_cmd(struct usbnet *dev, enum cx82310_cmd cmd, bool reply, buf, CMD_PACKET_SIZE, &actual_len, CMD_TIMEOUT); if (ret < 0) { - dev_err(&dev->udev->dev, - "reply receive error %d\n", ret); + if (cmd != CMD_GET_LINK_STATUS) + dev_err(&dev->udev->dev, + "reply receive error %d\n", + ret); goto end; } if (actual_len > 0) @@ -134,6 +136,8 @@ static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf) int ret; char buf[15]; struct usb_device *udev = dev->udev; + u8 link[3]; + int timeout = 50; /* avoid ADSL modems - continue only if iProduct is "USB NET CARD" */ if (usb_string(udev, udev->descriptor.iProduct, buf, sizeof(buf)) > 0 @@ -160,6 +164,20 @@ static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf) if (!dev->partial_data) return -ENOMEM; + /* wait for firmware to become ready (indicated by the link being up) */ + while (--timeout) { + ret = cx82310_cmd(dev, CMD_GET_LINK_STATUS, true, NULL, 0, + link, sizeof(link)); + /* the command can time out during boot - it's not an error */ + if (!ret && link[0] == 1 && link[2] == 1) + break; + msleep(500); + }; + if (!timeout) { + dev_err(&udev->dev, "firmware not ready in time\n"); + return -ETIMEDOUT; + } + /* enable ethernet mode (?) */ ret = cx82310_cmd(dev, CMD_ETHERNET_MODE, true, "\x01", 1, NULL, 0); if (ret) { @@ -300,9 +318,18 @@ static const struct driver_info cx82310_info = { .tx_fixup = cx82310_tx_fixup, }; +#define USB_DEVICE_CLASS(vend, prod, cl, sc, pr) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_DEV_INFO, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bDeviceClass = (cl), \ + .bDeviceSubClass = (sc), \ + .bDeviceProtocol = (pr) + static const struct usb_device_id products[] = { { - USB_DEVICE_AND_INTERFACE_INFO(0x0572, 0xcb01, 0xff, 0, 0), + USB_DEVICE_CLASS(0x0572, 0xcb01, 0xff, 0, 0), .driver_info = (unsigned long) &cx82310_info }, { }, diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 9cdfb3f..778e915 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -1594,7 +1594,7 @@ hso_wait_modem_status(struct hso_serial *serial, unsigned long arg) } cprev = cnow; } - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); remove_wait_queue(&tiocmget->waitq, &wait); return ret; diff --git a/drivers/net/usb/plusb.c b/drivers/net/usb/plusb.c index 3d18bb0..1bfe0fc 100644 --- a/drivers/net/usb/plusb.c +++ b/drivers/net/usb/plusb.c @@ -134,6 +134,11 @@ static const struct usb_device_id products [] = { }, { USB_DEVICE(0x050d, 0x258a), /* Belkin F5U258/F5U279 (PL-25A1) */ .driver_info = (unsigned long) &prolific_info, +}, { + USB_DEVICE(0x3923, 0x7825), /* National Instruments USB + * Host-to-Host Cable + */ + .driver_info = (unsigned long) &prolific_info, }, { }, // END diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 438fc6b..9f7c0ab 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -492,6 +492,7 @@ enum rtl8152_flags { /* Define these values to match your device */ #define VENDOR_ID_REALTEK 0x0bda #define VENDOR_ID_SAMSUNG 0x04e8 +#define VENDOR_ID_LENOVO 0x17ef #define MCU_TYPE_PLA 0x0100 #define MCU_TYPE_USB 0x0000 @@ -4037,6 +4038,7 @@ static struct usb_device_id rtl8152_table[] = { {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)}, {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)}, {REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101)}, + {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x7205)}, {} }; diff --git a/drivers/net/usb/sr9800.c b/drivers/net/usb/sr9800.c index b94a0fb..953de13 100644 --- a/drivers/net/usb/sr9800.c +++ b/drivers/net/usb/sr9800.c @@ -144,6 +144,7 @@ static struct sk_buff *sr_tx_fixup(struct usbnet *dev, struct sk_buff *skb, skb_put(skb, sizeof(padbytes)); } + usbnet_set_skb_tx_stats(skb, 1, 0); return skb; } diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 449835f..777757a 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1188,8 +1188,7 @@ static void tx_complete (struct urb *urb) struct usbnet *dev = entry->dev; if (urb->status == 0) { - if (!(dev->driver_info->flags & FLAG_MULTI_PACKET)) - dev->net->stats.tx_packets++; + dev->net->stats.tx_packets += entry->packets; dev->net->stats.tx_bytes += entry->length; } else { dev->net->stats.tx_errors++; @@ -1347,7 +1346,19 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, } else urb->transfer_flags |= URB_ZERO_PACKET; } - entry->length = urb->transfer_buffer_length = length; + urb->transfer_buffer_length = length; + + if (info->flags & FLAG_MULTI_PACKET) { + /* Driver has set number of packets and a length delta. + * Calculate the complete length and ensure that it's + * positive. + */ + entry->length += length; + if (WARN_ON_ONCE(entry->length <= 0)) + entry->length = length; + } else { + usbnet_set_skb_tx_stats(skb, 1, length); + } spin_lock_irqsave(&dev->txq.lock, flags); retval = usb_autopm_get_interface_async(dev->intf); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index f1ff366..59b0e97 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1448,8 +1448,10 @@ static void virtnet_free_queues(struct virtnet_info *vi) { int i; - for (i = 0; i < vi->max_queue_pairs; i++) + for (i = 0; i < vi->max_queue_pairs; i++) { + napi_hash_del(&vi->rq[i].napi); netif_napi_del(&vi->rq[i].napi); + } kfree(vi->rq); kfree(vi->sq); @@ -1948,11 +1950,8 @@ static int virtnet_freeze(struct virtio_device *vdev) cancel_delayed_work_sync(&vi->refill); if (netif_running(vi->dev)) { - for (i = 0; i < vi->max_queue_pairs; i++) { + for (i = 0; i < vi->max_queue_pairs; i++) napi_disable(&vi->rq[i].napi); - napi_hash_del(&vi->rq[i].napi); - netif_napi_del(&vi->rq[i].napi); - } } remove_vq_common(vi); diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 1e0a775..f8528a4 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1218,7 +1218,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) goto drop; flags &= ~VXLAN_HF_RCO; - vni &= VXLAN_VID_MASK; + vni &= VXLAN_VNI_MASK; } /* For backwards compatibility, only allow reserved fields to be @@ -1239,7 +1239,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) flags &= ~VXLAN_GBP_USED_BITS; } - if (flags || (vni & ~VXLAN_VID_MASK)) { + if (flags || vni & ~VXLAN_VNI_MASK) { /* If there are any unprocessed flags remaining treat * this as a malformed packet. This behavior diverges from * VXLAN RFC (RFC7348) which stipulates that bits in reserved diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index 83c39e2..88d121d 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -806,21 +806,21 @@ static ssize_t cosa_read(struct file *file, spin_lock_irqsave(&cosa->lock, flags); add_wait_queue(&chan->rxwaitq, &wait); while (!chan->rx_status) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&cosa->lock, flags); schedule(); spin_lock_irqsave(&cosa->lock, flags); if (signal_pending(current) && chan->rx_status == 0) { chan->rx_status = 1; remove_wait_queue(&chan->rxwaitq, &wait); - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); spin_unlock_irqrestore(&cosa->lock, flags); mutex_unlock(&chan->rlock); return -ERESTARTSYS; } } remove_wait_queue(&chan->rxwaitq, &wait); - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); kbuf = chan->rxdata; count = chan->rxsize; spin_unlock_irqrestore(&cosa->lock, flags); @@ -890,14 +890,14 @@ static ssize_t cosa_write(struct file *file, spin_lock_irqsave(&cosa->lock, flags); add_wait_queue(&chan->txwaitq, &wait); while (!chan->tx_status) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&cosa->lock, flags); schedule(); spin_lock_irqsave(&cosa->lock, flags); if (signal_pending(current) && chan->tx_status == 0) { chan->tx_status = 1; remove_wait_queue(&chan->txwaitq, &wait); - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); chan->tx_status = 1; spin_unlock_irqrestore(&cosa->lock, flags); up(&chan->wsem); @@ -905,7 +905,7 @@ static ssize_t cosa_write(struct file *file, } } remove_wait_queue(&chan->txwaitq, &wait); - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); up(&chan->wsem); spin_unlock_irqrestore(&cosa->lock, flags); kfree(kbuf); diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index cb366ad..f50a6bc 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -219,12 +219,15 @@ void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif) struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp = (void *)vif->drv_priv; struct ath_buf *bf = avp->av_bcbuf; + struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n", avp->av_bslot); tasklet_disable(&sc->bcon_tasklet); + cur_conf->enable_beacon &= ~BIT(avp->av_bslot); + if (bf && bf->bf_mpdu) { struct sk_buff *skb = bf->bf_mpdu; dma_unmap_single(sc->dev, bf->bf_buf_addr, @@ -521,8 +524,7 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc, } if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { - if ((vif->type != NL80211_IFTYPE_AP) || - (sc->nbcnvifs > 1)) { + if (vif->type != NL80211_IFTYPE_AP) { ath_dbg(common, CONFIG, "An AP interface is already present !\n"); return false; @@ -616,12 +618,14 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, * enabling/disabling SWBA. */ if (changed & BSS_CHANGED_BEACON_ENABLED) { - if (!bss_conf->enable_beacon && - (sc->nbcnvifs <= 1)) { - cur_conf->enable_beacon = false; - } else if (bss_conf->enable_beacon) { - cur_conf->enable_beacon = true; - ath9k_cache_beacon_config(sc, ctx, bss_conf); + bool enabled = cur_conf->enable_beacon; + + if (!bss_conf->enable_beacon) { + cur_conf->enable_beacon &= ~BIT(avp->av_bslot); + } else { + cur_conf->enable_beacon |= BIT(avp->av_bslot); + if (!enabled) + ath9k_cache_beacon_config(sc, ctx, bss_conf); } } diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h index 2b79a56..d237373 100644 --- a/drivers/net/wireless/ath/ath9k/common.h +++ b/drivers/net/wireless/ath/ath9k/common.h @@ -54,7 +54,7 @@ struct ath_beacon_config { u16 dtim_period; u16 bmiss_timeout; u8 dtim_count; - bool enable_beacon; + u8 enable_beacon; bool ibss_creator; u32 nexttbtt; u32 intval; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 60aa8d7..8529014 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -424,7 +424,7 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah) ah->power_mode = ATH9K_PM_UNDEFINED; ah->htc_reset_init = true; - ah->tpc_enabled = true; + ah->tpc_enabled = false; ah->ani_function = ATH9K_ANI_ALL; if (!AR_SREV_9300_20_OR_LATER(ah)) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index ccbdb05..75345c1 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -5370,6 +5370,7 @@ static void b43_supported_bands(struct b43_wldev *dev, bool *have_2ghz_phy, case 0x432a: /* BCM4321 */ case 0x432d: /* BCM4322 */ case 0x4352: /* BCM43222 */ + case 0x435a: /* BCM43228 */ case 0x4333: /* BCM4331 */ case 0x43a2: /* BCM4360 */ case 0x43b3: /* BCM4352 */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index defb7a4..7748a1c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -126,7 +126,8 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); if (drvr->bus_if->wowl_supported) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); - brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); + if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID) + brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); /* set chip related quirks */ switch (drvr->bus_if->chip) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c index 50cdf70..8eff275 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c @@ -39,13 +39,22 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, void *dcmd_buf = NULL, *wr_pointer; u16 msglen, maxmsglen = PAGE_SIZE - 0x100; - brcmf_dbg(TRACE, "cmd %x set %d len %d\n", cmdhdr->cmd, cmdhdr->set, - cmdhdr->len); + if (len < sizeof(*cmdhdr)) { + brcmf_err("vendor command too short: %d\n", len); + return -EINVAL; + } vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); ifp = vif->ifp; - len -= sizeof(struct brcmf_vndr_dcmd_hdr); + brcmf_dbg(TRACE, "ifidx=%d, cmd=%d\n", ifp->ifidx, cmdhdr->cmd); + + if (cmdhdr->offset > len) { + brcmf_err("bad buffer offset %d > %d\n", cmdhdr->offset, len); + return -EINVAL; + } + + len -= cmdhdr->offset; ret_len = cmdhdr->len; if (ret_len > 0 || len > 0) { if (len > BRCMF_DCMD_MAXLEN) { diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index a6f22c3..3811878 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -708,7 +708,6 @@ struct iwl_priv { unsigned long reload_jiffies; int reload_count; bool ucode_loaded; - bool init_ucode_run; /* Don't run init uCode again */ u8 plcp_delta_threshold; diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 47e64e8..cceb026 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1114,16 +1114,17 @@ static void iwlagn_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, scd_queues &= ~(BIT(IWL_IPAN_CMD_QUEUE_NUM) | BIT(IWL_DEFAULT_CMD_QUEUE_NUM)); - if (vif) - scd_queues &= ~BIT(vif->hw_queue[IEEE80211_AC_VO]); - - IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n", scd_queues); - if (iwlagn_txfifo_flush(priv, scd_queues)) { - IWL_ERR(priv, "flush request fail\n"); - goto done; + if (drop) { + IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n", + scd_queues); + if (iwlagn_txfifo_flush(priv, scd_queues)) { + IWL_ERR(priv, "flush request fail\n"); + goto done; + } } + IWL_DEBUG_TX_QUEUES(priv, "wait transmit/flush all frames\n"); - iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff); + iwl_trans_wait_tx_queue_empty(priv->trans, scd_queues); done: mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index 4dbef7e..5244e43 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -418,9 +418,6 @@ int iwl_run_init_ucode(struct iwl_priv *priv) if (!priv->fw->img[IWL_UCODE_INIT].sec[0].len) return 0; - if (priv->init_ucode_run) - return 0; - iwl_init_notification_wait(&priv->notif_wait, &calib_wait, calib_complete, ARRAY_SIZE(calib_complete), iwlagn_wait_calib, priv); @@ -440,8 +437,6 @@ int iwl_run_init_ucode(struct iwl_priv *priv) */ ret = iwl_wait_notification(&priv->notif_wait, &calib_wait, UCODE_CALIB_TIMEOUT); - if (!ret) - priv->init_ucode_run = true; goto out; diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index c3817fa..06f6cc0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -95,7 +95,8 @@ static const struct iwl_eeprom_params iwl1000_eeprom_params = { .nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ .base_params = &iwl1000_base_params, \ .eeprom_params = &iwl1000_eeprom_params, \ - .led_mode = IWL_LED_BLINK + .led_mode = IWL_LED_BLINK, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl1000_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 1000 BGN", @@ -121,7 +122,8 @@ const struct iwl_cfg iwl1000_bg_cfg = { .base_params = &iwl1000_base_params, \ .eeprom_params = &iwl1000_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ - .rx_with_siso_diversity = true + .rx_with_siso_diversity = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl100_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 100 BGN", diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c index 21e5d08..890b95f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-2000.c +++ b/drivers/net/wireless/iwlwifi/iwl-2000.c @@ -123,7 +123,9 @@ static const struct iwl_eeprom_params iwl20x0_eeprom_params = { .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2000_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE + .led_mode = IWL_LED_RF_STATE, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + const struct iwl_cfg iwl2000_2bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 2200 BGN", @@ -149,7 +151,8 @@ const struct iwl_cfg iwl2000_2bgn_d_cfg = { .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2030_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE + .led_mode = IWL_LED_RF_STATE, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl2030_2bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN", @@ -170,7 +173,8 @@ const struct iwl_cfg iwl2030_2bgn_cfg = { .base_params = &iwl2000_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ - .rx_with_siso_diversity = true + .rx_with_siso_diversity = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl105_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 105 BGN", @@ -197,7 +201,8 @@ const struct iwl_cfg iwl105_bgn_d_cfg = { .base_params = &iwl2030_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ - .rx_with_siso_diversity = true + .rx_with_siso_diversity = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl135_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 135 BGN", diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 332bbed..724194e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -93,7 +93,8 @@ static const struct iwl_eeprom_params iwl5000_eeprom_params = { .nvm_calib_ver = EEPROM_5000_TX_POWER_VERSION, \ .base_params = &iwl5000_base_params, \ .eeprom_params = &iwl5000_eeprom_params, \ - .led_mode = IWL_LED_BLINK + .led_mode = IWL_LED_BLINK, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl5300_agn_cfg = { .name = "Intel(R) Ultimate N WiFi Link 5300 AGN", @@ -158,7 +159,8 @@ const struct iwl_cfg iwl5350_agn_cfg = { .base_params = &iwl5000_base_params, \ .eeprom_params = &iwl5000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ - .internal_wimax_coex = true + .internal_wimax_coex = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl5150_agn_cfg = { .name = "Intel(R) WiMAX/WiFi Link 5150 AGN", diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index 8f2c3c8..21b2630 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -145,7 +145,8 @@ static const struct iwl_eeprom_params iwl6000_eeprom_params = { .nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE + .led_mode = IWL_LED_RF_STATE, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl6005_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6205 AGN", @@ -199,7 +200,8 @@ const struct iwl_cfg iwl6005_2agn_mow2_cfg = { .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE + .led_mode = IWL_LED_RF_STATE, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl6030_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN", @@ -235,7 +237,8 @@ const struct iwl_cfg iwl6030_2bg_cfg = { .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE + .led_mode = IWL_LED_RF_STATE, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl6035_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN", @@ -290,7 +293,8 @@ const struct iwl_cfg iwl130_bg_cfg = { .nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION, \ .base_params = &iwl6000_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .led_mode = IWL_LED_BLINK + .led_mode = IWL_LED_BLINK, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl6000i_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6200 AGN", @@ -322,7 +326,8 @@ const struct iwl_cfg iwl6000i_2bg_cfg = { .base_params = &iwl6050_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ - .internal_wimax_coex = true + .internal_wimax_coex = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl6050_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN", @@ -347,7 +352,8 @@ const struct iwl_cfg iwl6050_2abg_cfg = { .base_params = &iwl6050_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ - .internal_wimax_coex = true + .internal_wimax_coex = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K const struct iwl_cfg iwl6150_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN", diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 996e7f1..c7154ac 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -1257,6 +1257,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) op->name, err); #endif } + kfree(pieces); return; try_again: diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index 1ec4d55..7810c41 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -793,7 +793,8 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, if (!vif->bss_conf.assoc) smps_mode = IEEE80211_SMPS_AUTOMATIC; - if (IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status, + if (mvmvif->phy_ctxt && + IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status, mvmvif->phy_ctxt->id)) smps_mode = IEEE80211_SMPS_AUTOMATIC; diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c index d530ef3..542ee74 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c @@ -832,7 +832,8 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, if (!vif->bss_conf.assoc) smps_mode = IEEE80211_SMPS_AUTOMATIC; - if (data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id)) + if (mvmvif->phy_ctxt && + data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id)) smps_mode = IEEE80211_SMPS_AUTOMATIC; IWL_DEBUG_COEX(data->mvm, diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 1ff7ec0..09654e7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -405,7 +405,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ]; - if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_BEAMFORMER) + if ((mvm->fw->ucode_capa.capa[0] & + IWL_UCODE_TLV_CAPA_BEAMFORMER) && + (mvm->fw->ucode_capa.api[0] & + IWL_UCODE_TLV_API_LQ_SS_PARAMS)) hw->wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE; } @@ -2215,7 +2218,19 @@ static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); - iwl_mvm_cancel_scan(mvm); + /* Due to a race condition, it's possible that mac80211 asks + * us to stop a hw_scan when it's already stopped. This can + * happen, for instance, if we stopped the scan ourselves, + * called ieee80211_scan_completed() and the userspace called + * cancel scan scan before ieee80211_scan_work() could run. + * To handle that, simply return if the scan is not running. + */ + /* FIXME: for now, we ignore this race for UMAC scans, since + * they don't set the scan_status. + */ + if ((mvm->scan_status == IWL_MVM_SCAN_OS) || + (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) + iwl_mvm_cancel_scan(mvm); mutex_unlock(&mvm->mutex); } @@ -2559,12 +2574,29 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, int ret; mutex_lock(&mvm->mutex); + + /* Due to a race condition, it's possible that mac80211 asks + * us to stop a sched_scan when it's already stopped. This + * can happen, for instance, if we stopped the scan ourselves, + * called ieee80211_sched_scan_stopped() and the userspace called + * stop sched scan scan before ieee80211_sched_scan_stopped_work() + * could run. To handle this, simply return if the scan is + * not running. + */ + /* FIXME: for now, we ignore this race for UMAC scans, since + * they don't set the scan_status. + */ + if (mvm->scan_status != IWL_MVM_SCAN_SCHED && + !(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { + mutex_unlock(&mvm->mutex); + return 0; + } + ret = iwl_mvm_scan_offload_stop(mvm, false); mutex_unlock(&mvm->mutex); iwl_mvm_wait_for_async_handlers(mvm); return ret; - } static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 194bd1f..078f24c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -134,9 +134,12 @@ enum rs_column_mode { #define MAX_NEXT_COLUMNS 7 #define MAX_COLUMN_CHECKS 3 +struct rs_tx_column; + typedef bool (*allow_column_func_t) (struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl); + struct iwl_scale_tbl_info *tbl, + const struct rs_tx_column *next_col); struct rs_tx_column { enum rs_column_mode mode; @@ -147,13 +150,15 @@ struct rs_tx_column { }; static bool rs_ant_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl) + struct iwl_scale_tbl_info *tbl, + const struct rs_tx_column *next_col) { - return iwl_mvm_bt_coex_is_ant_avail(mvm, tbl->rate.ant); + return iwl_mvm_bt_coex_is_ant_avail(mvm, next_col->ant); } static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl) + struct iwl_scale_tbl_info *tbl, + const struct rs_tx_column *next_col) { if (!sta->ht_cap.ht_supported) return false; @@ -171,7 +176,8 @@ static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, } static bool rs_siso_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl) + struct iwl_scale_tbl_info *tbl, + const struct rs_tx_column *next_col) { if (!sta->ht_cap.ht_supported) return false; @@ -180,7 +186,8 @@ static bool rs_siso_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, } static bool rs_sgi_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl) + struct iwl_scale_tbl_info *tbl, + const struct rs_tx_column *next_col) { struct rs_rate *rate = &tbl->rate; struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; @@ -1271,6 +1278,9 @@ static void rs_mac80211_tx_status(void *mvm_r, struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + if (!iwl_mvm_sta_from_mac80211(sta)->vif) + return; + if (!ieee80211_is_data(hdr->frame_control) || info->flags & IEEE80211_TX_CTL_NO_ACK) return; @@ -1590,7 +1600,7 @@ static enum rs_column rs_get_next_column(struct iwl_mvm *mvm, for (j = 0; j < MAX_COLUMN_CHECKS; j++) { allow_func = next_col->checks[j]; - if (allow_func && !allow_func(mvm, sta, tbl)) + if (allow_func && !allow_func(mvm, sta, tbl, next_col)) break; } @@ -2504,6 +2514,14 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_lq_sta *lq_sta = mvm_sta; + if (sta && !iwl_mvm_sta_from_mac80211(sta)->vif) { + /* if vif isn't initialized mvm doesn't know about + * this station, so don't do anything with the it + */ + sta = NULL; + mvm_sta = NULL; + } + /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */ /* Treat uninitialized rate scaling data same as non-existing. */ @@ -2820,6 +2838,9 @@ static void rs_rate_update(void *mvm_r, (struct iwl_op_mode *)mvm_r; struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + if (!iwl_mvm_sta_from_mac80211(sta)->vif) + return; + /* Stop any ongoing aggregations as rs starts off assuming no agg */ for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) ieee80211_stop_tx_ba_session(sta, tid); @@ -3580,9 +3601,15 @@ static ssize_t iwl_dbgfs_ss_force_write(struct iwl_lq_sta *lq_sta, char *buf, MVM_DEBUGFS_READ_WRITE_FILE_OPS(ss_force, 32); -static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir) +static void rs_add_debugfs(void *mvm, void *priv_sta, struct dentry *dir) { - struct iwl_lq_sta *lq_sta = mvm_sta; + struct iwl_lq_sta *lq_sta = priv_sta; + struct iwl_mvm_sta *mvmsta; + + mvmsta = container_of(lq_sta, struct iwl_mvm_sta, lq_sta); + + if (!mvmsta->vif) + return; debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, lq_sta, &rs_sta_dbgfs_scale_table_ops); diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 7e9aa3c..c47c8051 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -1128,8 +1128,10 @@ int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify) if (mvm->scan_status == IWL_MVM_SCAN_NONE) return 0; - if (iwl_mvm_is_radio_killed(mvm)) + if (iwl_mvm_is_radio_killed(mvm)) { + ret = 0; goto out; + } if (mvm->scan_status != IWL_MVM_SCAN_SCHED && (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) || @@ -1148,16 +1150,14 @@ int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify) IWL_DEBUG_SCAN(mvm, "Send stop %sscan failed %d\n", sched ? "offloaded " : "", ret); iwl_remove_notification(&mvm->notif_wait, &wait_scan_done); - return ret; + goto out; } IWL_DEBUG_SCAN(mvm, "Successfully sent stop %sscan\n", sched ? "offloaded " : ""); ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ); - if (ret) - return ret; - +out: /* * Clear the scan status so the next scan requests will succeed. This * also ensures the Rx handler doesn't do anything, as the scan was @@ -1167,7 +1167,6 @@ int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify) if (mvm->scan_status == IWL_MVM_SCAN_OS) iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); -out: mvm->scan_status = IWL_MVM_SCAN_NONE; if (notify) { @@ -1177,7 +1176,7 @@ out: ieee80211_scan_completed(mvm->hw, true); } - return 0; + return ret; } static void iwl_mvm_unified_scan_fill_tx_cmd(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index 54fafbf..4b81c0b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -197,6 +197,8 @@ iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm, struct iwl_time_event_notif *notif) { if (!le32_to_cpu(notif->status)) { + if (te_data->vif->type == NL80211_IFTYPE_STATION) + ieee80211_connection_loss(te_data->vif); IWL_DEBUG_TE(mvm, "CSA time event failed to start\n"); iwl_mvm_te_clear_data(mvm, te_data); return; @@ -750,8 +752,7 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm) * request */ list_for_each_entry(te_data, &mvm->time_event_list, list) { - if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE && - te_data->running) { + if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); is_p2p = true; goto remove_te; @@ -766,10 +767,8 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm) * request */ list_for_each_entry(te_data, &mvm->aux_roc_te_list, list) { - if (te_data->running) { - mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); - goto remove_te; - } + mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); + goto remove_te; } remove_te: diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 07304e1..96a0540 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -949,8 +949,10 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, mvmsta = iwl_mvm_sta_from_mac80211(sta); tid_data = &mvmsta->tid_data[tid]; - if (WARN_ONCE(tid_data->txq_id != scd_flow, "Q %d, tid %d, flow %d", - tid_data->txq_id, tid, scd_flow)) { + if (tid_data->txq_id != scd_flow) { + IWL_ERR(mvm, + "invalid BA notification: Q %d, tid %d, flow %d\n", + tid_data->txq_id, tid, scd_flow); rcu_read_unlock(); return 0; } diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index dbd6bcf..686dd30 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -368,10 +368,12 @@ static const struct pci_device_id iwl_hw_card_ids[] = { /* 3165 Series */ {IWL_PCI_DEVICE(0x3165, 0x4010, iwl3165_2ac_cfg)}, {IWL_PCI_DEVICE(0x3165, 0x4012, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x4210, iwl3165_2ac_cfg)}, {IWL_PCI_DEVICE(0x3165, 0x4410, iwl3165_2ac_cfg)}, {IWL_PCI_DEVICE(0x3165, 0x4510, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3166, 0x4310, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3166, 0x4210, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x8010, iwl3165_2ac_cfg)}, /* 7265 Series */ {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)}, diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 4a4c658..8908be6 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -946,7 +946,8 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, goto nla_put_failure; genlmsg_end(skb, msg_head); - genlmsg_unicast(&init_net, skb, dst_portid); + if (genlmsg_unicast(&init_net, skb, dst_portid)) + goto err_free_txskb; /* Enqueue the packet */ skb_queue_tail(&data->pending, my_skb); @@ -955,6 +956,8 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, return; nla_put_failure: + nlmsg_free(skb); +err_free_txskb: printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__); ieee80211_free_txskb(hw, my_skb); data->tx_failed++; diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 1d46774..074f716 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -1386,8 +1386,11 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) } return true; - } else if (0x86DD == ether_type) { - return true; + } else if (ETH_P_IPV6 == ether_type) { + /* TODO: Handle any IPv6 cases that need special handling. + * For now, always return false + */ + goto end; } end: diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index a62170e..8c45cf4 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -1124,12 +1124,22 @@ static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw) /*This is for new trx flow*/ struct rtl_tx_buffer_desc *pbuffer_desc = NULL; u8 temp_one = 1; + u8 *entry; memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc)); ring = &rtlpci->tx_ring[BEACON_QUEUE]; pskb = __skb_dequeue(&ring->queue); - if (pskb) + if (rtlpriv->use_new_trx_flow) + entry = (u8 *)(&ring->buffer_desc[ring->idx]); + else + entry = (u8 *)(&ring->desc[ring->idx]); + if (pskb) { + pci_unmap_single(rtlpci->pdev, + rtlpriv->cfg->ops->get_desc( + (u8 *)entry, true, HW_DESC_TXBUFF_ADDR), + pskb->len, PCI_DMA_TODEVICE); kfree_skb(pskb); + } /*NB: the beacon data buffer must be 32-bit aligned. */ pskb = ieee80211_beacon_get(hw, mac->vif); diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index f38227af..3aa8648 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -340,12 +340,11 @@ static void xenvif_get_ethtool_stats(struct net_device *dev, unsigned int num_queues = vif->num_queues; int i; unsigned int queue_index; - struct xenvif_stats *vif_stats; for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++) { unsigned long accum = 0; for (queue_index = 0; queue_index < num_queues; ++queue_index) { - vif_stats = &vif->queues[queue_index].stats; + void *vif_stats = &vif->queues[queue_index].stats; accum += *(unsigned long *)(vif_stats + xenvif_stats[i].offset); } data[i] = accum; diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index f7a31d2..997cf09 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -96,6 +96,7 @@ static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx, static void make_tx_response(struct xenvif_queue *queue, struct xen_netif_tx_request *txp, s8 st); +static void push_tx_responses(struct xenvif_queue *queue); static inline int tx_work_todo(struct xenvif_queue *queue); @@ -657,6 +658,7 @@ static void xenvif_tx_err(struct xenvif_queue *queue, do { spin_lock_irqsave(&queue->response_lock, flags); make_tx_response(queue, txp, XEN_NETIF_RSP_ERROR); + push_tx_responses(queue); spin_unlock_irqrestore(&queue->response_lock, flags); if (cons == end) break; @@ -1343,7 +1345,7 @@ static int xenvif_handle_frag_list(struct xenvif_queue *queue, struct sk_buff *s { unsigned int offset = skb_headlen(skb); skb_frag_t frags[MAX_SKB_FRAGS]; - int i; + int i, f; struct ubuf_info *uarg; struct sk_buff *nskb = skb_shinfo(skb)->frag_list; @@ -1383,23 +1385,25 @@ static int xenvif_handle_frag_list(struct xenvif_queue *queue, struct sk_buff *s frags[i].page_offset = 0; skb_frag_size_set(&frags[i], len); } - /* swap out with old one */ - memcpy(skb_shinfo(skb)->frags, - frags, - i * sizeof(skb_frag_t)); - skb_shinfo(skb)->nr_frags = i; - skb->truesize += i * PAGE_SIZE; - /* remove traces of mapped pages and frag_list */ + /* Copied all the bits from the frag list -- free it. */ skb_frag_list_init(skb); + xenvif_skb_zerocopy_prepare(queue, nskb); + kfree_skb(nskb); + + /* Release all the original (foreign) frags. */ + for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) + skb_frag_unref(skb, f); uarg = skb_shinfo(skb)->destructor_arg; /* increase inflight counter to offset decrement in callback */ atomic_inc(&queue->inflight_packets); uarg->callback(uarg, true); skb_shinfo(skb)->destructor_arg = NULL; - xenvif_skb_zerocopy_prepare(queue, nskb); - kfree_skb(nskb); + /* Fill the skb with the new (local) frags. */ + memcpy(skb_shinfo(skb)->frags, frags, i * sizeof(skb_frag_t)); + skb_shinfo(skb)->nr_frags = i; + skb->truesize += i * PAGE_SIZE; return 0; } @@ -1652,13 +1656,20 @@ static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx, unsigned long flags; pending_tx_info = &queue->pending_tx_info[pending_idx]; + spin_lock_irqsave(&queue->response_lock, flags); + make_tx_response(queue, &pending_tx_info->req, status); - index = pending_index(queue->pending_prod); + + /* Release the pending index before pusing the Tx response so + * its available before a new Tx request is pushed by the + * frontend. + */ + index = pending_index(queue->pending_prod++); queue->pending_ring[index] = pending_idx; - /* TX shouldn't use the index before we give it back here */ - mb(); - queue->pending_prod++; + + push_tx_responses(queue); + spin_unlock_irqrestore(&queue->response_lock, flags); } @@ -1669,7 +1680,6 @@ static void make_tx_response(struct xenvif_queue *queue, { RING_IDX i = queue->tx.rsp_prod_pvt; struct xen_netif_tx_response *resp; - int notify; resp = RING_GET_RESPONSE(&queue->tx, i); resp->id = txp->id; @@ -1679,6 +1689,12 @@ static void make_tx_response(struct xenvif_queue *queue, RING_GET_RESPONSE(&queue->tx, ++i)->status = XEN_NETIF_RSP_NULL; queue->tx.rsp_prod_pvt = ++i; +} + +static void push_tx_responses(struct xenvif_queue *queue) +{ + int notify; + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&queue->tx, notify); if (notify) notify_remote_via_irq(queue->tx_irq); diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index e9b960f..720aaf6 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1008,8 +1008,7 @@ err: static int xennet_change_mtu(struct net_device *dev, int mtu) { - int max = xennet_can_sg(dev) ? - XEN_NETIF_MAX_TX_SIZE - MAX_TCP_HEADER : ETH_DATA_LEN; + int max = xennet_can_sg(dev) ? XEN_NETIF_MAX_TX_SIZE : ETH_DATA_LEN; if (mtu > max) return -EINVAL; @@ -1279,8 +1278,6 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev) netdev->ethtool_ops = &xennet_ethtool_ops; SET_NETDEV_DEV(netdev, &dev->dev); - netif_set_gso_max_size(netdev, XEN_NETIF_MAX_TX_SIZE - MAX_TCP_HEADER); - np->netdev = netdev; netif_carrier_off(netdev); diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 38d1c51..7bcaeec 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -84,8 +84,7 @@ config OF_RESOLVE bool config OF_OVERLAY - bool - depends on OF + bool "Device Tree overlays" select OF_DYNAMIC select OF_RESOLVE diff --git a/drivers/of/address.c b/drivers/of/address.c index ad29069..78a7dcb 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -450,12 +450,17 @@ static struct of_bus *of_match_bus(struct device_node *np) return NULL; } -static int of_empty_ranges_quirk(void) +static int of_empty_ranges_quirk(struct device_node *np) { if (IS_ENABLED(CONFIG_PPC)) { - /* To save cycles, we cache the result */ + /* To save cycles, we cache the result for global "Mac" setting */ static int quirk_state = -1; + /* PA-SEMI sdc DT bug */ + if (of_device_is_compatible(np, "1682m-sdc")) + return true; + + /* Make quirk cached */ if (quirk_state < 0) quirk_state = of_machine_is_compatible("Power Macintosh") || @@ -490,7 +495,7 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, * This code is only enabled on powerpc. --gcl */ ranges = of_get_property(parent, rprop, &rlen); - if (ranges == NULL && !of_empty_ranges_quirk()) { + if (ranges == NULL && !of_empty_ranges_quirk(parent)) { pr_debug("OF: no ranges; cannot translate\n"); return 1; } diff --git a/drivers/of/base.c b/drivers/of/base.c index 0a8aeb8..8f165b1 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -714,16 +714,12 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent, const char *path) { struct device_node *child; - int len = strchrnul(path, '/') - path; - int term; + int len; + len = strcspn(path, "/:"); if (!len) return NULL; - term = strchrnul(path, ':') - path; - if (term < len) - len = term; - __for_each_child_of_node(parent, child) { const char *name = strrchr(child->full_name, '/'); if (WARN(!name, "malformed device_node %s\n", child->full_name)) @@ -768,8 +764,12 @@ struct device_node *of_find_node_opts_by_path(const char *path, const char **opt /* The path could begin with an alias */ if (*path != '/') { - char *p = strchrnul(path, '/'); - int len = separator ? separator - path : p - path; + int len; + const char *p = separator; + + if (!p) + p = strchrnul(path, '/'); + len = p - path; /* of_aliases must not be NULL */ if (!of_aliases) @@ -794,6 +794,8 @@ struct device_node *of_find_node_opts_by_path(const char *path, const char **opt path++; /* Increment past '/' delimiter */ np = __of_find_node_by_path(np, path); path = strchrnul(path, '/'); + if (separator && separator < path) + break; } raw_spin_unlock_irqrestore(&devtree_lock, flags); return np; diff --git a/drivers/of/device.c b/drivers/of/device.c index 46d6c75c..20c1332 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -2,6 +2,9 @@ #include <linux/kernel.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/of_iommu.h> +#include <linux/dma-mapping.h> #include <linux/init.h> #include <linux/module.h> #include <linux/mod_devicetable.h> @@ -66,6 +69,87 @@ int of_device_add(struct platform_device *ofdev) return device_add(&ofdev->dev); } +/** + * of_dma_configure - Setup DMA configuration + * @dev: Device to apply DMA configuration + * @np: Pointer to OF node having DMA configuration + * + * Try to get devices's DMA configuration from DT and update it + * accordingly. + * + * If platform code needs to use its own special DMA configuration, it + * can use a platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE events + * to fix up DMA configuration. + */ +void of_dma_configure(struct device *dev, struct device_node *np) +{ + u64 dma_addr, paddr, size; + int ret; + bool coherent; + unsigned long offset; + struct iommu_ops *iommu; + + /* + * Set default coherent_dma_mask to 32 bit. Drivers are expected to + * setup the correct supported mask. + */ + if (!dev->coherent_dma_mask) + dev->coherent_dma_mask = DMA_BIT_MASK(32); + + /* + * Set it to coherent_dma_mask by default if the architecture + * code has not set it. + */ + if (!dev->dma_mask) + dev->dma_mask = &dev->coherent_dma_mask; + + ret = of_dma_get_range(np, &dma_addr, &paddr, &size); + if (ret < 0) { + dma_addr = offset = 0; + size = dev->coherent_dma_mask + 1; + } else { + offset = PFN_DOWN(paddr - dma_addr); + + /* + * Add a work around to treat the size as mask + 1 in case + * it is defined in DT as a mask. + */ + if (size & 1) { + dev_warn(dev, "Invalid size 0x%llx for dma-range\n", + size); + size = size + 1; + } + + if (!size) { + dev_err(dev, "Adjusted size 0x%llx invalid\n", size); + return; + } + dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset); + } + + dev->dma_pfn_offset = offset; + + /* + * Limit coherent and dma mask based on size and default mask + * set by the driver. + */ + dev->coherent_dma_mask = min(dev->coherent_dma_mask, + DMA_BIT_MASK(ilog2(dma_addr + size))); + *dev->dma_mask = min((*dev->dma_mask), + DMA_BIT_MASK(ilog2(dma_addr + size))); + + coherent = of_dma_is_coherent(np); + dev_dbg(dev, "device is%sdma coherent\n", + coherent ? " " : " not "); + + iommu = of_iommu_configure(dev, np); + dev_dbg(dev, "device is%sbehind an iommu\n", + iommu ? " " : " not "); + + arch_setup_dma_ops(dev, dma_addr, size, iommu, coherent); +} +EXPORT_SYMBOL_GPL(of_dma_configure); + int of_device_register(struct platform_device *pdev) { device_initialize(&pdev->dev); diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 0d77658..1a79806 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -290,7 +290,7 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar struct device_node *p; const __be32 *intspec, *tmp, *addr; u32 intsize, intlen; - int i, res = -EINVAL; + int i, res; pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index); @@ -323,15 +323,19 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar /* Get size of interrupt specifier */ tmp = of_get_property(p, "#interrupt-cells", NULL); - if (tmp == NULL) + if (tmp == NULL) { + res = -EINVAL; goto out; + } intsize = be32_to_cpu(*tmp); pr_debug(" intsize=%d intlen=%d\n", intsize, intlen); /* Check index */ - if ((index + 1) * intsize > intlen) + if ((index + 1) * intsize > intlen) { + res = -EINVAL; goto out; + } /* Copy intspec into irq structure */ intspec += index * intsize; diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index 62426d8..5751dc5 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -2,6 +2,7 @@ #include <linux/export.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/of_pci.h> #include <linux/slab.h> @@ -116,6 +117,26 @@ int of_get_pci_domain_nr(struct device_node *node) } EXPORT_SYMBOL_GPL(of_get_pci_domain_nr); +/** + * of_pci_dma_configure - Setup DMA configuration + * @dev: ptr to pci_dev struct of the PCI device + * + * Function to update PCI devices's DMA configuration using the same + * info from the OF node of host bridge's parent (if any). + */ +void of_pci_dma_configure(struct pci_dev *pci_dev) +{ + struct device *dev = &pci_dev->dev; + struct device *bridge = pci_get_host_bridge_device(pci_dev); + + if (!bridge->parent) + return; + + of_dma_configure(dev, bridge->parent->of_node); + pci_put_host_bridge_device(bridge); +} +EXPORT_SYMBOL_GPL(of_pci_dma_configure); + #if defined(CONFIG_OF_ADDRESS) /** * of_pci_get_host_bridge_resources - Parse PCI host bridge resources from DT diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 352b4f2..dee9270 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -19,6 +19,7 @@ #include <linux/string.h> #include <linux/slab.h> #include <linux/err.h> +#include <linux/idr.h> #include "of_private.h" @@ -85,7 +86,7 @@ static int of_overlay_apply_single_device_node(struct of_overlay *ov, struct device_node *target, struct device_node *child) { const char *cname; - struct device_node *tchild, *grandchild; + struct device_node *tchild; int ret = 0; cname = kbasename(child->full_name); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index b189733..a01f57c 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -19,7 +19,6 @@ #include <linux/slab.h> #include <linux/of_address.h> #include <linux/of_device.h> -#include <linux/of_iommu.h> #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> @@ -150,59 +149,6 @@ struct platform_device *of_device_alloc(struct device_node *np, } EXPORT_SYMBOL(of_device_alloc); -/** - * of_dma_configure - Setup DMA configuration - * @dev: Device to apply DMA configuration - * - * Try to get devices's DMA configuration from DT and update it - * accordingly. - * - * In case if platform code need to use own special DMA configuration,it - * can use Platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE event - * to fix up DMA configuration. - */ -static void of_dma_configure(struct device *dev) -{ - u64 dma_addr, paddr, size; - int ret; - bool coherent; - unsigned long offset; - struct iommu_ops *iommu; - - /* - * Set default dma-mask to 32 bit. Drivers are expected to setup - * the correct supported dma_mask. - */ - dev->coherent_dma_mask = DMA_BIT_MASK(32); - - /* - * Set it to coherent_dma_mask by default if the architecture - * code has not set it. - */ - if (!dev->dma_mask) - dev->dma_mask = &dev->coherent_dma_mask; - - ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size); - if (ret < 0) { - dma_addr = offset = 0; - size = dev->coherent_dma_mask; - } else { - offset = PFN_DOWN(paddr - dma_addr); - dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset); - } - dev->dma_pfn_offset = offset; - - coherent = of_dma_is_coherent(dev->of_node); - dev_dbg(dev, "device is%sdma coherent\n", - coherent ? " " : " not "); - - iommu = of_iommu_configure(dev); - dev_dbg(dev, "device is%sbehind an iommu\n", - iommu ? " " : " not "); - - arch_setup_dma_ops(dev, dma_addr, size, iommu, coherent); -} - static void of_dma_deconfigure(struct device *dev) { arch_teardown_dma_ops(dev); @@ -236,7 +182,7 @@ static struct platform_device *of_platform_device_create_pdata( dev->dev.bus = &platform_bus_type; dev->dev.platform_data = platform_data; - of_dma_configure(&dev->dev); + of_dma_configure(&dev->dev, dev->dev.of_node); if (of_device_add(dev) != 0) { of_dma_deconfigure(&dev->dev); @@ -299,7 +245,7 @@ static struct amba_device *of_amba_device_create(struct device_node *node, dev_set_name(&dev->dev, "%s", bus_id); else of_device_make_bus_id(&dev->dev); - of_dma_configure(&dev->dev); + of_dma_configure(&dev->dev, dev->dev.of_node); /* Allow the HW Peripheral ID to be overridden */ prop = of_get_property(node, "arm,primecell-periphid", NULL); diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 0cf9a23..52c45c7 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -92,6 +92,16 @@ static void __init of_selftest_find_node_by_name(void) "option path test failed\n"); of_node_put(np); + np = of_find_node_opts_by_path("/testcase-data:test/option", &options); + selftest(np && !strcmp("test/option", options), + "option path test, subcase #1 failed\n"); + of_node_put(np); + + np = of_find_node_opts_by_path("/testcase-data/testcase-device1:test/option", &options); + selftest(np && !strcmp("test/option", options), + "option path test, subcase #2 failed\n"); + of_node_put(np); + np = of_find_node_opts_by_path("/testcase-data:testoption", NULL); selftest(np, "NULL option path test failed\n"); of_node_put(np); @@ -102,6 +112,12 @@ static void __init of_selftest_find_node_by_name(void) "option alias path test failed\n"); of_node_put(np); + np = of_find_node_opts_by_path("testcase-alias:test/alias/option", + &options); + selftest(np && !strcmp("test/alias/option", options), + "option alias path test, subcase #1 failed\n"); + of_node_put(np); + np = of_find_node_opts_by_path("testcase-alias:testaliasoption", NULL); selftest(np, "NULL option alias path test failed\n"); of_node_put(np); @@ -378,9 +394,9 @@ static void __init of_selftest_property_string(void) rc = of_property_match_string(np, "phandle-list-names", "first"); selftest(rc == 0, "first expected:0 got:%i\n", rc); rc = of_property_match_string(np, "phandle-list-names", "second"); - selftest(rc == 1, "second expected:0 got:%i\n", rc); + selftest(rc == 1, "second expected:1 got:%i\n", rc); rc = of_property_match_string(np, "phandle-list-names", "third"); - selftest(rc == 2, "third expected:0 got:%i\n", rc); + selftest(rc == 2, "third expected:2 got:%i\n", rc); rc = of_property_match_string(np, "phandle-list-names", "fourth"); selftest(rc == -ENODATA, "unmatched string; rc=%i\n", rc); rc = of_property_match_string(np, "missing-property", "blah"); @@ -478,7 +494,6 @@ static void __init of_selftest_changeset(void) struct device_node *n1, *n2, *n21, *nremove, *parent, *np; struct of_changeset chgset; - of_changeset_init(&chgset); n1 = __of_node_dup(NULL, "/testcase-data/changeset/n1"); selftest(n1, "testcase setup failure\n"); n2 = __of_node_dup(NULL, "/testcase-data/changeset/n2"); @@ -979,7 +994,7 @@ static int of_path_platform_device_exists(const char *path) return pdev != NULL; } -#if IS_ENABLED(CONFIG_I2C) +#if IS_BUILTIN(CONFIG_I2C) /* get the i2c client device instantiated at the path */ static struct i2c_client *of_path_to_i2c_client(const char *path) @@ -1445,7 +1460,7 @@ static void of_selftest_overlay_11(void) return; } -#if IS_ENABLED(CONFIG_I2C) && IS_ENABLED(CONFIG_OF_OVERLAY) +#if IS_BUILTIN(CONFIG_I2C) && IS_ENABLED(CONFIG_OF_OVERLAY) struct selftest_i2c_bus_data { struct platform_device *pdev; @@ -1584,7 +1599,7 @@ static struct i2c_driver selftest_i2c_dev_driver = { .id_table = selftest_i2c_dev_id, }; -#if IS_ENABLED(CONFIG_I2C_MUX) +#if IS_BUILTIN(CONFIG_I2C_MUX) struct selftest_i2c_mux_data { int nchans; @@ -1695,7 +1710,7 @@ static int of_selftest_overlay_i2c_init(void) "could not register selftest i2c bus driver\n")) return ret; -#if IS_ENABLED(CONFIG_I2C_MUX) +#if IS_BUILTIN(CONFIG_I2C_MUX) ret = i2c_add_driver(&selftest_i2c_mux_driver); if (selftest(ret == 0, "could not register selftest i2c mux driver\n")) @@ -1707,7 +1722,7 @@ static int of_selftest_overlay_i2c_init(void) static void of_selftest_overlay_i2c_cleanup(void) { -#if IS_ENABLED(CONFIG_I2C_MUX) +#if IS_BUILTIN(CONFIG_I2C_MUX) i2c_del_driver(&selftest_i2c_mux_driver); #endif platform_driver_unregister(&selftest_i2c_bus_driver); @@ -1814,7 +1829,7 @@ static void __init of_selftest_overlay(void) of_selftest_overlay_10(); of_selftest_overlay_11(); -#if IS_ENABLED(CONFIG_I2C) +#if IS_BUILTIN(CONFIG_I2C) if (selftest(of_selftest_overlay_i2c_init() == 0, "i2c init failed\n")) goto out; diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c index 39b2dbe..5f4a2e0 100644 --- a/drivers/pci/host-bridge.c +++ b/drivers/pci/host-bridge.c @@ -16,13 +16,27 @@ static struct pci_bus *find_pci_root_bus(struct pci_bus *bus) return bus; } -static struct pci_host_bridge *find_pci_host_bridge(struct pci_bus *bus) +struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus) { struct pci_bus *root_bus = find_pci_root_bus(bus); return to_pci_host_bridge(root_bus->bridge); } +struct device *pci_get_host_bridge_device(struct pci_dev *dev) +{ + struct pci_bus *root_bus = find_pci_root_bus(dev->bus); + struct device *bridge = root_bus->bridge; + + kobject_get(&bridge->kobj); + return bridge; +} + +void pci_put_host_bridge_device(struct device *dev) +{ + kobject_put(&dev->kobj); +} + void pci_set_host_bridge_release(struct pci_host_bridge *bridge, void (*release_fn)(struct pci_host_bridge *), void *release_data) @@ -34,7 +48,7 @@ void pci_set_host_bridge_release(struct pci_host_bridge *bridge, void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region, struct resource *res) { - struct pci_host_bridge *bridge = find_pci_host_bridge(bus); + struct pci_host_bridge *bridge = pci_find_host_bridge(bus); struct resource_entry *window; resource_size_t offset = 0; @@ -59,7 +73,7 @@ static bool region_contains(struct pci_bus_region *region1, void pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res, struct pci_bus_region *region) { - struct pci_host_bridge *bridge = find_pci_host_bridge(bus); + struct pci_host_bridge *bridge = pci_find_host_bridge(bus); struct resource_entry *window; resource_size_t offset = 0; diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 7b892a9..1dfb567 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -106,4 +106,23 @@ config PCI_VERSATILE bool "ARM Versatile PB PCI controller" depends on ARCH_VERSATILE +config PCIE_IPROC + tristate "Broadcom iProc PCIe controller" + depends on OF && ARM + default n + help + This enables the iProc PCIe core controller support for Broadcom's + iProc family of SoCs. An appropriate bus interface driver also needs + to be enabled + +config PCIE_IPROC_PLATFORM + tristate "Broadcom iProc PCIe platform bus driver" + depends on ARCH_BCM_IPROC || (ARM && COMPILE_TEST) + depends on OF + select PCIE_IPROC + default ARCH_BCM_IPROC + help + Say Y here if you want to use the Broadcom iProc PCIe controller + through the generic platform bus interface + endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index e61d91c..f733b4e 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -13,3 +13,5 @@ obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o obj-$(CONFIG_PCI_XGENE) += pci-xgene.o obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o +obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o +obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c index d202b37..c139237 100644 --- a/drivers/pci/host/pci-exynos.c +++ b/drivers/pci/host/pci-exynos.c @@ -396,7 +396,7 @@ static void exynos_pcie_enable_irq_pulse(struct pcie_port *pp) /* enable INTX interrupt */ val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT | - IRQ_INTC_ASSERT | IRQ_INTD_ASSERT, + IRQ_INTC_ASSERT | IRQ_INTD_ASSERT; exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_EN_PULSE); } diff --git a/drivers/pci/host/pci-keystone-dw.c b/drivers/pci/host/pci-keystone-dw.c index 66d8ea4..f34892e 100644 --- a/drivers/pci/host/pci-keystone-dw.c +++ b/drivers/pci/host/pci-keystone-dw.c @@ -496,11 +496,12 @@ int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie, /* Index 1 is the application reg. space address */ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - ks_pcie->app = *res; ks_pcie->va_app_base = devm_ioremap_resource(pp->dev, res); if (IS_ERR(ks_pcie->va_app_base)) return PTR_ERR(ks_pcie->va_app_base); + ks_pcie->app = *res; + /* Create legacy IRQ domain */ ks_pcie->legacy_irq_domain = irq_domain_add_linear(ks_pcie->legacy_intc_np, diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci-layerscape.c index 68c9e5e..4a6e62f 100644 --- a/drivers/pci/host/pci-layerscape.c +++ b/drivers/pci/host/pci-layerscape.c @@ -127,14 +127,11 @@ static int __init ls_pcie_probe(struct platform_device *pdev) pcie->dev = &pdev->dev; dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - if (!dbi_base) { - dev_err(&pdev->dev, "missing *regs* space\n"); - return -ENODEV; - } - pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base); - if (IS_ERR(pcie->dbi)) + if (IS_ERR(pcie->dbi)) { + dev_err(&pdev->dev, "missing *regs* space\n"); return PTR_ERR(pcie->dbi); + } pcie->scfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "fsl,pcie-scfg"); diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 1309cfb..1ab8635 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -129,6 +129,7 @@ struct mvebu_pcie_port { size_t memwin_size; phys_addr_t iowin_base; size_t iowin_size; + u32 saved_pcie_stat; }; static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg) @@ -899,6 +900,35 @@ static void mvebu_pcie_msi_enable(struct mvebu_pcie *pcie) pcie->msi->dev = &pcie->pdev->dev; } +static int mvebu_pcie_suspend(struct device *dev) +{ + struct mvebu_pcie *pcie; + int i; + + pcie = dev_get_drvdata(dev); + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = pcie->ports + i; + port->saved_pcie_stat = mvebu_readl(port, PCIE_STAT_OFF); + } + + return 0; +} + +static int mvebu_pcie_resume(struct device *dev) +{ + struct mvebu_pcie *pcie; + int i; + + pcie = dev_get_drvdata(dev); + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = pcie->ports + i; + mvebu_writel(port, port->saved_pcie_stat, PCIE_STAT_OFF); + mvebu_pcie_setup_hw(port); + } + + return 0; +} + static int mvebu_pcie_probe(struct platform_device *pdev) { struct mvebu_pcie *pcie; @@ -1056,6 +1086,8 @@ static int mvebu_pcie_probe(struct platform_device *pdev) mvebu_pcie_msi_enable(pcie); mvebu_pcie_enable(pcie); + platform_set_drvdata(pdev, pcie); + return 0; } @@ -1068,12 +1100,18 @@ static const struct of_device_id mvebu_pcie_of_match_table[] = { }; MODULE_DEVICE_TABLE(of, mvebu_pcie_of_match_table); +static struct dev_pm_ops mvebu_pcie_pm_ops = { + .suspend_noirq = mvebu_pcie_suspend, + .resume_noirq = mvebu_pcie_resume, +}; + static struct platform_driver mvebu_pcie_driver = { .driver = { .name = "mvebu-pcie", .of_match_table = mvebu_pcie_of_match_table, /* driver unloading/unbinding currently not supported */ .suppress_bind_attrs = true, + .pm = &mvebu_pcie_pm_ops, }, .probe = mvebu_pcie_probe, }; diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index dd6b84e..367e28f 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -301,6 +301,9 @@ static int rcar_pci_probe(struct platform_device *pdev) if (!mem_res || !mem_res->start) return -ENODEV; + if (mem_res->start & 0xFFFF) + return -EINVAL; + priv = devm_kzalloc(&pdev->dev, sizeof(struct rcar_pci_priv), GFP_KERNEL); if (!priv) diff --git a/drivers/pci/host/pci-versatile.c b/drivers/pci/host/pci-versatile.c index 1ec694a..0863d9c 100644 --- a/drivers/pci/host/pci-versatile.c +++ b/drivers/pci/host/pci-versatile.c @@ -80,7 +80,7 @@ static int versatile_pci_parse_request_of_pci_ranges(struct device *dev, if (err) return err; - resource_list_for_each_entry(win, res, list) { + resource_list_for_each_entry(win, res) { struct resource *parent, *res = win->res; switch (resource_type(res)) { @@ -138,19 +138,19 @@ static int versatile_pci_probe(struct platform_device *pdev) LIST_HEAD(pci_res); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; versatile_pci_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(versatile_pci_base)) + return PTR_ERR(versatile_pci_base); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) - return -ENODEV; versatile_cfg_base[0] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(versatile_cfg_base[0])) + return PTR_ERR(versatile_cfg_base[0]); res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - if (!res) - return -ENODEV; versatile_cfg_base[1] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(versatile_cfg_base[1])) + return PTR_ERR(versatile_cfg_base[1]); ret = versatile_pci_parse_request_of_pci_ranges(&pdev->dev, &pci_res); if (ret) @@ -214,6 +214,7 @@ static int versatile_pci_probe(struct platform_device *pdev) pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); pci_assign_unassigned_bus_resources(bus); + pci_bus_add_devices(bus); return 0; } diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c index aab5547..ee082c0 100644 --- a/drivers/pci/host/pci-xgene.c +++ b/drivers/pci/host/pci-xgene.c @@ -127,7 +127,7 @@ static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset) return false; } -static int xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, +static void __iomem *xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, int offset) { struct xgene_pcie_port *port = bus->sysdata; @@ -137,7 +137,7 @@ static int xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, return NULL; xgene_pcie_set_rtdid_reg(bus, devfn); - return xgene_pcie_get_cfg_base(bus); + return xgene_pcie_get_cfg_base(bus) + offset; } static struct pci_ops xgene_pcie_ops = { diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 1f4ea6f..2e9f84f 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -342,7 +342,7 @@ static const struct irq_domain_ops msi_domain_ops = { .map = dw_pcie_msi_map, }; -int __init dw_pcie_host_init(struct pcie_port *pp) +int dw_pcie_host_init(struct pcie_port *pp) { struct device_node *np = pp->dev->of_node; struct platform_device *pdev = to_platform_device(pp->dev); diff --git a/drivers/pci/host/pcie-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c new file mode 100644 index 0000000..afad6c2 --- /dev/null +++ b/drivers/pci/host/pcie-iproc-platform.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * 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 version 2. + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/of_address.h> +#include <linux/of_pci.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/phy/phy.h> + +#include "pcie-iproc.h" + +static int iproc_pcie_pltfm_probe(struct platform_device *pdev) +{ + struct iproc_pcie *pcie; + struct device_node *np = pdev->dev.of_node; + struct resource reg; + resource_size_t iobase = 0; + LIST_HEAD(res); + int ret; + + pcie = devm_kzalloc(&pdev->dev, sizeof(struct iproc_pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pcie->dev = &pdev->dev; + platform_set_drvdata(pdev, pcie); + + ret = of_address_to_resource(np, 0, ®); + if (ret < 0) { + dev_err(pcie->dev, "unable to obtain controller resources\n"); + return ret; + } + + pcie->base = devm_ioremap(pcie->dev, reg.start, resource_size(®)); + if (!pcie->base) { + dev_err(pcie->dev, "unable to map controller registers\n"); + return -ENOMEM; + } + + /* PHY use is optional */ + pcie->phy = devm_phy_get(&pdev->dev, "pcie-phy"); + if (IS_ERR(pcie->phy)) { + if (PTR_ERR(pcie->phy) == -EPROBE_DEFER) + return -EPROBE_DEFER; + pcie->phy = NULL; + } + + ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &iobase); + if (ret) { + dev_err(pcie->dev, + "unable to get PCI host bridge resources\n"); + return ret; + } + + pcie->resources = &res; + + ret = iproc_pcie_setup(pcie); + if (ret) { + dev_err(pcie->dev, "PCIe controller setup failed\n"); + return ret; + } + + return 0; +} + +static int iproc_pcie_pltfm_remove(struct platform_device *pdev) +{ + struct iproc_pcie *pcie = platform_get_drvdata(pdev); + + return iproc_pcie_remove(pcie); +} + +static const struct of_device_id iproc_pcie_of_match_table[] = { + { .compatible = "brcm,iproc-pcie", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table); + +static struct platform_driver iproc_pcie_pltfm_driver = { + .driver = { + .name = "iproc-pcie", + .of_match_table = of_match_ptr(iproc_pcie_of_match_table), + }, + .probe = iproc_pcie_pltfm_probe, + .remove = iproc_pcie_pltfm_remove, +}; +module_platform_driver(iproc_pcie_pltfm_driver); + +MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom iPROC PCIe platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c new file mode 100644 index 0000000..329e1b5 --- /dev/null +++ b/drivers/pci/host/pcie-iproc.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2014 Hauke Mehrtens <hauke@hauke-m.de> + * Copyright (C) 2015 Broadcom Corporatcommon ion + * + * 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 version 2. + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/msi.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/mbus.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/of_address.h> +#include <linux/of_pci.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/phy/phy.h> + +#include "pcie-iproc.h" + +#define CLK_CONTROL_OFFSET 0x000 +#define EP_MODE_SURVIVE_PERST_SHIFT 1 +#define EP_MODE_SURVIVE_PERST BIT(EP_MODE_SURVIVE_PERST_SHIFT) +#define RC_PCIE_RST_OUTPUT_SHIFT 0 +#define RC_PCIE_RST_OUTPUT BIT(RC_PCIE_RST_OUTPUT_SHIFT) + +#define CFG_IND_ADDR_OFFSET 0x120 +#define CFG_IND_ADDR_MASK 0x00001ffc + +#define CFG_IND_DATA_OFFSET 0x124 + +#define CFG_ADDR_OFFSET 0x1f8 +#define CFG_ADDR_BUS_NUM_SHIFT 20 +#define CFG_ADDR_BUS_NUM_MASK 0x0ff00000 +#define CFG_ADDR_DEV_NUM_SHIFT 15 +#define CFG_ADDR_DEV_NUM_MASK 0x000f8000 +#define CFG_ADDR_FUNC_NUM_SHIFT 12 +#define CFG_ADDR_FUNC_NUM_MASK 0x00007000 +#define CFG_ADDR_REG_NUM_SHIFT 2 +#define CFG_ADDR_REG_NUM_MASK 0x00000ffc +#define CFG_ADDR_CFG_TYPE_SHIFT 0 +#define CFG_ADDR_CFG_TYPE_MASK 0x00000003 + +#define CFG_DATA_OFFSET 0x1fc + +#define SYS_RC_INTX_EN 0x330 +#define SYS_RC_INTX_MASK 0xf + +static inline struct iproc_pcie *sys_to_pcie(struct pci_sys_data *sys) +{ + return sys->private_data; +} + +/** + * Note access to the configuration registers are protected at the higher layer + * by 'pci_lock' in drivers/pci/access.c + */ +static void __iomem *iproc_pcie_map_cfg_bus(struct pci_bus *bus, + unsigned int devfn, + int where) +{ + struct pci_sys_data *sys = bus->sysdata; + struct iproc_pcie *pcie = sys_to_pcie(sys); + unsigned slot = PCI_SLOT(devfn); + unsigned fn = PCI_FUNC(devfn); + unsigned busno = bus->number; + u32 val; + + /* root complex access */ + if (busno == 0) { + if (slot >= 1) + return NULL; + writel(where & CFG_IND_ADDR_MASK, + pcie->base + CFG_IND_ADDR_OFFSET); + return (pcie->base + CFG_IND_DATA_OFFSET); + } + + if (fn > 1) + return NULL; + + /* EP device access */ + val = (busno << CFG_ADDR_BUS_NUM_SHIFT) | + (slot << CFG_ADDR_DEV_NUM_SHIFT) | + (fn << CFG_ADDR_FUNC_NUM_SHIFT) | + (where & CFG_ADDR_REG_NUM_MASK) | + (1 & CFG_ADDR_CFG_TYPE_MASK); + writel(val, pcie->base + CFG_ADDR_OFFSET); + + return (pcie->base + CFG_DATA_OFFSET); +} + +static struct pci_ops iproc_pcie_ops = { + .map_bus = iproc_pcie_map_cfg_bus, + .read = pci_generic_config_read32, + .write = pci_generic_config_write32, +}; + +static void iproc_pcie_reset(struct iproc_pcie *pcie) +{ + u32 val; + + /* + * Configure the PCIe controller as root complex and send a downstream + * reset + */ + val = EP_MODE_SURVIVE_PERST | RC_PCIE_RST_OUTPUT; + writel(val, pcie->base + CLK_CONTROL_OFFSET); + udelay(250); + val &= ~EP_MODE_SURVIVE_PERST; + writel(val, pcie->base + CLK_CONTROL_OFFSET); + msleep(250); +} + +static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus) +{ + u8 hdr_type; + u32 link_ctrl; + u16 pos, link_status; + int link_is_active = 0; + + /* make sure we are not in EP mode */ + pci_bus_read_config_byte(bus, 0, PCI_HEADER_TYPE, &hdr_type); + if ((hdr_type & 0x7f) != PCI_HEADER_TYPE_BRIDGE) { + dev_err(pcie->dev, "in EP mode, hdr=%#02x\n", hdr_type); + return -EFAULT; + } + + /* force class to PCI_CLASS_BRIDGE_PCI (0x0604) */ + pci_bus_write_config_word(bus, 0, PCI_CLASS_DEVICE, + PCI_CLASS_BRIDGE_PCI); + + /* check link status to see if link is active */ + pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP); + pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA, &link_status); + if (link_status & PCI_EXP_LNKSTA_NLW) + link_is_active = 1; + + if (!link_is_active) { + /* try GEN 1 link speed */ +#define PCI_LINK_STATUS_CTRL_2_OFFSET 0x0dc +#define PCI_TARGET_LINK_SPEED_MASK 0xf +#define PCI_TARGET_LINK_SPEED_GEN2 0x2 +#define PCI_TARGET_LINK_SPEED_GEN1 0x1 + pci_bus_read_config_dword(bus, 0, + PCI_LINK_STATUS_CTRL_2_OFFSET, + &link_ctrl); + if ((link_ctrl & PCI_TARGET_LINK_SPEED_MASK) == + PCI_TARGET_LINK_SPEED_GEN2) { + link_ctrl &= ~PCI_TARGET_LINK_SPEED_MASK; + link_ctrl |= PCI_TARGET_LINK_SPEED_GEN1; + pci_bus_write_config_dword(bus, 0, + PCI_LINK_STATUS_CTRL_2_OFFSET, + link_ctrl); + msleep(100); + + pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP); + pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA, + &link_status); + if (link_status & PCI_EXP_LNKSTA_NLW) + link_is_active = 1; + } + } + + dev_info(pcie->dev, "link: %s\n", link_is_active ? "UP" : "DOWN"); + + return link_is_active ? 0 : -ENODEV; +} + +static void iproc_pcie_enable(struct iproc_pcie *pcie) +{ + writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN); +} + +int iproc_pcie_setup(struct iproc_pcie *pcie) +{ + int ret; + struct pci_bus *bus; + + if (!pcie || !pcie->dev || !pcie->base) + return -EINVAL; + + if (pcie->phy) { + ret = phy_init(pcie->phy); + if (ret) { + dev_err(pcie->dev, "unable to initialize PCIe PHY\n"); + return ret; + } + + ret = phy_power_on(pcie->phy); + if (ret) { + dev_err(pcie->dev, "unable to power on PCIe PHY\n"); + goto err_exit_phy; + } + + } + + iproc_pcie_reset(pcie); + + pcie->sysdata.private_data = pcie; + + bus = pci_create_root_bus(pcie->dev, 0, &iproc_pcie_ops, + &pcie->sysdata, pcie->resources); + if (!bus) { + dev_err(pcie->dev, "unable to create PCI root bus\n"); + ret = -ENOMEM; + goto err_power_off_phy; + } + pcie->root_bus = bus; + + ret = iproc_pcie_check_link(pcie, bus); + if (ret) { + dev_err(pcie->dev, "no PCIe EP device detected\n"); + goto err_rm_root_bus; + } + + iproc_pcie_enable(pcie); + + pci_scan_child_bus(bus); + pci_assign_unassigned_bus_resources(bus); + pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); + pci_bus_add_devices(bus); + + return 0; + +err_rm_root_bus: + pci_stop_root_bus(bus); + pci_remove_root_bus(bus); + +err_power_off_phy: + if (pcie->phy) + phy_power_off(pcie->phy); +err_exit_phy: + if (pcie->phy) + phy_exit(pcie->phy); + + return ret; +} +EXPORT_SYMBOL(iproc_pcie_setup); + +int iproc_pcie_remove(struct iproc_pcie *pcie) +{ + pci_stop_root_bus(pcie->root_bus); + pci_remove_root_bus(pcie->root_bus); + + if (pcie->phy) { + phy_power_off(pcie->phy); + phy_exit(pcie->phy); + } + + return 0; +} +EXPORT_SYMBOL(iproc_pcie_remove); + +MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom iPROC PCIe common driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h new file mode 100644 index 0000000..e28075e --- /dev/null +++ b/drivers/pci/host/pcie-iproc.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014-2015 Broadcom Corporation + * + * 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 version 2. + * + * 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. + */ + +#ifndef _PCIE_IPROC_H +#define _PCIE_IPROC_H + +#define IPROC_PCIE_MAX_NUM_IRQS 6 + +/** + * iProc PCIe device + * @dev: pointer to device data structure + * @base: PCIe host controller I/O register base + * @resources: linked list of all PCI resources + * @sysdata: Per PCI controller data + * @root_bus: pointer to root bus + * @phy: optional PHY device that controls the Serdes + * @irqs: interrupt IDs + */ +struct iproc_pcie { + struct device *dev; + void __iomem *base; + struct list_head *resources; + struct pci_sys_data sysdata; + struct pci_bus *root_bus; + struct phy *phy; + int irqs[IPROC_PCIE_MAX_NUM_IRQS]; +}; + +int iproc_pcie_setup(struct iproc_pcie *pcie); +int iproc_pcie_remove(struct iproc_pcie *pcie); + +#endif /* _PCIE_IPROC_H */ diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index c57bd0a..c086210 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -64,8 +64,8 @@ #define LAR_ENABLE (1 << 1) /* PCIe address reg & mask */ -#define PCIEPARL(x) (0x03400 + ((x) * 0x20)) -#define PCIEPARH(x) (0x03404 + ((x) * 0x20)) +#define PCIEPALR(x) (0x03400 + ((x) * 0x20)) +#define PCIEPAUR(x) (0x03404 + ((x) * 0x20)) #define PCIEPAMR(x) (0x03408 + ((x) * 0x20)) #define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20)) #define PAR_ENABLE (1 << 31) @@ -341,8 +341,9 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie) else res_start = res->start; - rcar_pci_write_reg(pcie, upper_32_bits(res_start), PCIEPARH(win)); - rcar_pci_write_reg(pcie, lower_32_bits(res_start), PCIEPARL(win)); + rcar_pci_write_reg(pcie, upper_32_bits(res_start), PCIEPAUR(win)); + rcar_pci_write_reg(pcie, lower_32_bits(res_start) & ~0x7F, + PCIEPALR(win)); /* First resource is for IO */ mask = PAR_ENABLE; @@ -501,7 +502,7 @@ static int rcar_pcie_hw_init(struct rcar_pcie *pcie) /* Enable MSI */ if (IS_ENABLED(CONFIG_PCI_MSI)) - rcar_pci_write_reg(pcie, 0x101f0000, PCIEMSITXR); + rcar_pci_write_reg(pcie, 0x801f0000, PCIEMSITXR); /* Finish initialization - establish a PCI Express link */ rcar_pci_write_reg(pcie, CFINIT, PCIETCTLR); diff --git a/drivers/pci/host/pcie-spear13xx.c b/drivers/pci/host/pcie-spear13xx.c index 866465f..020d788 100644 --- a/drivers/pci/host/pcie-spear13xx.c +++ b/drivers/pci/host/pcie-spear13xx.c @@ -269,7 +269,7 @@ static struct pcie_host_ops spear13xx_pcie_host_ops = { .host_init = spear13xx_pcie_host_init, }; -static int __init spear13xx_add_pcie_port(struct pcie_port *pp, +static int spear13xx_add_pcie_port(struct pcie_port *pp, struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -299,7 +299,7 @@ static int __init spear13xx_add_pcie_port(struct pcie_port *pp, return 0; } -static int __init spear13xx_pcie_probe(struct platform_device *pdev) +static int spear13xx_pcie_probe(struct platform_device *pdev) { struct spear13xx_pcie *spear13xx_pcie; struct pcie_port *pp; @@ -370,7 +370,7 @@ static const struct of_device_id spear13xx_pcie_of_match[] = { }; MODULE_DEVICE_TABLE(of, spear13xx_pcie_of_match); -static struct platform_driver spear13xx_pcie_driver __initdata = { +static struct platform_driver spear13xx_pcie_driver = { .probe = spear13xx_pcie_probe, .driver = { .name = "spear-pcie", diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c index 7d48eca..788db48 100644 --- a/drivers/pci/hotplug/cpci_hotplug_pci.c +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c @@ -286,11 +286,12 @@ int cpci_configure_slot(struct slot *slot) } parent = slot->dev->bus; - list_for_each_entry(dev, &parent->devices, bus_list) + list_for_each_entry(dev, &parent->devices, bus_list) { if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn)) continue; if (pci_is_bridge(dev)) pci_hp_add_bridge(dev); + } pci_assign_unassigned_bridge_resources(parent->self); diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index 96c5c72..1530247 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -738,7 +738,7 @@ static void ibm_unconfigure_device(struct pci_func *func) */ static u8 bus_structure_fixup(u8 busno) { - struct pci_bus *bus; + struct pci_bus *bus, *b; struct pci_dev *dev; u16 l; @@ -765,7 +765,11 @@ static u8 bus_structure_fixup(u8 busno) (l != 0x0000) && (l != 0xffff)) { debug("%s - Inside bus_structure_fixup()\n", __func__); - pci_scan_bus(busno, ibmphp_pci_bus->ops, NULL); + b = pci_scan_bus(busno, ibmphp_pci_bus->ops, NULL); + if (!b) + continue; + + pci_bus_add_devices(b); break; } } diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 4890639..6f6f175 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -18,6 +18,15 @@ #include <linux/pm_qos.h> #include "pci.h" +/* + * The UUID is defined in the PCI Firmware Specification available here: + * https://www.pcisig.com/members/downloads/pcifw_r3_1_13Dec10.pdf + */ +const u8 pci_acpi_dsm_uuid[] = { + 0xd0, 0x37, 0xc9, 0xe5, 0x53, 0x35, 0x7a, 0x4d, + 0x91, 0x17, 0xea, 0x4d, 0x19, 0xc3, 0x43, 0x4d +}; + phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle) { acpi_status status = AE_NOT_EXIST; @@ -248,6 +257,9 @@ int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp) acpi_handle handle, phandle; struct pci_bus *pbus; + if (acpi_pci_disabled) + return -ENODEV; + handle = NULL; for (pbus = dev->bus; pbus; pbus = pbus->parent) { handle = acpi_pci_get_bridge_handle(pbus); @@ -528,11 +540,32 @@ static struct pci_platform_pm_ops acpi_pci_platform_pm = { void acpi_pci_add_bus(struct pci_bus *bus) { + union acpi_object *obj; + struct pci_host_bridge *bridge; + if (acpi_pci_disabled || !bus->bridge) return; acpi_pci_slot_enumerate(bus); acpiphp_enumerate_slots(bus); + + /* + * For a host bridge, check its _DSM for function 8 and if + * that is available, mark it in pci_host_bridge. + */ + if (!pci_is_root_bus(bus)) + return; + + obj = acpi_evaluate_dsm(ACPI_HANDLE(bus->bridge), pci_acpi_dsm_uuid, 3, + RESET_DELAY_DSM, NULL); + if (!obj) + return; + + if (obj->type == ACPI_TYPE_INTEGER && obj->integer.value == 1) { + bridge = pci_find_host_bridge(bus); + bridge->ignore_reset_delay = 1; + } + ACPI_FREE(obj); } void acpi_pci_remove_bus(struct pci_bus *bus) @@ -558,6 +591,57 @@ static struct acpi_device *acpi_pci_find_companion(struct device *dev) check_children); } +/** + * pci_acpi_optimize_delay - optimize PCI D3 and D3cold delay from ACPI + * @pdev: the PCI device whose delay is to be updated + * @adev: the companion ACPI device of this PCI device + * + * Update the d3_delay and d3cold_delay of a PCI device from the ACPI _DSM + * control method of either the device itself or the PCI host bridge. + * + * Function 8, "Reset Delay," applies to the entire hierarchy below a PCI + * host bridge. If it returns one, the OS may assume that all devices in + * the hierarchy have already completed power-on reset delays. + * + * Function 9, "Device Readiness Durations," applies only to the object + * where it is located. It returns delay durations required after various + * events if the device requires less time than the spec requires. Delays + * from this function take precedence over the Reset Delay function. + * + * These _DSM functions are defined by the draft ECN of January 28, 2014, + * titled "ACPI additions for FW latency optimizations." + */ +static void pci_acpi_optimize_delay(struct pci_dev *pdev, + acpi_handle handle) +{ + struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus); + int value; + union acpi_object *obj, *elements; + + if (bridge->ignore_reset_delay) + pdev->d3cold_delay = 0; + + obj = acpi_evaluate_dsm(handle, pci_acpi_dsm_uuid, 3, + FUNCTION_DELAY_DSM, NULL); + if (!obj) + return; + + if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 5) { + elements = obj->package.elements; + if (elements[0].type == ACPI_TYPE_INTEGER) { + value = (int)elements[0].integer.value / 1000; + if (value < PCI_PM_D3COLD_WAIT) + pdev->d3cold_delay = value; + } + if (elements[3].type == ACPI_TYPE_INTEGER) { + value = (int)elements[3].integer.value / 1000; + if (value < PCI_PM_D3_WAIT) + pdev->d3_delay = value; + } + } + ACPI_FREE(obj); +} + static void pci_acpi_setup(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -566,6 +650,8 @@ static void pci_acpi_setup(struct device *dev) if (!adev) return; + pci_acpi_optimize_delay(pci_dev, adev->handle); + pci_acpi_add_pm_notifier(adev, pci_dev); if (!adev->wakeup.flags.valid) return; diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c index 2ab1b47..024b5c1 100644 --- a/drivers/pci/pci-label.c +++ b/drivers/pci/pci-label.c @@ -31,8 +31,6 @@ #include <linux/pci-acpi.h> #include "pci.h" -#define DEVICE_LABEL_DSM 0x07 - #ifdef CONFIG_DMI enum smbios_attr_enum { SMBIOS_ATTR_NONE = 0, @@ -148,11 +146,6 @@ static inline void pci_remove_smbiosname_file(struct pci_dev *pdev) #endif #ifdef CONFIG_ACPI -static const char device_label_dsm_uuid[] = { - 0xD0, 0x37, 0xC9, 0xE5, 0x53, 0x35, 0x7A, 0x4D, - 0x91, 0x17, 0xEA, 0x4D, 0x19, 0xC3, 0x43, 0x4D -}; - enum acpi_attr_enum { ACPI_ATTR_LABEL_SHOW, ACPI_ATTR_INDEX_SHOW, @@ -179,7 +172,7 @@ static int dsm_get_label(struct device *dev, char *buf, if (!handle) return -1; - obj = acpi_evaluate_dsm(handle, device_label_dsm_uuid, 0x2, + obj = acpi_evaluate_dsm(handle, pci_acpi_dsm_uuid, 0x2, DEVICE_LABEL_DSM, NULL); if (!obj) return -1; @@ -219,7 +212,7 @@ static bool device_has_dsm(struct device *dev) if (!handle) return false; - return !!acpi_check_dsm(handle, device_label_dsm_uuid, 0x2, + return !!acpi_check_dsm(handle, pci_acpi_dsm_uuid, 0x2, 1 << DEVICE_LABEL_DSM); } diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index aa012fb..312f23a 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -521,7 +521,8 @@ static ssize_t driver_override_store(struct device *dev, struct pci_dev *pdev = to_pci_dev(dev); char *driver_override, *old = pdev->driver_override, *cp; - if (count > PATH_MAX) + /* We need to keep extra room for a newline */ + if (count >= (PAGE_SIZE - 1)) return -EINVAL; driver_override = kstrndup(buf, count, GFP_KERNEL); @@ -549,7 +550,7 @@ static ssize_t driver_override_show(struct device *dev, { struct pci_dev *pdev = to_pci_dev(dev); - return sprintf(buf, "%s\n", pdev->driver_override); + return snprintf(buf, PAGE_SIZE, "%s\n", pdev->driver_override); } static DEVICE_ATTR_RW(driver_override); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 81f06e8..acc4b6e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -126,15 +126,16 @@ EXPORT_SYMBOL_GPL(pci_bus_max_busnr); #ifdef CONFIG_HAS_IOMEM void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar) { + struct resource *res = &pdev->resource[bar]; + /* * Make sure the BAR is actually a memory resource, not an IO resource */ - if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { - WARN_ON(1); + if (res->flags & IORESOURCE_UNSET || !(res->flags & IORESOURCE_MEM)) { + dev_warn(&pdev->dev, "can't ioremap BAR %d: %pR\n", bar, res); return NULL; } - return ioremap_nocache(pci_resource_start(pdev, bar), - pci_resource_len(pdev, bar)); + return ioremap_nocache(res->start, resource_size(res)); } EXPORT_SYMBOL_GPL(pci_ioremap_bar); #endif @@ -145,19 +146,22 @@ static int __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn, u8 pos, int cap, int *ttl) { u8 id; + u16 ent; + + pci_bus_read_config_byte(bus, devfn, pos, &pos); while ((*ttl)--) { - pci_bus_read_config_byte(bus, devfn, pos, &pos); if (pos < 0x40) break; pos &= ~3; - pci_bus_read_config_byte(bus, devfn, pos + PCI_CAP_LIST_ID, - &id); + pci_bus_read_config_word(bus, devfn, pos, &ent); + + id = ent & 0xff; if (id == 0xff) break; if (id == cap) return pos; - pos += PCI_CAP_LIST_NEXT; + pos = (ent >> 8); } return 0; } @@ -2492,6 +2496,7 @@ u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp) *pinp = pin; return PCI_SLOT(dev->devfn); } +EXPORT_SYMBOL_GPL(pci_common_swizzle); /** * pci_release_region - Release a PCI bar diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4091f82..d72f849 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -321,4 +321,6 @@ static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe) } #endif +struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus); + #endif /* DRIVERS_PCI_H */ diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index c6849d9..167fe41 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -132,16 +132,8 @@ static const char *aer_agent_string[] = { static void __print_tlp_header(struct pci_dev *dev, struct aer_header_log_regs *t) { - unsigned char *tlp = (unsigned char *)&t; - - dev_err(&dev->dev, " TLP Header:" - " %02x%02x%02x%02x %02x%02x%02x%02x" - " %02x%02x%02x%02x %02x%02x%02x%02x\n", - *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp, - *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4), - *(tlp + 11), *(tlp + 10), *(tlp + 9), - *(tlp + 8), *(tlp + 15), *(tlp + 14), - *(tlp + 13), *(tlp + 12)); + dev_err(&dev->dev, " TLP Header: %08x %08x %08x %08x\n", + t->dw0, t->dw1, t->dw2, t->dw3); } static void __aer_print_error(struct pci_dev *dev, diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 820740a..7d4fcdc 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -782,24 +782,6 @@ void pci_disable_link_state(struct pci_dev *pdev, int state) } EXPORT_SYMBOL(pci_disable_link_state); -void pcie_clear_aspm(struct pci_bus *bus) -{ - struct pci_dev *child; - - if (aspm_force) - return; - - /* - * Clear any ASPM setup that the firmware has carried out on this bus - */ - list_for_each_entry(child, &bus->devices, bus_list) { - __pci_disable_link_state(child, PCIE_LINK_STATE_L0S | - PCIE_LINK_STATE_L1 | - PCIE_LINK_STATE_CLKPM, - false, true); - } -} - static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) { int i; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 8d2f400..6675a7a 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -6,6 +6,7 @@ #include <linux/delay.h> #include <linux/init.h> #include <linux/pci.h> +#include <linux/of_pci.h> #include <linux/pci_hotplug.h> #include <linux/slab.h> #include <linux/module.h> @@ -1520,6 +1521,7 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) dev->dev.dma_mask = &dev->dma_mask; dev->dev.dma_parms = &dev->dma_parms; dev->dev.coherent_dma_mask = 0xffffffffull; + of_pci_dma_configure(dev); pci_set_dma_max_seg_size(dev, 65536); pci_set_dma_seg_boundary(dev, 0xffffffff); @@ -1993,6 +1995,7 @@ err_out: kfree(b); return NULL; } +EXPORT_SYMBOL_GPL(pci_create_root_bus); int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max) { @@ -2087,7 +2090,6 @@ struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, if (!found) pci_bus_update_busn_res_end(b, max); - pci_bus_add_devices(b); return b; } EXPORT_SYMBOL(pci_scan_root_bus); @@ -2123,7 +2125,6 @@ struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops, b = pci_create_root_bus(NULL, bus, ops, sysdata, &resources); if (b) { pci_scan_child_bus(b); - pci_bus_add_devices(b); } else { pci_free_resource_list(&resources); } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 85f247e..c6dc1df 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3182,7 +3182,7 @@ static void quirk_apple_wait_for_thunderbolt(struct pci_dev *dev) || nhi->subsystem_vendor != 0x2222 || nhi->subsystem_device != 0x1111) goto out; - dev_info(&dev->dev, "quirk: wating for thunderbolt to reestablish pci tunnels...\n"); + dev_info(&dev->dev, "quirk: waiting for thunderbolt to reestablish PCI tunnels...\n"); device_pm_wait_for_dev(&dev->dev, &nhi->dev); out: pci_dev_put(nhi); @@ -3822,6 +3822,38 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_INTEL, 0x154F, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_INTEL, 0x1551, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_INTEL, 0x1558, pci_quirk_mf_endpoint_acs }, + /* 82580 */ + { PCI_VENDOR_ID_INTEL, 0x1509, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x150E, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x150F, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1510, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1511, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1516, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1527, pci_quirk_mf_endpoint_acs }, + /* 82576 */ + { PCI_VENDOR_ID_INTEL, 0x10C9, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x10E6, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x10E7, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x10E8, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x150A, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x150D, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1518, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1526, pci_quirk_mf_endpoint_acs }, + /* 82575 */ + { PCI_VENDOR_ID_INTEL, 0x10A7, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x10A9, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x10D6, pci_quirk_mf_endpoint_acs }, + /* I350 */ + { PCI_VENDOR_ID_INTEL, 0x1521, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1522, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1523, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1524, pci_quirk_mf_endpoint_acs }, + /* 82571 (Quads omitted due to non-ACS switch) */ + { PCI_VENDOR_ID_INTEL, 0x105E, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x105F, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1060, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x10D9, pci_quirk_mf_endpoint_acs }, + /* Intel PCH root ports */ { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs }, { 0x19a2, 0x710, pci_quirk_mf_endpoint_acs }, /* Emulex BE3-R */ { 0x10df, 0x720, pci_quirk_mf_endpoint_acs }, /* Emulex Skyhawk-R */ diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 8bd76c9..8a280e9 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -139,6 +139,7 @@ void pci_stop_root_bus(struct pci_bus *bus) /* stop the host bridge */ device_release_driver(&host_bridge->dev); } +EXPORT_SYMBOL_GPL(pci_stop_root_bus); void pci_remove_root_bus(struct pci_bus *bus) { @@ -158,3 +159,4 @@ void pci_remove_root_bus(struct pci_bus *bus) /* remove the host bridge */ device_unregister(&host_bridge->dev); } +EXPORT_SYMBOL_GPL(pci_remove_root_bus); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index e3e17f3..8169597 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1750,3 +1750,4 @@ void pci_assign_unassigned_bus_resources(struct pci_bus *bus) __pci_bus_assign_resources(bus, &add_list, NULL); BUG_ON(!list_empty(&add_list)); } +EXPORT_SYMBOL_GPL(pci_assign_unassigned_bus_resources); diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c index 4e2d595..95c225b 100644 --- a/drivers/pci/setup-irq.c +++ b/drivers/pci/setup-irq.c @@ -65,3 +65,4 @@ void pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *), for_each_pci_dev(dev) pdev_fixup_irq(dev, swizzle, map_irq); } +EXPORT_SYMBOL_GPL(pci_fixup_irqs); diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index b7c3a5e..232f925 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -120,6 +120,7 @@ int pci_claim_resource(struct pci_dev *dev, int resource) if (!root) { dev_info(&dev->dev, "can't claim BAR %d %pR: no compatible bridge window\n", resource, res); + res->flags |= IORESOURCE_UNSET; return -EINVAL; } @@ -127,6 +128,7 @@ int pci_claim_resource(struct pci_dev *dev, int resource) if (conflict) { dev_info(&dev->dev, "can't claim BAR %d %pR: address conflict with %s %pR\n", resource, res, conflict->name, conflict); + res->flags |= IORESOURCE_UNSET; return -EBUSY; } diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 3bb4925..45f67c6 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -69,8 +69,7 @@ config YENTA tristate "CardBus yenta-compatible bridge support" depends on PCI select CARDBUS if !EXPERT - select PCCARD_NONSTATIC if PCMCIA != n && ISA - select PCCARD_PCI if PCMCIA !=n && !ISA + select PCCARD_NONSTATIC if PCMCIA != n ---help--- This option enables support for CardBus host bridges. Virtually all modern PCMCIA bridges are CardBus compatible. A "bridge" is @@ -110,8 +109,7 @@ config YENTA_TOSHIBA config PD6729 tristate "Cirrus PD6729 compatible bridge support" depends on PCMCIA && PCI - select PCCARD_NONSTATIC if PCMCIA != n && ISA - select PCCARD_PCI if PCMCIA !=n && !ISA + select PCCARD_NONSTATIC help This provides support for the Cirrus PD6729 PCI-to-PCMCIA bridge device, found in some older laptops and PCMCIA card readers. @@ -119,8 +117,7 @@ config PD6729 config I82092 tristate "i82092 compatible bridge support" depends on PCMCIA && PCI - select PCCARD_NONSTATIC if PCMCIA != n && ISA - select PCCARD_PCI if PCMCIA !=n && !ISA + select PCCARD_NONSTATIC help This provides support for the Intel I82092AA PCI-to-PCMCIA bridge device, found in some older laptops and more commonly in evaluation boards for the @@ -291,9 +288,6 @@ config ELECTRA_CF Say Y here to support the CompactFlash controller on the PA Semi Electra eval board. -config PCCARD_PCI - bool - config PCCARD_NONSTATIC bool diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index f1a7ca0..27e94b3 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -12,7 +12,6 @@ obj-$(CONFIG_PCMCIA) += pcmcia.o pcmcia_rsrc-y += rsrc_mgr.o pcmcia_rsrc-$(CONFIG_PCCARD_NONSTATIC) += rsrc_nonstatic.o pcmcia_rsrc-$(CONFIG_PCCARD_IODYN) += rsrc_iodyn.o -pcmcia_rsrc-$(CONFIG_PCCARD_PCI) += rsrc_pci.o obj-$(CONFIG_PCCARD) += pcmcia_rsrc.o diff --git a/drivers/pcmcia/rsrc_pci.c b/drivers/pcmcia/rsrc_pci.c deleted file mode 100644 index 1f67b3b..0000000 --- a/drivers/pcmcia/rsrc_pci.c +++ /dev/null @@ -1,173 +0,0 @@ -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/pci.h> - -#include <pcmcia/ss.h> -#include <pcmcia/cistpl.h> -#include "cs_internal.h" - - -struct pcmcia_align_data { - unsigned long mask; - unsigned long offset; -}; - -static resource_size_t pcmcia_align(void *align_data, - const struct resource *res, - resource_size_t size, resource_size_t align) -{ - struct pcmcia_align_data *data = align_data; - resource_size_t start; - - start = (res->start & ~data->mask) + data->offset; - if (start < res->start) - start += data->mask + 1; - return start; -} - -static struct resource *find_io_region(struct pcmcia_socket *s, - unsigned long base, int num, - unsigned long align) -{ - struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO, - dev_name(&s->dev)); - struct pcmcia_align_data data; - int ret; - - data.mask = align - 1; - data.offset = base & data.mask; - - ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1, - base, 0, pcmcia_align, &data); - if (ret != 0) { - kfree(res); - res = NULL; - } - return res; -} - -static int res_pci_find_io(struct pcmcia_socket *s, unsigned int attr, - unsigned int *base, unsigned int num, - unsigned int align, struct resource **parent) -{ - int i, ret = 0; - - /* Check for an already-allocated window that must conflict with - * what was asked for. It is a hack because it does not catch all - * potential conflicts, just the most obvious ones. - */ - for (i = 0; i < MAX_IO_WIN; i++) { - if (!s->io[i].res) - continue; - - if (!*base) - continue; - - if ((s->io[i].res->start & (align-1)) == *base) - return -EBUSY; - } - - for (i = 0; i < MAX_IO_WIN; i++) { - struct resource *res = s->io[i].res; - unsigned int try; - - if (res && (res->flags & IORESOURCE_BITS) != - (attr & IORESOURCE_BITS)) - continue; - - if (!res) { - if (align == 0) - align = 0x10000; - - res = s->io[i].res = find_io_region(s, *base, num, - align); - if (!res) - return -EINVAL; - - *base = res->start; - s->io[i].res->flags = - ((res->flags & ~IORESOURCE_BITS) | - (attr & IORESOURCE_BITS)); - s->io[i].InUse = num; - *parent = res; - return 0; - } - - /* Try to extend top of window */ - try = res->end + 1; - if ((*base == 0) || (*base == try)) { - ret = adjust_resource(s->io[i].res, res->start, - resource_size(res) + num); - if (ret) - continue; - *base = try; - s->io[i].InUse += num; - *parent = res; - return 0; - } - - /* Try to extend bottom of window */ - try = res->start - num; - if ((*base == 0) || (*base == try)) { - ret = adjust_resource(s->io[i].res, - res->start - num, - resource_size(res) + num); - if (ret) - continue; - *base = try; - s->io[i].InUse += num; - *parent = res; - return 0; - } - } - return -EINVAL; -} - -static struct resource *res_pci_find_mem(u_long base, u_long num, - u_long align, int low, struct pcmcia_socket *s) -{ - struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_MEM, - dev_name(&s->dev)); - struct pcmcia_align_data data; - unsigned long min; - int ret; - - if (align < 0x20000) - align = 0x20000; - data.mask = align - 1; - data.offset = base & data.mask; - - min = 0; - if (!low) - min = 0x100000UL; - - ret = pci_bus_alloc_resource(s->cb_dev->bus, - res, num, 1, min, 0, - pcmcia_align, &data); - - if (ret != 0) { - kfree(res); - res = NULL; - } - return res; -} - - -static int res_pci_init(struct pcmcia_socket *s) -{ - if (!s->cb_dev || !(s->features & SS_CAP_PAGE_REGS)) { - dev_err(&s->dev, "not supported by res_pci\n"); - return -EOPNOTSUPP; - } - return 0; -} - -struct pccard_resource_ops pccard_nonstatic_ops = { - .validate_mem = NULL, - .find_io = res_pci_find_io, - .find_mem = res_pci_find_mem, - .init = res_pci_init, - .exit = NULL, -}; -EXPORT_SYMBOL(pccard_nonstatic_ops); diff --git a/drivers/phy/phy-armada375-usb2.c b/drivers/phy/phy-armada375-usb2.c index 7c99ca2..8ccc395 100644 --- a/drivers/phy/phy-armada375-usb2.c +++ b/drivers/phy/phy-armada375-usb2.c @@ -37,7 +37,7 @@ static int armada375_usb_phy_init(struct phy *phy) struct armada375_cluster_phy *cluster_phy; u32 reg; - cluster_phy = dev_get_drvdata(phy->dev.parent); + cluster_phy = phy_get_drvdata(phy); if (!cluster_phy) return -ENODEV; @@ -131,6 +131,7 @@ static int armada375_usb_phy_probe(struct platform_device *pdev) cluster_phy->reg = usb_cluster_base; dev_set_drvdata(dev, cluster_phy); + phy_set_drvdata(phy, cluster_phy); phy_provider = devm_of_phy_provider_register(&pdev->dev, armada375_usb_phy_xlate); diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index a12d353..3791838 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -52,7 +52,9 @@ static void devm_phy_consume(struct device *dev, void *res) static int devm_phy_match(struct device *dev, void *res, void *match_data) { - return res == match_data; + struct phy **phy = res; + + return *phy == match_data; } /** @@ -223,6 +225,7 @@ int phy_init(struct phy *phy) ret = phy_pm_runtime_get_sync(phy); if (ret < 0 && ret != -ENOTSUPP) return ret; + ret = 0; /* Override possible ret == -ENOTSUPP */ mutex_lock(&phy->mutex); if (phy->init_count == 0 && phy->ops->init) { @@ -231,8 +234,6 @@ int phy_init(struct phy *phy) dev_err(&phy->dev, "phy init failed --> %d\n", ret); goto out; } - } else { - ret = 0; /* Override possible ret == -ENOTSUPP */ } ++phy->init_count; @@ -253,6 +254,7 @@ int phy_exit(struct phy *phy) ret = phy_pm_runtime_get_sync(phy); if (ret < 0 && ret != -ENOTSUPP) return ret; + ret = 0; /* Override possible ret == -ENOTSUPP */ mutex_lock(&phy->mutex); if (phy->init_count == 1 && phy->ops->exit) { @@ -287,6 +289,7 @@ int phy_power_on(struct phy *phy) ret = phy_pm_runtime_get_sync(phy); if (ret < 0 && ret != -ENOTSUPP) return ret; + ret = 0; /* Override possible ret == -ENOTSUPP */ mutex_lock(&phy->mutex); if (phy->power_count == 0 && phy->ops->power_on) { @@ -295,8 +298,6 @@ int phy_power_on(struct phy *phy) dev_err(&phy->dev, "phy poweron failed --> %d\n", ret); goto out; } - } else { - ret = 0; /* Override possible ret == -ENOTSUPP */ } ++phy->power_count; mutex_unlock(&phy->mutex); diff --git a/drivers/phy/phy-exynos-dp-video.c b/drivers/phy/phy-exynos-dp-video.c index f86cbe6..179cbf9 100644 --- a/drivers/phy/phy-exynos-dp-video.c +++ b/drivers/phy/phy-exynos-dp-video.c @@ -30,28 +30,13 @@ struct exynos_dp_video_phy { const struct exynos_dp_video_phy_drvdata *drvdata; }; -static void exynos_dp_video_phy_pwr_isol(struct exynos_dp_video_phy *state, - unsigned int on) -{ - unsigned int val; - - if (IS_ERR(state->regs)) - return; - - val = on ? 0 : EXYNOS5_PHY_ENABLE; - - regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset, - EXYNOS5_PHY_ENABLE, val); -} - static int exynos_dp_video_phy_power_on(struct phy *phy) { struct exynos_dp_video_phy *state = phy_get_drvdata(phy); /* Disable power isolation on DP-PHY */ - exynos_dp_video_phy_pwr_isol(state, 0); - - return 0; + return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset, + EXYNOS5_PHY_ENABLE, EXYNOS5_PHY_ENABLE); } static int exynos_dp_video_phy_power_off(struct phy *phy) @@ -59,9 +44,8 @@ static int exynos_dp_video_phy_power_off(struct phy *phy) struct exynos_dp_video_phy *state = phy_get_drvdata(phy); /* Enable power isolation on DP-PHY */ - exynos_dp_video_phy_pwr_isol(state, 1); - - return 0; + return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset, + EXYNOS5_PHY_ENABLE, 0); } static struct phy_ops exynos_dp_video_phy_ops = { diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c index f017b2f..df7519a 100644 --- a/drivers/phy/phy-exynos-mipi-video.c +++ b/drivers/phy/phy-exynos-mipi-video.c @@ -43,7 +43,6 @@ struct exynos_mipi_video_phy { } phys[EXYNOS_MIPI_PHYS_NUM]; spinlock_t slock; void __iomem *regs; - struct mutex mutex; struct regmap *regmap; }; @@ -59,8 +58,9 @@ static int __set_phy_state(struct exynos_mipi_video_phy *state, else reset = EXYNOS4_MIPI_PHY_SRESETN; - if (state->regmap) { - mutex_lock(&state->mutex); + spin_lock(&state->slock); + + if (!IS_ERR(state->regmap)) { regmap_read(state->regmap, offset, &val); if (on) val |= reset; @@ -72,11 +72,9 @@ static int __set_phy_state(struct exynos_mipi_video_phy *state, else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK)) val &= ~EXYNOS4_MIPI_PHY_ENABLE; regmap_write(state->regmap, offset, val); - mutex_unlock(&state->mutex); } else { addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2); - spin_lock(&state->slock); val = readl(addr); if (on) val |= reset; @@ -90,9 +88,9 @@ static int __set_phy_state(struct exynos_mipi_video_phy *state, val &= ~EXYNOS4_MIPI_PHY_ENABLE; writel(val, addr); - spin_unlock(&state->slock); } + spin_unlock(&state->slock); return 0; } @@ -158,7 +156,6 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev) dev_set_drvdata(dev, state); spin_lock_init(&state->slock); - mutex_init(&state->mutex); for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) { struct phy *phy = devm_phy_create(dev, NULL, diff --git a/drivers/phy/phy-exynos4210-usb2.c b/drivers/phy/phy-exynos4210-usb2.c index 236a52a..f30bbb0 100644 --- a/drivers/phy/phy-exynos4210-usb2.c +++ b/drivers/phy/phy-exynos4210-usb2.c @@ -250,7 +250,6 @@ static const struct samsung_usb2_common_phy exynos4210_phys[] = { .power_on = exynos4210_power_on, .power_off = exynos4210_power_off, }, - {}, }; const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = { diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c index 0b9de88..765da90 100644 --- a/drivers/phy/phy-exynos4x12-usb2.c +++ b/drivers/phy/phy-exynos4x12-usb2.c @@ -361,7 +361,6 @@ static const struct samsung_usb2_common_phy exynos4x12_phys[] = { .power_on = exynos4x12_power_on, .power_off = exynos4x12_power_off, }, - {}, }; const struct samsung_usb2_phy_config exynos3250_usb2_phy_config = { diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c index 0437401..e2a0be7 100644 --- a/drivers/phy/phy-exynos5-usbdrd.c +++ b/drivers/phy/phy-exynos5-usbdrd.c @@ -531,7 +531,7 @@ static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev, { struct exynos5_usbdrd_phy *phy_drd = dev_get_drvdata(dev); - if (WARN_ON(args->args[0] > EXYNOS5_DRDPHYS_NUM)) + if (WARN_ON(args->args[0] >= EXYNOS5_DRDPHYS_NUM)) return ERR_PTR(-ENODEV); return phy_drd->phys[args->args[0]].phy; diff --git a/drivers/phy/phy-exynos5250-usb2.c b/drivers/phy/phy-exynos5250-usb2.c index 1c139aa..2ed1735 100644 --- a/drivers/phy/phy-exynos5250-usb2.c +++ b/drivers/phy/phy-exynos5250-usb2.c @@ -391,7 +391,6 @@ static const struct samsung_usb2_common_phy exynos5250_phys[] = { .power_on = exynos5250_power_on, .power_off = exynos5250_power_off, }, - {}, }; const struct samsung_usb2_phy_config exynos5250_usb2_phy_config = { diff --git a/drivers/phy/phy-hix5hd2-sata.c b/drivers/phy/phy-hix5hd2-sata.c index 34915b4..d6b2265 100644 --- a/drivers/phy/phy-hix5hd2-sata.c +++ b/drivers/phy/phy-hix5hd2-sata.c @@ -147,6 +147,9 @@ static int hix5hd2_sata_phy_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + priv->base = devm_ioremap(dev, res->start, resource_size(res)); if (!priv->base) return -ENOMEM; diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c index 9b2848e..9334352 100644 --- a/drivers/phy/phy-miphy28lp.c +++ b/drivers/phy/phy-miphy28lp.c @@ -228,6 +228,7 @@ struct miphy28lp_dev { struct regmap *regmap; struct mutex miphy_mutex; struct miphy28lp_phy **phys; + int nphys; }; struct miphy_initval { @@ -1116,7 +1117,7 @@ static struct phy *miphy28lp_xlate(struct device *dev, return ERR_PTR(-EINVAL); } - for (index = 0; index < of_get_child_count(dev->of_node); index++) + for (index = 0; index < miphy_dev->nphys; index++) if (phynode == miphy_dev->phys[index]->phy->dev.of_node) { miphy_phy = miphy_dev->phys[index]; break; @@ -1138,6 +1139,7 @@ static struct phy *miphy28lp_xlate(struct device *dev, static struct phy_ops miphy28lp_ops = { .init = miphy28lp_init, + .owner = THIS_MODULE, }; static int miphy28lp_probe_resets(struct device_node *node, @@ -1200,16 +1202,15 @@ static int miphy28lp_probe(struct platform_device *pdev) struct miphy28lp_dev *miphy_dev; struct phy_provider *provider; struct phy *phy; - int chancount, port = 0; - int ret; + int ret, port = 0; miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL); if (!miphy_dev) return -ENOMEM; - chancount = of_get_child_count(np); - miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount, - GFP_KERNEL); + miphy_dev->nphys = of_get_child_count(np); + miphy_dev->phys = devm_kcalloc(&pdev->dev, miphy_dev->nphys, + sizeof(*miphy_dev->phys), GFP_KERNEL); if (!miphy_dev->phys) return -ENOMEM; diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c index 6c80154..51b459d 100644 --- a/drivers/phy/phy-miphy365x.c +++ b/drivers/phy/phy-miphy365x.c @@ -150,6 +150,7 @@ struct miphy365x_dev { struct regmap *regmap; struct mutex miphy_mutex; struct miphy365x_phy **phys; + int nphys; }; /* @@ -485,7 +486,7 @@ static struct phy *miphy365x_xlate(struct device *dev, return ERR_PTR(-EINVAL); } - for (index = 0; index < of_get_child_count(dev->of_node); index++) + for (index = 0; index < miphy_dev->nphys; index++) if (phynode == miphy_dev->phys[index]->phy->dev.of_node) { miphy_phy = miphy_dev->phys[index]; break; @@ -541,16 +542,15 @@ static int miphy365x_probe(struct platform_device *pdev) struct miphy365x_dev *miphy_dev; struct phy_provider *provider; struct phy *phy; - int chancount, port = 0; - int ret; + int ret, port = 0; miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL); if (!miphy_dev) return -ENOMEM; - chancount = of_get_child_count(np); - miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount, - GFP_KERNEL); + miphy_dev->nphys = of_get_child_count(np); + miphy_dev->phys = devm_kcalloc(&pdev->dev, miphy_dev->nphys, + sizeof(*miphy_dev->phys), GFP_KERNEL); if (!miphy_dev->phys) return -ENOMEM; diff --git a/drivers/phy/phy-omap-control.c b/drivers/phy/phy-omap-control.c index efe724f..93252e0 100644 --- a/drivers/phy/phy-omap-control.c +++ b/drivers/phy/phy-omap-control.c @@ -360,7 +360,7 @@ static void __exit omap_control_phy_exit(void) } module_exit(omap_control_phy_exit); -MODULE_ALIAS("platform: omap_control_phy"); +MODULE_ALIAS("platform:omap_control_phy"); MODULE_AUTHOR("Texas Instruments Inc."); MODULE_DESCRIPTION("OMAP Control Module PHY Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c index 6f4aef3..4757e76 100644 --- a/drivers/phy/phy-omap-usb2.c +++ b/drivers/phy/phy-omap-usb2.c @@ -296,10 +296,11 @@ static int omap_usb2_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "found usb_otg_ss_refclk960m, please fix DTS\n"); } - } else { - clk_prepare(phy->optclk); } + if (!IS_ERR(phy->optclk)) + clk_prepare(phy->optclk); + usb_add_phy_dev(&phy->phy); return 0; @@ -383,7 +384,7 @@ static struct platform_driver omap_usb2_driver = { module_platform_driver(omap_usb2_driver); -MODULE_ALIAS("platform: omap_usb2"); +MODULE_ALIAS("platform:omap_usb2"); MODULE_AUTHOR("Texas Instruments Inc."); MODULE_DESCRIPTION("OMAP USB2 phy driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c index 44ee983..86665e9 100644 --- a/drivers/phy/phy-qcom-ufs.c +++ b/drivers/phy/phy-qcom-ufs.c @@ -346,10 +346,10 @@ int ufs_qcom_phy_cfg_vreg(struct phy *phy, goto out; } uA_load = on ? vreg->max_uA : 0; - ret = regulator_set_optimum_mode(reg, uA_load); + ret = regulator_set_load(reg, uA_load); if (ret >= 0) { /* - * regulator_set_optimum_mode() returns new regulator + * regulator_set_load() returns new regulator * mode upon success. */ ret = 0; diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c index 22011c3..7d4c336 100644 --- a/drivers/phy/phy-rockchip-usb.c +++ b/drivers/phy/phy-rockchip-usb.c @@ -61,8 +61,6 @@ static int rockchip_usb_phy_power_off(struct phy *_phy) return ret; clk_disable_unprepare(phy->clk); - if (ret) - return ret; return 0; } @@ -78,8 +76,10 @@ static int rockchip_usb_phy_power_on(struct phy *_phy) /* Power up usb phy analog blocks by set siddq 0 */ ret = rockchip_usb_phy_power(phy, 0); - if (ret) + if (ret) { + clk_disable_unprepare(phy->clk); return ret; + } return 0; } diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c index 95c88f9..2ba610b 100644 --- a/drivers/phy/phy-ti-pipe3.c +++ b/drivers/phy/phy-ti-pipe3.c @@ -165,15 +165,11 @@ static int ti_pipe3_dpll_wait_lock(struct ti_pipe3 *phy) cpu_relax(); val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); if (val & PLL_LOCK) - break; + return 0; } while (!time_after(jiffies, timeout)); - if (!(val & PLL_LOCK)) { - dev_err(phy->dev, "DPLL failed to lock\n"); - return -EBUSY; - } - - return 0; + dev_err(phy->dev, "DPLL failed to lock\n"); + return -EBUSY; } static int ti_pipe3_dpll_program(struct ti_pipe3 *phy) @@ -608,7 +604,7 @@ static struct platform_driver ti_pipe3_driver = { module_platform_driver(ti_pipe3_driver); -MODULE_ALIAS("platform: ti_pipe3"); +MODULE_ALIAS("platform:ti_pipe3"); MODULE_AUTHOR("Texas Instruments Inc."); MODULE_DESCRIPTION("TI PIPE3 phy driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 8e87f54..bc42d6a 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -666,7 +666,6 @@ static int twl4030_usb_probe(struct platform_device *pdev) twl->dev = &pdev->dev; twl->irq = platform_get_irq(pdev, 0); twl->vbus_supplied = false; - twl->linkstat = -EINVAL; twl->linkstat = OMAP_MUSB_UNKNOWN; twl->phy.dev = twl->dev; diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c index 29214a3..2263cd0 100644 --- a/drivers/phy/phy-xgene.c +++ b/drivers/phy/phy-xgene.c @@ -1704,7 +1704,6 @@ static int xgene_phy_probe(struct platform_device *pdev) for (i = 0; i < MAX_LANE; i++) ctx->sata_param.speed[i] = 2; /* Default to Gen3 */ - ctx->dev = &pdev->dev; platform_set_drvdata(pdev, ctx); ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops); diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c index 5afe03e..2062c22 100644 --- a/drivers/pinctrl/intel/pinctrl-baytrail.c +++ b/drivers/pinctrl/intel/pinctrl-baytrail.c @@ -66,6 +66,10 @@ #define BYT_DIR_MASK (BIT(1) | BIT(2)) #define BYT_TRIG_MASK (BIT(26) | BIT(25) | BIT(24)) +#define BYT_CONF0_RESTORE_MASK (BYT_DIRECT_IRQ_EN | BYT_TRIG_MASK | \ + BYT_PIN_MUX) +#define BYT_VAL_RESTORE_MASK (BYT_DIR_MASK | BYT_LEVEL) + #define BYT_NGPIO_SCORE 102 #define BYT_NGPIO_NCORE 28 #define BYT_NGPIO_SUS 44 @@ -134,12 +138,18 @@ static struct pinctrl_gpio_range byt_ranges[] = { }, }; +struct byt_gpio_pin_context { + u32 conf0; + u32 val; +}; + struct byt_gpio { struct gpio_chip chip; struct platform_device *pdev; spinlock_t lock; void __iomem *reg_base; struct pinctrl_gpio_range *range; + struct byt_gpio_pin_context *saved_context; }; #define to_byt_gpio(c) container_of(c, struct byt_gpio, chip) @@ -158,40 +168,62 @@ static void __iomem *byt_gpio_reg(struct gpio_chip *chip, unsigned offset, return vg->reg_base + reg_offset + reg; } -static bool is_special_pin(struct byt_gpio *vg, unsigned offset) +static void byt_gpio_clear_triggering(struct byt_gpio *vg, unsigned offset) +{ + void __iomem *reg = byt_gpio_reg(&vg->chip, offset, BYT_CONF0_REG); + unsigned long flags; + u32 value; + + spin_lock_irqsave(&vg->lock, flags); + value = readl(reg); + value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); + writel(value, reg); + spin_unlock_irqrestore(&vg->lock, flags); +} + +static u32 byt_get_gpio_mux(struct byt_gpio *vg, unsigned offset) { /* SCORE pin 92-93 */ if (!strcmp(vg->range->name, BYT_SCORE_ACPI_UID) && offset >= 92 && offset <= 93) - return true; + return 1; /* SUS pin 11-21 */ if (!strcmp(vg->range->name, BYT_SUS_ACPI_UID) && offset >= 11 && offset <= 21) - return true; + return 1; - return false; + return 0; } static int byt_gpio_request(struct gpio_chip *chip, unsigned offset) { struct byt_gpio *vg = to_byt_gpio(chip); void __iomem *reg = byt_gpio_reg(chip, offset, BYT_CONF0_REG); - u32 value; - bool special; + u32 value, gpio_mux; /* * In most cases, func pin mux 000 means GPIO function. * But, some pins may have func pin mux 001 represents - * GPIO function. Only allow user to export pin with - * func pin mux preset as GPIO function by BIOS/FW. + * GPIO function. + * + * Because there are devices out there where some pins were not + * configured correctly we allow changing the mux value from + * request (but print out warning about that). */ value = readl(reg) & BYT_PIN_MUX; - special = is_special_pin(vg, offset); - if ((special && value != 1) || (!special && value)) { - dev_err(&vg->pdev->dev, - "pin %u cannot be used as GPIO.\n", offset); - return -EINVAL; + gpio_mux = byt_get_gpio_mux(vg, offset); + if (WARN_ON(gpio_mux != value)) { + unsigned long flags; + + spin_lock_irqsave(&vg->lock, flags); + value = readl(reg) & ~BYT_PIN_MUX; + value |= gpio_mux; + writel(value, reg); + spin_unlock_irqrestore(&vg->lock, flags); + + dev_warn(&vg->pdev->dev, + "pin %u forcibly re-configured as GPIO\n", offset); } pm_runtime_get(&vg->pdev->dev); @@ -202,14 +234,8 @@ static int byt_gpio_request(struct gpio_chip *chip, unsigned offset) static void byt_gpio_free(struct gpio_chip *chip, unsigned offset) { struct byt_gpio *vg = to_byt_gpio(chip); - void __iomem *reg = byt_gpio_reg(&vg->chip, offset, BYT_CONF0_REG); - u32 value; - - /* clear interrupt triggering */ - value = readl(reg); - value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); - writel(value, reg); + byt_gpio_clear_triggering(vg, offset); pm_runtime_put(&vg->pdev->dev); } @@ -236,23 +262,13 @@ static int byt_irq_type(struct irq_data *d, unsigned type) value &= ~(BYT_DIRECT_IRQ_EN | BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); - switch (type) { - case IRQ_TYPE_LEVEL_HIGH: - value |= BYT_TRIG_LVL; - case IRQ_TYPE_EDGE_RISING: - value |= BYT_TRIG_POS; - break; - case IRQ_TYPE_LEVEL_LOW: - value |= BYT_TRIG_LVL; - case IRQ_TYPE_EDGE_FALLING: - value |= BYT_TRIG_NEG; - break; - case IRQ_TYPE_EDGE_BOTH: - value |= (BYT_TRIG_NEG | BYT_TRIG_POS); - break; - } writel(value, reg); + if (type & IRQ_TYPE_EDGE_BOTH) + __irq_set_handler_locked(d->irq, handle_edge_irq); + else if (type & IRQ_TYPE_LEVEL_MASK) + __irq_set_handler_locked(d->irq, handle_level_irq); + spin_unlock_irqrestore(&vg->lock, flags); return 0; @@ -410,58 +426,80 @@ static void byt_gpio_irq_handler(unsigned irq, struct irq_desc *desc) struct irq_data *data = irq_desc_get_irq_data(desc); struct byt_gpio *vg = to_byt_gpio(irq_desc_get_handler_data(desc)); struct irq_chip *chip = irq_data_get_irq_chip(data); - u32 base, pin, mask; + u32 base, pin; void __iomem *reg; - u32 pending; + unsigned long pending; unsigned virq; - int looplimit = 0; /* check from GPIO controller which pin triggered the interrupt */ for (base = 0; base < vg->chip.ngpio; base += 32) { - reg = byt_gpio_reg(&vg->chip, base, BYT_INT_STAT_REG); - - while ((pending = readl(reg))) { - pin = __ffs(pending); - mask = BIT(pin); - /* Clear before handling so we can't lose an edge */ - writel(mask, reg); - + pending = readl(reg); + for_each_set_bit(pin, &pending, 32) { virq = irq_find_mapping(vg->chip.irqdomain, base + pin); generic_handle_irq(virq); - - /* In case bios or user sets triggering incorretly a pin - * might remain in "interrupt triggered" state. - */ - if (looplimit++ > 32) { - dev_err(&vg->pdev->dev, - "Gpio %d interrupt flood, disabling\n", - base + pin); - - reg = byt_gpio_reg(&vg->chip, base + pin, - BYT_CONF0_REG); - mask = readl(reg); - mask &= ~(BYT_TRIG_NEG | BYT_TRIG_POS | - BYT_TRIG_LVL); - writel(mask, reg); - mask = readl(reg); /* flush */ - break; - } } } chip->irq_eoi(data); } +static void byt_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct byt_gpio *vg = to_byt_gpio(gc); + unsigned offset = irqd_to_hwirq(d); + void __iomem *reg; + + reg = byt_gpio_reg(&vg->chip, offset, BYT_INT_STAT_REG); + writel(BIT(offset % 32), reg); +} + static void byt_irq_unmask(struct irq_data *d) { + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct byt_gpio *vg = to_byt_gpio(gc); + unsigned offset = irqd_to_hwirq(d); + unsigned long flags; + void __iomem *reg; + u32 value; + + spin_lock_irqsave(&vg->lock, flags); + + reg = byt_gpio_reg(&vg->chip, offset, BYT_CONF0_REG); + value = readl(reg); + + switch (irqd_get_trigger_type(d)) { + case IRQ_TYPE_LEVEL_HIGH: + value |= BYT_TRIG_LVL; + case IRQ_TYPE_EDGE_RISING: + value |= BYT_TRIG_POS; + break; + case IRQ_TYPE_LEVEL_LOW: + value |= BYT_TRIG_LVL; + case IRQ_TYPE_EDGE_FALLING: + value |= BYT_TRIG_NEG; + break; + case IRQ_TYPE_EDGE_BOTH: + value |= (BYT_TRIG_NEG | BYT_TRIG_POS); + break; + } + + writel(value, reg); + + spin_unlock_irqrestore(&vg->lock, flags); } static void byt_irq_mask(struct irq_data *d) { + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct byt_gpio *vg = to_byt_gpio(gc); + + byt_gpio_clear_triggering(vg, irqd_to_hwirq(d)); } static struct irq_chip byt_irqchip = { .name = "BYT-GPIO", + .irq_ack = byt_irq_ack, .irq_mask = byt_irq_mask, .irq_unmask = byt_irq_unmask, .irq_set_type = byt_irq_type, @@ -472,6 +510,21 @@ static void byt_gpio_irq_init_hw(struct byt_gpio *vg) { void __iomem *reg; u32 base, value; + int i; + + /* + * Clear interrupt triggers for all pins that are GPIOs and + * do not use direct IRQ mode. This will prevent spurious + * interrupts from misconfigured pins. + */ + for (i = 0; i < vg->chip.ngpio; i++) { + value = readl(byt_gpio_reg(&vg->chip, i, BYT_CONF0_REG)); + if ((value & BYT_PIN_MUX) == byt_get_gpio_mux(vg, i) && + !(value & BYT_DIRECT_IRQ_EN)) { + byt_gpio_clear_triggering(vg, i); + dev_dbg(&vg->pdev->dev, "disabling GPIO %d\n", i); + } + } /* clear interrupt status trigger registers */ for (base = 0; base < vg->chip.ngpio; base += 32) { @@ -541,6 +594,11 @@ static int byt_gpio_probe(struct platform_device *pdev) gc->can_sleep = false; gc->dev = dev; +#ifdef CONFIG_PM_SLEEP + vg->saved_context = devm_kcalloc(&pdev->dev, gc->ngpio, + sizeof(*vg->saved_context), GFP_KERNEL); +#endif + ret = gpiochip_add(gc); if (ret) { dev_err(&pdev->dev, "failed adding byt-gpio chip\n"); @@ -569,6 +627,69 @@ static int byt_gpio_probe(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int byt_gpio_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct byt_gpio *vg = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < vg->chip.ngpio; i++) { + void __iomem *reg; + u32 value; + + reg = byt_gpio_reg(&vg->chip, i, BYT_CONF0_REG); + value = readl(reg) & BYT_CONF0_RESTORE_MASK; + vg->saved_context[i].conf0 = value; + + reg = byt_gpio_reg(&vg->chip, i, BYT_VAL_REG); + value = readl(reg) & BYT_VAL_RESTORE_MASK; + vg->saved_context[i].val = value; + } + + return 0; +} + +static int byt_gpio_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct byt_gpio *vg = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < vg->chip.ngpio; i++) { + void __iomem *reg; + u32 value; + + reg = byt_gpio_reg(&vg->chip, i, BYT_CONF0_REG); + value = readl(reg); + if ((value & BYT_CONF0_RESTORE_MASK) != + vg->saved_context[i].conf0) { + value &= ~BYT_CONF0_RESTORE_MASK; + value |= vg->saved_context[i].conf0; + writel(value, reg); + dev_info(dev, "restored pin %d conf0 %#08x", i, value); + } + + reg = byt_gpio_reg(&vg->chip, i, BYT_VAL_REG); + value = readl(reg); + if ((value & BYT_VAL_RESTORE_MASK) != + vg->saved_context[i].val) { + u32 v; + + v = value & ~BYT_VAL_RESTORE_MASK; + v |= vg->saved_context[i].val; + if (v != value) { + writel(v, reg); + dev_dbg(dev, "restored pin %d val %#08x\n", + i, v); + } + } + } + + return 0; +} +#endif + static int byt_gpio_runtime_suspend(struct device *dev) { return 0; @@ -580,8 +701,9 @@ static int byt_gpio_runtime_resume(struct device *dev) } static const struct dev_pm_ops byt_gpio_pm_ops = { - .runtime_suspend = byt_gpio_runtime_suspend, - .runtime_resume = byt_gpio_runtime_resume, + SET_LATE_SYSTEM_SLEEP_PM_OPS(byt_gpio_suspend, byt_gpio_resume) + SET_RUNTIME_PM_OPS(byt_gpio_runtime_suspend, byt_gpio_runtime_resume, + NULL) }; static const struct acpi_device_id byt_gpio_acpi_match[] = { diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index 3034fd0..82f691e 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -1226,6 +1226,7 @@ static int chv_gpio_direction_input(struct gpio_chip *chip, unsigned offset) static int chv_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { + chv_gpio_set(chip, offset, value); return pinctrl_gpio_direction_output(chip->base + offset); } diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index f4cd0b9..a481406 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -1477,28 +1477,25 @@ static void gpio_irq_ack(struct irq_data *d) /* the interrupt is already cleared before by reading ISR */ } -static unsigned int gpio_irq_startup(struct irq_data *d) +static int gpio_irq_request_res(struct irq_data *d) { struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); unsigned pin = d->hwirq; int ret; ret = gpiochip_lock_as_irq(&at91_gpio->chip, pin); - if (ret) { + if (ret) dev_err(at91_gpio->chip.dev, "unable to lock pind %lu IRQ\n", d->hwirq); - return ret; - } - gpio_irq_unmask(d); - return 0; + + return ret; } -static void gpio_irq_shutdown(struct irq_data *d) +static void gpio_irq_release_res(struct irq_data *d) { struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); unsigned pin = d->hwirq; - gpio_irq_mask(d); gpiochip_unlock_as_irq(&at91_gpio->chip, pin); } @@ -1577,8 +1574,8 @@ void at91_pinctrl_gpio_resume(void) static struct irq_chip gpio_irqchip = { .name = "GPIO", .irq_ack = gpio_irq_ack, - .irq_startup = gpio_irq_startup, - .irq_shutdown = gpio_irq_shutdown, + .irq_request_resources = gpio_irq_request_res, + .irq_release_resources = gpio_irq_release_res, .irq_disable = gpio_irq_mask, .irq_mask = gpio_irq_mask, .irq_unmask = gpio_irq_unmask, diff --git a/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c b/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c index 24c5d88..3c68a8e 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c @@ -1011,6 +1011,7 @@ static const struct sunxi_pinctrl_desc sun4i_a10_pinctrl_data = { .pins = sun4i_a10_pins, .npins = ARRAY_SIZE(sun4i_a10_pins), .irq_banks = 1, + .irq_read_needs_mux = true, }; static int sun4i_a10_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 3d07443..f8e171b 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -29,6 +29,7 @@ #include <linux/slab.h> #include "../core.h" +#include "../../gpio/gpiolib.h" #include "pinctrl-sunxi.h" static struct irq_chip sunxi_pinctrl_edge_irq_chip; @@ -464,10 +465,19 @@ static int sunxi_pinctrl_gpio_direction_input(struct gpio_chip *chip, static int sunxi_pinctrl_gpio_get(struct gpio_chip *chip, unsigned offset) { struct sunxi_pinctrl *pctl = dev_get_drvdata(chip->dev); - u32 reg = sunxi_data_reg(offset); u8 index = sunxi_data_offset(offset); - u32 val = (readl(pctl->membase + reg) >> index) & DATA_PINS_MASK; + u32 set_mux = pctl->desc->irq_read_needs_mux && + test_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags); + u32 val; + + if (set_mux) + sunxi_pmx_set(pctl->pctl_dev, offset, SUN4I_FUNC_INPUT); + + val = (readl(pctl->membase + reg) >> index) & DATA_PINS_MASK; + + if (set_mux) + sunxi_pmx_set(pctl->pctl_dev, offset, SUN4I_FUNC_IRQ); return val; } diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h index 5a51523..e248e81 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h @@ -77,6 +77,9 @@ #define IRQ_LEVEL_LOW 0x03 #define IRQ_EDGE_BOTH 0x04 +#define SUN4I_FUNC_INPUT 0 +#define SUN4I_FUNC_IRQ 6 + struct sunxi_desc_function { const char *name; u8 muxval; @@ -94,6 +97,7 @@ struct sunxi_pinctrl_desc { int npins; unsigned pin_base; unsigned irq_banks; + bool irq_read_needs_mux; }; struct sunxi_pinctrl_function { diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 15c0fab..b4e9447 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -177,7 +177,7 @@ struct compal_data{ unsigned char curr_pwm; /* Power supply */ - struct power_supply psy; + struct power_supply *psy; struct power_supply_info psy_info; char bat_model_name[BAT_MODEL_NAME_LEN + 1]; char bat_manufacturer_name[BAT_MANUFACTURER_NAME_LEN + 1]; @@ -565,8 +565,7 @@ static int bat_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct compal_data *data; - data = container_of(psy, struct compal_data, psy); + struct compal_data *data = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_STATUS: @@ -875,13 +874,16 @@ static struct dmi_system_id __initdata compal_dmi_table[] = { }; MODULE_DEVICE_TABLE(dmi, compal_dmi_table); +static const struct power_supply_desc psy_bat_desc = { + .name = DRIVER_NAME, + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = compal_bat_properties, + .num_properties = ARRAY_SIZE(compal_bat_properties), + .get_property = bat_get_property, +}; + static void initialize_power_supply_data(struct compal_data *data) { - data->psy.name = DRIVER_NAME; - data->psy.type = POWER_SUPPLY_TYPE_BATTERY; - data->psy.properties = compal_bat_properties; - data->psy.num_properties = ARRAY_SIZE(compal_bat_properties); - data->psy.get_property = bat_get_property; ec_read_sequence(BAT_MANUFACTURER_NAME_ADDR, data->bat_manufacturer_name, @@ -1011,6 +1013,7 @@ static int compal_probe(struct platform_device *pdev) int err; struct compal_data *data; struct device *hwmon_dev; + struct power_supply_config psy_cfg = {}; if (!extra_features) return 0; @@ -1026,9 +1029,9 @@ static int compal_probe(struct platform_device *pdev) if (err) return err; - hwmon_dev = hwmon_device_register_with_groups(&pdev->dev, - "compal", data, - compal_hwmon_groups); + hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, + "compal", data, + compal_hwmon_groups); if (IS_ERR(hwmon_dev)) { err = PTR_ERR(hwmon_dev); goto remove; @@ -1036,7 +1039,13 @@ static int compal_probe(struct platform_device *pdev) /* Power supply */ initialize_power_supply_data(data); - power_supply_register(&compal_device->dev, &data->psy); + psy_cfg.drv_data = data; + data->psy = power_supply_register(&compal_device->dev, &psy_bat_desc, + &psy_cfg); + if (IS_ERR(data->psy)) { + err = PTR_ERR(data->psy); + goto remove; + } platform_set_drvdata(pdev, data); @@ -1071,7 +1080,7 @@ static int compal_remove(struct platform_device *pdev) pwm_disable_control(); data = platform_get_drvdata(pdev); - power_supply_unregister(&data->psy); + power_supply_unregister(data->psy); sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group); diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c index ebf0d67..943c1cb 100644 --- a/drivers/pnp/quirks.c +++ b/drivers/pnp/quirks.c @@ -246,13 +246,16 @@ static void quirk_system_pci_resources(struct pnp_dev *dev) */ for_each_pci_dev(pdev) { for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { - unsigned long type; + unsigned long flags, type; - type = pci_resource_flags(pdev, i) & - (IORESOURCE_IO | IORESOURCE_MEM); + flags = pci_resource_flags(pdev, i); + type = flags & (IORESOURCE_IO | IORESOURCE_MEM); if (!type || pci_resource_len(pdev, i) == 0) continue; + if (flags & IORESOURCE_UNSET) + continue; + pci_start = pci_resource_start(pdev, i); pci_end = pci_resource_end(pdev, i); for (j = 0; diff --git a/drivers/power/88pm860x_battery.c b/drivers/power/88pm860x_battery.c index bd3c997..d49579b 100644 --- a/drivers/power/88pm860x_battery.c +++ b/drivers/power/88pm860x_battery.c @@ -98,7 +98,7 @@ struct pm860x_battery_info { struct i2c_client *i2c; struct device *dev; - struct power_supply battery; + struct power_supply *battery; struct mutex lock; int status; int irq_cc; @@ -798,9 +798,8 @@ out: static void pm860x_external_power_changed(struct power_supply *psy) { - struct pm860x_battery_info *info; + struct pm860x_battery_info *info = dev_get_drvdata(psy->dev.parent); - info = container_of(psy, struct pm860x_battery_info, battery); calc_resistor(info); } @@ -808,7 +807,7 @@ static int pm860x_batt_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct pm860x_battery_info *info = dev_get_drvdata(psy->dev->parent); + struct pm860x_battery_info *info = dev_get_drvdata(psy->dev.parent); int data; int ret; @@ -874,7 +873,7 @@ static int pm860x_batt_set_prop(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { - struct pm860x_battery_info *info = dev_get_drvdata(psy->dev->parent); + struct pm860x_battery_info *info = dev_get_drvdata(psy->dev.parent); switch (psp) { case POWER_SUPPLY_PROP_CHARGE_FULL: @@ -901,6 +900,16 @@ static enum power_supply_property pm860x_batt_props[] = { POWER_SUPPLY_PROP_TEMP, }; +static const struct power_supply_desc pm860x_battery_desc = { + .name = "battery-monitor", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = pm860x_batt_props, + .num_properties = ARRAY_SIZE(pm860x_batt_props), + .get_property = pm860x_batt_get_prop, + .set_property = pm860x_batt_set_prop, + .external_power_changed = pm860x_external_power_changed, +}; + static int pm860x_battery_probe(struct platform_device *pdev) { struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); @@ -936,14 +945,6 @@ static int pm860x_battery_probe(struct platform_device *pdev) pm860x_init_battery(info); - info->battery.name = "battery-monitor"; - info->battery.type = POWER_SUPPLY_TYPE_BATTERY; - info->battery.properties = pm860x_batt_props; - info->battery.num_properties = ARRAY_SIZE(pm860x_batt_props); - info->battery.get_property = pm860x_batt_get_prop; - info->battery.set_property = pm860x_batt_set_prop; - info->battery.external_power_changed = pm860x_external_power_changed; - if (pdata && pdata->max_capacity) info->max_capacity = pdata->max_capacity; else @@ -953,10 +954,11 @@ static int pm860x_battery_probe(struct platform_device *pdev) else info->resistor = 300; /* set default internal resistor */ - ret = power_supply_register(&pdev->dev, &info->battery); - if (ret) - return ret; - info->battery.dev->parent = &pdev->dev; + info->battery = power_supply_register(&pdev->dev, &pm860x_battery_desc, + NULL); + if (IS_ERR(info->battery)) + return PTR_ERR(info->battery); + info->battery->dev.parent = &pdev->dev; ret = request_threaded_irq(info->irq_cc, NULL, pm860x_coulomb_handler, IRQF_ONESHOT, @@ -981,7 +983,7 @@ static int pm860x_battery_probe(struct platform_device *pdev) out_coulomb: free_irq(info->irq_cc, info); out_reg: - power_supply_unregister(&info->battery); + power_supply_unregister(info->battery); return ret; } @@ -991,7 +993,7 @@ static int pm860x_battery_remove(struct platform_device *pdev) free_irq(info->irq_batt, info); free_irq(info->irq_cc, info); - power_supply_unregister(&info->battery); + power_supply_unregister(info->battery); return 0; } diff --git a/drivers/power/88pm860x_charger.c b/drivers/power/88pm860x_charger.c index 734ec4a..0e448c6 100644 --- a/drivers/power/88pm860x_charger.c +++ b/drivers/power/88pm860x_charger.c @@ -102,7 +102,7 @@ struct pm860x_charger_info { struct i2c_client *i2c_8606; struct device *dev; - struct power_supply usb; + struct power_supply *usb; struct mutex lock; int irq_nums; int irq[7]; @@ -296,14 +296,20 @@ static int set_charging_fsm(struct pm860x_charger_info *info) psy = power_supply_get_by_name(pm860x_supplied_to[0]); if (!psy) return -EINVAL; - ret = psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &data); - if (ret) + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, + &data); + if (ret) { + power_supply_put(psy); return ret; + } vbatt = data.intval / 1000; - ret = psy->get_property(psy, POWER_SUPPLY_PROP_PRESENT, &data); - if (ret) + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &data); + if (ret) { + power_supply_put(psy); return ret; + } + power_supply_put(psy); mutex_lock(&info->lock); info->present = data.intval; @@ -414,7 +420,7 @@ static irqreturn_t pm860x_charger_handler(int irq, void *data) set_charging_fsm(info); - power_supply_changed(&info->usb); + power_supply_changed(info->usb); out: return IRQ_HANDLED; } @@ -430,7 +436,7 @@ static irqreturn_t pm860x_temp_handler(int irq, void *data) psy = power_supply_get_by_name(pm860x_supplied_to[0]); if (!psy) goto out; - ret = psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &temp); + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &temp); if (ret) goto out; value = temp.intval / 10; @@ -446,6 +452,7 @@ static irqreturn_t pm860x_temp_handler(int irq, void *data) set_charging_fsm(info); out: + power_supply_put(psy); return IRQ_HANDLED; } @@ -485,9 +492,10 @@ static irqreturn_t pm860x_done_handler(int irq, void *data) psy = power_supply_get_by_name(pm860x_supplied_to[0]); if (!psy) goto out; - ret = psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, + &val); if (ret) - goto out; + goto out_psy_put; vbatt = val.intval / 1000; /* * CHG_DONE interrupt is faster than CHG_DET interrupt when @@ -498,10 +506,13 @@ static irqreturn_t pm860x_done_handler(int irq, void *data) */ ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); if (ret < 0) - goto out; + goto out_psy_put; if (vbatt > CHARGE_THRESHOLD && ret & STATUS2_CHG) - psy->set_property(psy, POWER_SUPPLY_PROP_CHARGE_FULL, &val); + power_supply_set_property(psy, POWER_SUPPLY_PROP_CHARGE_FULL, + &val); +out_psy_put: + power_supply_put(psy); out: mutex_unlock(&info->lock); dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); @@ -584,8 +595,7 @@ static int pm860x_usb_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct pm860x_charger_info *info = - dev_get_drvdata(psy->dev->parent); + struct pm860x_charger_info *info = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_STATUS: @@ -645,9 +655,18 @@ static struct pm860x_irq_desc { { "vchg", pm860x_vchg_handler }, }; +static const struct power_supply_desc pm860x_charger_desc = { + .name = "usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = pm860x_usb_props, + .num_properties = ARRAY_SIZE(pm860x_usb_props), + .get_property = pm860x_usb_get_prop, +}; + static int pm860x_charger_probe(struct platform_device *pdev) { struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct power_supply_config psy_cfg = {}; struct pm860x_charger_info *info; int ret; int count; @@ -685,16 +704,15 @@ static int pm860x_charger_probe(struct platform_device *pdev) mutex_init(&info->lock); platform_set_drvdata(pdev, info); - info->usb.name = "usb"; - info->usb.type = POWER_SUPPLY_TYPE_USB; - info->usb.supplied_to = pm860x_supplied_to; - info->usb.num_supplicants = ARRAY_SIZE(pm860x_supplied_to); - info->usb.properties = pm860x_usb_props; - info->usb.num_properties = ARRAY_SIZE(pm860x_usb_props); - info->usb.get_property = pm860x_usb_get_prop; - ret = power_supply_register(&pdev->dev, &info->usb); - if (ret) + psy_cfg.drv_data = info; + psy_cfg.supplied_to = pm860x_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(pm860x_supplied_to); + info->usb = power_supply_register(&pdev->dev, &pm860x_charger_desc, + &psy_cfg); + if (IS_ERR(info->usb)) { + ret = PTR_ERR(info->usb); goto out; + } pm860x_init_charger(info); @@ -711,7 +729,7 @@ static int pm860x_charger_probe(struct platform_device *pdev) return 0; out_irq: - power_supply_unregister(&info->usb); + power_supply_unregister(info->usb); while (--i >= 0) free_irq(info->irq[i], info); out: @@ -723,7 +741,7 @@ static int pm860x_charger_remove(struct platform_device *pdev) struct pm860x_charger_info *info = platform_get_drvdata(pdev); int i; - power_supply_unregister(&info->usb); + power_supply_unregister(info->usb); free_irq(info->irq[0], info); for (i = 0; i < info->irq_nums; i++) free_irq(info->irq[i], info); diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 27b751b..4091fb0 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -192,6 +192,27 @@ config BATTERY_DA9052 Say Y here to enable support for batteries charger integrated into DA9052 PMIC. +config CHARGER_DA9150 + tristate "Dialog Semiconductor DA9150 Charger support" + depends on MFD_DA9150 + depends on DA9150_GPADC + depends on IIO + help + Say Y here to enable support for charger unit of the DA9150 + Integrated Charger & Fuel-Gauge IC. + + This driver can also be built as a module. If so, the module will be + called da9150-charger. + +config AXP288_FUEL_GAUGE + tristate "X-Powers AXP288 Fuel Gauge" + depends on MFD_AXP20X && IIO + help + Say yes here to have support for X-Power power management IC (PMIC) + Fuel Gauge. The device provides battery statistics and status + monitoring as well as alerts for battery over/under voltage and + over/under temperature. + config BATTERY_MAX17040 tristate "Maxim MAX17040 Fuel Gauge" depends on I2C diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 36f9e0d..b7b0181 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -1,4 +1,4 @@ -ccflags-$(CONFIG_POWER_SUPPLY_DEBUG) := -DDEBUG +subdir-ccflags-$(CONFIG_POWER_SUPPLY_DEBUG) := -DDEBUG power_supply-y := power_supply_core.o power_supply-$(CONFIG_SYSFS) += power_supply_sysfs.o @@ -32,6 +32,7 @@ obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o +obj-$(CONFIG_CHARGER_DA9150) += da9150-charger.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o obj-$(CONFIG_BATTERY_Z2) += z2_battery.o @@ -62,3 +63,4 @@ obj-$(CONFIG_POWER_AVS) += avs/ obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o obj-$(CONFIG_POWER_RESET) += reset/ +obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c index 4ebf7b0..8f8044e 100644 --- a/drivers/power/ab8500_btemp.c +++ b/drivers/power/ab8500_btemp.c @@ -45,9 +45,6 @@ #define BTEMP_BATCTRL_CURR_SRC_60UA 60 #define BTEMP_BATCTRL_CURR_SRC_120UA 120 -#define to_ab8500_btemp_device_info(x) container_of((x), \ - struct ab8500_btemp, btemp_psy); - /** * struct ab8500_btemp_interrupts - ab8500 interrupts * @name: name of the interrupt @@ -102,7 +99,7 @@ struct ab8500_btemp { struct ab8500_gpadc *gpadc; struct ab8500_fg *fg; struct abx500_bm_data *bm; - struct power_supply btemp_psy; + struct power_supply *btemp_psy; struct ab8500_btemp_events events; struct ab8500_btemp_ranges btemp_ranges; struct workqueue_struct *btemp_wq; @@ -654,14 +651,14 @@ static void ab8500_btemp_periodic_work(struct work_struct *work) if ((di->bat_temp != di->prev_bat_temp) || !di->initialized) { di->initialized = true; di->bat_temp = bat_temp; - power_supply_changed(&di->btemp_psy); + power_supply_changed(di->btemp_psy); } } else if (bat_temp < di->prev_bat_temp) { di->bat_temp--; - power_supply_changed(&di->btemp_psy); + power_supply_changed(di->btemp_psy); } else if (bat_temp > di->prev_bat_temp) { di->bat_temp++; - power_supply_changed(&di->btemp_psy); + power_supply_changed(di->btemp_psy); } di->prev_bat_temp = bat_temp; @@ -689,7 +686,7 @@ static irqreturn_t ab8500_btemp_batctrlindb_handler(int irq, void *_di) dev_err(di->dev, "Battery removal detected!\n"); di->events.batt_rem = true; - power_supply_changed(&di->btemp_psy); + power_supply_changed(di->btemp_psy); return IRQ_HANDLED; } @@ -715,7 +712,7 @@ static irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di) di->events.btemp_high = false; di->events.btemp_medhigh = false; di->events.btemp_lowmed = false; - power_supply_changed(&di->btemp_psy); + power_supply_changed(di->btemp_psy); } return IRQ_HANDLED; @@ -738,7 +735,7 @@ static irqreturn_t ab8500_btemp_temphigh_handler(int irq, void *_di) di->events.btemp_medhigh = false; di->events.btemp_lowmed = false; di->events.btemp_low = false; - power_supply_changed(&di->btemp_psy); + power_supply_changed(di->btemp_psy); return IRQ_HANDLED; } @@ -760,7 +757,7 @@ static irqreturn_t ab8500_btemp_lowmed_handler(int irq, void *_di) di->events.btemp_medhigh = false; di->events.btemp_high = false; di->events.btemp_low = false; - power_supply_changed(&di->btemp_psy); + power_supply_changed(di->btemp_psy); return IRQ_HANDLED; } @@ -782,7 +779,7 @@ static irqreturn_t ab8500_btemp_medhigh_handler(int irq, void *_di) di->events.btemp_lowmed = false; di->events.btemp_high = false; di->events.btemp_low = false; - power_supply_changed(&di->btemp_psy); + power_supply_changed(di->btemp_psy); return IRQ_HANDLED; } @@ -884,9 +881,7 @@ static int ab8500_btemp_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct ab8500_btemp *di; - - di = to_ab8500_btemp_device_info(psy); + struct ab8500_btemp *di = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_PRESENT: @@ -919,14 +914,14 @@ static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data) psy = (struct power_supply *)data; ext = dev_get_drvdata(dev); - di = to_ab8500_btemp_device_info(psy); + di = power_supply_get_drvdata(psy); /* * For all psy where the name of your driver * appears in any supplied_to */ for (i = 0; i < ext->num_supplicants; i++) { - if (!strcmp(ext->supplied_to[i], psy->name)) + if (!strcmp(ext->supplied_to[i], psy->desc->name)) psy_found = true; } @@ -934,16 +929,16 @@ static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data) return 0; /* Go through all properties for the psy */ - for (j = 0; j < ext->num_properties; j++) { + for (j = 0; j < ext->desc->num_properties; j++) { enum power_supply_property prop; - prop = ext->properties[j]; + prop = ext->desc->properties[j]; - if (ext->get_property(ext, prop, &ret)) + if (power_supply_get_property(ext, prop, &ret)) continue; switch (prop) { case POWER_SUPPLY_PROP_PRESENT: - switch (ext->type) { + switch (ext->desc->type) { case POWER_SUPPLY_TYPE_MAINS: /* AC disconnected */ if (!ret.intval && di->events.ac_conn) { @@ -990,10 +985,10 @@ static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data) */ static void ab8500_btemp_external_power_changed(struct power_supply *psy) { - struct ab8500_btemp *di = to_ab8500_btemp_device_info(psy); + struct ab8500_btemp *di = power_supply_get_drvdata(psy); class_for_each_device(power_supply_class, NULL, - &di->btemp_psy, ab8500_btemp_get_ext_psy_data); + di->btemp_psy, ab8500_btemp_get_ext_psy_data); } /* ab8500 btemp driver interrupts and their respective isr */ @@ -1044,7 +1039,7 @@ static int ab8500_btemp_remove(struct platform_device *pdev) destroy_workqueue(di->btemp_wq); flush_scheduled_work(); - power_supply_unregister(&di->btemp_psy); + power_supply_unregister(di->btemp_psy); return 0; } @@ -1054,10 +1049,20 @@ static char *supply_interface[] = { "ab8500_fg", }; +static const struct power_supply_desc ab8500_btemp_desc = { + .name = "ab8500_btemp", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = ab8500_btemp_props, + .num_properties = ARRAY_SIZE(ab8500_btemp_props), + .get_property = ab8500_btemp_get_property, + .external_power_changed = ab8500_btemp_external_power_changed, +}; + static int ab8500_btemp_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct abx500_bm_data *plat = pdev->dev.platform_data; + struct power_supply_config psy_cfg = {}; struct ab8500_btemp *di; int irq, i, ret = 0; u8 val; @@ -1089,17 +1094,9 @@ static int ab8500_btemp_probe(struct platform_device *pdev) di->initialized = false; - /* BTEMP supply */ - di->btemp_psy.name = "ab8500_btemp"; - di->btemp_psy.type = POWER_SUPPLY_TYPE_BATTERY; - di->btemp_psy.properties = ab8500_btemp_props; - di->btemp_psy.num_properties = ARRAY_SIZE(ab8500_btemp_props); - di->btemp_psy.get_property = ab8500_btemp_get_property; - di->btemp_psy.supplied_to = supply_interface; - di->btemp_psy.num_supplicants = ARRAY_SIZE(supply_interface); - di->btemp_psy.external_power_changed = - ab8500_btemp_external_power_changed; - + psy_cfg.supplied_to = supply_interface; + psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); + psy_cfg.drv_data = di; /* Create a work queue for the btemp */ di->btemp_wq = @@ -1140,9 +1137,11 @@ static int ab8500_btemp_probe(struct platform_device *pdev) } /* Register BTEMP power supply class */ - ret = power_supply_register(di->dev, &di->btemp_psy); - if (ret) { + di->btemp_psy = power_supply_register(di->dev, &ab8500_btemp_desc, + &psy_cfg); + if (IS_ERR(di->btemp_psy)) { dev_err(di->dev, "failed to register BTEMP psy\n"); + ret = PTR_ERR(di->btemp_psy); goto free_btemp_wq; } @@ -1171,7 +1170,7 @@ static int ab8500_btemp_probe(struct platform_device *pdev) return ret; free_irq: - power_supply_unregister(&di->btemp_psy); + power_supply_unregister(di->btemp_psy); /* We also have to free all successfully registered irqs */ for (i = i - 1; i >= 0; i--) { diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index 8c8d170..e388171 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -435,7 +435,7 @@ static void ab8500_charger_set_usb_connected(struct ab8500_charger *di, if (!connected) di->flags.vbus_drop_end = false; - sysfs_notify(&di->usb_chg.psy.dev->kobj, NULL, "present"); + sysfs_notify(&di->usb_chg.psy->dev.kobj, NULL, "present"); if (connected) { mutex_lock(&di->charger_attached_mutex); @@ -1516,7 +1516,7 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger, dev_dbg(di->dev, "%s Disabled AC charging\n", __func__); } - ab8500_power_supply_changed(di, &di->ac_chg.psy); + ab8500_power_supply_changed(di, di->ac_chg.psy); return ret; } @@ -1672,7 +1672,7 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger, cancel_delayed_work(&di->check_vbat_work); } - ab8500_power_supply_changed(di, &di->usb_chg.psy); + ab8500_power_supply_changed(di, di->usb_chg.psy); return ret; } @@ -1811,9 +1811,9 @@ static int ab8500_charger_watchdog_kick(struct ux500_charger *charger) int ret; struct ab8500_charger *di; - if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS) + if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS) di = to_ab8500_charger_ac_device_info(charger); - else if (charger->psy.type == POWER_SUPPLY_TYPE_USB) + else if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB) di = to_ab8500_charger_usb_device_info(charger); else return -ENXIO; @@ -1839,9 +1839,9 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger, int ret; struct ab8500_charger *di; - if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS) + if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS) di = to_ab8500_charger_ac_device_info(charger); - else if (charger->psy.type == POWER_SUPPLY_TYPE_USB) + else if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB) di = to_ab8500_charger_usb_device_info(charger); else return -ENXIO; @@ -1879,7 +1879,7 @@ static int ab8540_charger_power_path_enable(struct ux500_charger *charger, int ret; struct ab8500_charger *di; - if (charger->psy.type == POWER_SUPPLY_TYPE_USB) + if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB) di = to_ab8500_charger_usb_device_info(charger); else return -ENXIO; @@ -1910,7 +1910,7 @@ static int ab8540_charger_usb_pre_chg_enable(struct ux500_charger *charger, int ret; struct ab8500_charger *di; - if (charger->psy.type == POWER_SUPPLY_TYPE_USB) + if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB) di = to_ab8500_charger_usb_device_info(charger); else return -ENXIO; @@ -1937,7 +1937,7 @@ static int ab8500_charger_get_ext_psy_data(struct device *dev, void *data) struct ux500_charger *usb_chg; usb_chg = (struct ux500_charger *)data; - psy = &usb_chg->psy; + psy = usb_chg->psy; di = to_ab8500_charger_usb_device_info(usb_chg); @@ -1945,7 +1945,7 @@ static int ab8500_charger_get_ext_psy_data(struct device *dev, void *data) /* For all psy where the driver name appears in any supplied_to */ for (i = 0; i < ext->num_supplicants; i++) { - if (!strcmp(ext->supplied_to[i], psy->name)) + if (!strcmp(ext->supplied_to[i], psy->desc->name)) psy_found = true; } @@ -1953,16 +1953,16 @@ static int ab8500_charger_get_ext_psy_data(struct device *dev, void *data) return 0; /* Go through all properties for the psy */ - for (j = 0; j < ext->num_properties; j++) { + for (j = 0; j < ext->desc->num_properties; j++) { enum power_supply_property prop; - prop = ext->properties[j]; + prop = ext->desc->properties[j]; - if (ext->get_property(ext, prop, &ret)) + if (power_supply_get_property(ext, prop, &ret)) continue; switch (prop) { case POWER_SUPPLY_PROP_VOLTAGE_NOW: - switch (ext->type) { + switch (ext->desc->type) { case POWER_SUPPLY_TYPE_BATTERY: di->vbat = ret.intval / 1000; break; @@ -1993,7 +1993,7 @@ static void ab8500_charger_check_vbat_work(struct work_struct *work) struct ab8500_charger, check_vbat_work.work); class_for_each_device(power_supply_class, NULL, - &di->usb_chg.psy, ab8500_charger_get_ext_psy_data); + di->usb_chg.psy, ab8500_charger_get_ext_psy_data); /* First run old_vbat is 0. */ if (di->old_vbat == 0) @@ -2009,7 +2009,7 @@ static void ab8500_charger_check_vbat_work(struct work_struct *work) di->vbat, di->old_vbat); ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr.usb_type_max); - power_supply_changed(&di->usb_chg.psy); + power_supply_changed(di->usb_chg.psy); } di->old_vbat = di->vbat; @@ -2049,7 +2049,7 @@ static void ab8500_charger_check_hw_failure_work(struct work_struct *work) } if (!(reg_value & MAIN_CH_NOK)) { di->flags.mainextchnotok = false; - ab8500_power_supply_changed(di, &di->ac_chg.psy); + ab8500_power_supply_changed(di, di->ac_chg.psy); } } if (di->flags.vbus_ovv) { @@ -2062,7 +2062,7 @@ static void ab8500_charger_check_hw_failure_work(struct work_struct *work) } if (!(reg_value & VBUS_OVV_TH)) { di->flags.vbus_ovv = false; - ab8500_power_supply_changed(di, &di->usb_chg.psy); + ab8500_power_supply_changed(di, di->usb_chg.psy); } } /* If we still have a failure, schedule a new check */ @@ -2132,8 +2132,8 @@ static void ab8500_charger_ac_work(struct work_struct *work) di->ac.charger_connected = 0; } - ab8500_power_supply_changed(di, &di->ac_chg.psy); - sysfs_notify(&di->ac_chg.psy.dev->kobj, NULL, "present"); + ab8500_power_supply_changed(di, di->ac_chg.psy); + sysfs_notify(&di->ac_chg.psy->dev.kobj, NULL, "present"); } static void ab8500_charger_usb_attached_work(struct work_struct *work) @@ -2240,7 +2240,7 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work) dev_dbg(di->dev, "%s di->vbus_detected = false\n", __func__); di->vbus_detected = false; ab8500_charger_set_usb_connected(di, false); - ab8500_power_supply_changed(di, &di->usb_chg.psy); + ab8500_power_supply_changed(di, di->usb_chg.psy); } else { dev_dbg(di->dev, "%s di->vbus_detected = true\n", __func__); di->vbus_detected = true; @@ -2250,7 +2250,7 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work) if (!ret) { ab8500_charger_set_usb_connected(di, true); ab8500_power_supply_changed(di, - &di->usb_chg.psy); + di->usb_chg.psy); } } else { /* @@ -2267,7 +2267,7 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work) ab8500_charger_set_usb_connected(di, true); ab8500_power_supply_changed(di, - &di->usb_chg.psy); + di->usb_chg.psy); } } } @@ -2295,7 +2295,7 @@ static void ab8500_charger_usb_link_attach_work(struct work_struct *work) } ab8500_charger_set_usb_connected(di, true); - ab8500_power_supply_changed(di, &di->usb_chg.psy); + ab8500_power_supply_changed(di, di->usb_chg.psy); } /** @@ -2393,7 +2393,7 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work) if (!(detected_chargers & USB_PW_CONN)) { di->vbus_detected = false; ab8500_charger_set_usb_connected(di, false); - ab8500_power_supply_changed(di, &di->usb_chg.psy); + ab8500_power_supply_changed(di, di->usb_chg.psy); return; } @@ -2404,7 +2404,7 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work) if (ret == -ENXIO) { /* No valid charger type detected */ ab8500_charger_set_usb_connected(di, false); - ab8500_power_supply_changed(di, &di->usb_chg.psy); + ab8500_power_supply_changed(di, di->usb_chg.psy); } return; } @@ -2463,7 +2463,7 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work) case AB8500_BM_USB_STATE_SUSPEND: case AB8500_BM_USB_STATE_MAX: ab8500_charger_set_usb_connected(di, false); - ab8500_power_supply_changed(di, &di->usb_chg.psy); + ab8500_power_supply_changed(di, di->usb_chg.psy); break; case AB8500_BM_USB_STATE_RESUME: @@ -2486,7 +2486,7 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work) return; ab8500_charger_set_usb_connected(di, true); - ab8500_power_supply_changed(di, &di->usb_chg.psy); + ab8500_power_supply_changed(di, di->usb_chg.psy); } break; @@ -2530,7 +2530,7 @@ static void ab8500_charger_check_usbchargernotok_work(struct work_struct *work) } if (prev_status != di->flags.usbchargernotok) - ab8500_power_supply_changed(di, &di->usb_chg.psy); + ab8500_power_supply_changed(di, di->usb_chg.psy); } /** @@ -2560,7 +2560,7 @@ static void ab8500_charger_check_main_thermal_prot_work( else di->flags.main_thermal_prot = false; - ab8500_power_supply_changed(di, &di->ac_chg.psy); + ab8500_power_supply_changed(di, di->ac_chg.psy); } /** @@ -2590,7 +2590,7 @@ static void ab8500_charger_check_usb_thermal_prot_work( else di->flags.usb_thermal_prot = false; - ab8500_power_supply_changed(di, &di->usb_chg.psy); + ab8500_power_supply_changed(di, di->usb_chg.psy); } /** @@ -2651,7 +2651,7 @@ static irqreturn_t ab8500_charger_mainextchnotok_handler(int irq, void *_di) dev_dbg(di->dev, "Main charger not ok\n"); di->flags.mainextchnotok = true; - ab8500_power_supply_changed(di, &di->ac_chg.psy); + ab8500_power_supply_changed(di, di->ac_chg.psy); /* Schedule a new HW failure check */ queue_delayed_work(di->charger_wq, &di->check_hw_failure_work, 0); @@ -2880,11 +2880,11 @@ static irqreturn_t ab8500_charger_chwdexp_handler(int irq, void *_di) */ if (di->ac.charger_online) { di->ac.wd_expired = true; - ab8500_power_supply_changed(di, &di->ac_chg.psy); + ab8500_power_supply_changed(di, di->ac_chg.psy); } if (di->usb.charger_online) { di->usb.wd_expired = true; - ab8500_power_supply_changed(di, &di->usb_chg.psy); + ab8500_power_supply_changed(di, di->usb_chg.psy); } return IRQ_HANDLED; @@ -2927,7 +2927,7 @@ static irqreturn_t ab8500_charger_vbusovv_handler(int irq, void *_di) dev_dbg(di->dev, "VBUS overvoltage detected\n"); di->flags.vbus_ovv = true; - ab8500_power_supply_changed(di, &di->usb_chg.psy); + ab8500_power_supply_changed(di, di->usb_chg.psy); /* Schedule a new HW failure check */ queue_delayed_work(di->charger_wq, &di->check_hw_failure_work, 0); @@ -3428,10 +3428,10 @@ static int ab8500_charger_remove(struct platform_device *pdev) flush_scheduled_work(); if (di->usb_chg.enabled) - power_supply_unregister(&di->usb_chg.psy); + power_supply_unregister(di->usb_chg.psy); if (di->ac_chg.enabled && !di->ac_chg.external) - power_supply_unregister(&di->ac_chg.psy); + power_supply_unregister(di->ac_chg.psy); return 0; } @@ -3442,10 +3442,27 @@ static char *supply_interface[] = { "ab8500_btemp", }; +static const struct power_supply_desc ab8500_ac_chg_desc = { + .name = "ab8500_ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = ab8500_charger_ac_props, + .num_properties = ARRAY_SIZE(ab8500_charger_ac_props), + .get_property = ab8500_charger_ac_get_property, +}; + +static const struct power_supply_desc ab8500_usb_chg_desc = { + .name = "ab8500_usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = ab8500_charger_usb_props, + .num_properties = ARRAY_SIZE(ab8500_charger_usb_props), + .get_property = ab8500_charger_usb_get_property, +}; + static int ab8500_charger_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct abx500_bm_data *plat = pdev->dev.platform_data; + struct power_supply_config ac_psy_cfg = {}, usb_psy_cfg = {}; struct ab8500_charger *di; int irq, i, charger_status, ret = 0, ch_stat; @@ -3483,15 +3500,15 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->autopower = false; di->invalid_charger_detect_state = 0; + /* AC and USB supply config */ + ac_psy_cfg.supplied_to = supply_interface; + ac_psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); + ac_psy_cfg.drv_data = &di->ac_chg; + usb_psy_cfg.supplied_to = supply_interface; + usb_psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); + usb_psy_cfg.drv_data = &di->usb_chg; + /* AC supply */ - /* power_supply base class */ - di->ac_chg.psy.name = "ab8500_ac"; - di->ac_chg.psy.type = POWER_SUPPLY_TYPE_MAINS; - di->ac_chg.psy.properties = ab8500_charger_ac_props; - di->ac_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_ac_props); - di->ac_chg.psy.get_property = ab8500_charger_ac_get_property; - di->ac_chg.psy.supplied_to = supply_interface; - di->ac_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface), /* ux500_charger sub-class */ di->ac_chg.ops.enable = &ab8500_charger_ac_en; di->ac_chg.ops.check_enable = &ab8500_charger_ac_check_enable; @@ -3511,14 +3528,6 @@ static int ab8500_charger_probe(struct platform_device *pdev) &charger_notifier_list, &charger_nb); /* USB supply */ - /* power_supply base class */ - di->usb_chg.psy.name = "ab8500_usb"; - di->usb_chg.psy.type = POWER_SUPPLY_TYPE_USB; - di->usb_chg.psy.properties = ab8500_charger_usb_props; - di->usb_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_usb_props); - di->usb_chg.psy.get_property = ab8500_charger_usb_get_property; - di->usb_chg.psy.supplied_to = supply_interface; - di->usb_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface), /* ux500_charger sub-class */ di->usb_chg.ops.enable = &ab8500_charger_usb_en; di->usb_chg.ops.check_enable = &ab8500_charger_usb_check_enable; @@ -3616,18 +3625,24 @@ static int ab8500_charger_probe(struct platform_device *pdev) /* Register AC charger class */ if (di->ac_chg.enabled) { - ret = power_supply_register(di->dev, &di->ac_chg.psy); - if (ret) { + di->ac_chg.psy = power_supply_register(di->dev, + &ab8500_ac_chg_desc, + &ac_psy_cfg); + if (IS_ERR(di->ac_chg.psy)) { dev_err(di->dev, "failed to register AC charger\n"); + ret = PTR_ERR(di->ac_chg.psy); goto free_charger_wq; } } /* Register USB charger class */ if (di->usb_chg.enabled) { - ret = power_supply_register(di->dev, &di->usb_chg.psy); - if (ret) { + di->usb_chg.psy = power_supply_register(di->dev, + &ab8500_usb_chg_desc, + &usb_psy_cfg); + if (IS_ERR(di->usb_chg.psy)) { dev_err(di->dev, "failed to register USB charger\n"); + ret = PTR_ERR(di->usb_chg.psy); goto free_ac; } } @@ -3650,8 +3665,8 @@ static int ab8500_charger_probe(struct platform_device *pdev) if (charger_status & AC_PW_CONN) { di->ac.charger_connected = 1; di->ac_conn = true; - ab8500_power_supply_changed(di, &di->ac_chg.psy); - sysfs_notify(&di->ac_chg.psy.dev->kobj, NULL, "present"); + ab8500_power_supply_changed(di, di->ac_chg.psy); + sysfs_notify(&di->ac_chg.psy->dev.kobj, NULL, "present"); } if (charger_status & USB_PW_CONN) { @@ -3712,10 +3727,10 @@ put_usb_phy: usb_put_phy(di->usb_phy); free_usb: if (di->usb_chg.enabled) - power_supply_unregister(&di->usb_chg.psy); + power_supply_unregister(di->usb_chg.psy); free_ac: if (di->ac_chg.enabled) - power_supply_unregister(&di->ac_chg.psy); + power_supply_unregister(di->ac_chg.psy); free_charger_wq: destroy_workqueue(di->charger_wq); return ret; diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index c908658..3830dad 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -57,9 +57,6 @@ #define interpolate(x, x1, y1, x2, y2) \ ((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1)))); -#define to_ab8500_fg_device_info(x) container_of((x), \ - struct ab8500_fg, fg_psy); - /** * struct ab8500_fg_interrupts - ab8500 fg interupts * @name: name of the interrupt @@ -229,7 +226,7 @@ struct ab8500_fg { struct ab8500 *parent; struct ab8500_gpadc *gpadc; struct abx500_bm_data *bm; - struct power_supply fg_psy; + struct power_supply *fg_psy; struct workqueue_struct *fg_wq; struct delayed_work fg_periodic_work; struct delayed_work fg_low_bat_work; @@ -622,14 +619,14 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res) u8 low, high; int val; int ret; - int timeout; + unsigned long timeout; if (!completion_done(&di->ab8500_fg_complete)) { timeout = wait_for_completion_timeout( &di->ab8500_fg_complete, INS_CURR_TIMEOUT); dev_dbg(di->dev, "Finalize time: %d ms\n", - ((INS_CURR_TIMEOUT - timeout) * 1000) / HZ); + jiffies_to_msecs(INS_CURR_TIMEOUT - timeout)); if (!timeout) { ret = -ETIME; disable_irq(di->irq); @@ -716,7 +713,7 @@ fail: int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di) { int ret; - int timeout; + unsigned long timeout; int res = 0; ret = ab8500_fg_inst_curr_start(di); @@ -731,7 +728,7 @@ int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di) &di->ab8500_fg_started, INS_CURR_TIMEOUT); dev_dbg(di->dev, "Start time: %d ms\n", - ((INS_CURR_TIMEOUT - timeout) * 1000) / HZ); + jiffies_to_msecs(INS_CURR_TIMEOUT - timeout)); if (!timeout) { ret = -ETIME; dev_err(di->dev, "completion timed out [%d]\n", @@ -1391,7 +1388,7 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init) di->bat_cap.prev_percent, di->bat_cap.cap_scale.scaled_cap); } - power_supply_changed(&di->fg_psy); + power_supply_changed(di->fg_psy); if (di->flags.fully_charged && di->flags.force_full) { dev_dbg(di->dev, "Battery full, notifying.\n"); di->flags.force_full = false; @@ -1850,7 +1847,7 @@ static void ab8500_fg_check_hw_failure_work(struct work_struct *work) if (!di->flags.bat_ovv) { dev_dbg(di->dev, "Battery OVV\n"); di->flags.bat_ovv = true; - power_supply_changed(&di->fg_psy); + power_supply_changed(di->fg_psy); } /* Not yet recovered from ovv, reschedule this test */ queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, @@ -1858,7 +1855,7 @@ static void ab8500_fg_check_hw_failure_work(struct work_struct *work) } else { dev_dbg(di->dev, "Battery recovered from OVV\n"); di->flags.bat_ovv = false; - power_supply_changed(&di->fg_psy); + power_supply_changed(di->fg_psy); } } @@ -2096,9 +2093,7 @@ static int ab8500_fg_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); /* * If battery is identified as unknown and charging of unknown @@ -2181,14 +2176,14 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) psy = (struct power_supply *)data; ext = dev_get_drvdata(dev); - di = to_ab8500_fg_device_info(psy); + di = power_supply_get_drvdata(psy); /* * For all psy where the name of your driver * appears in any supplied_to */ for (i = 0; i < ext->num_supplicants; i++) { - if (!strcmp(ext->supplied_to[i], psy->name)) + if (!strcmp(ext->supplied_to[i], psy->desc->name)) psy_found = true; } @@ -2196,16 +2191,16 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) return 0; /* Go through all properties for the psy */ - for (j = 0; j < ext->num_properties; j++) { + for (j = 0; j < ext->desc->num_properties; j++) { enum power_supply_property prop; - prop = ext->properties[j]; + prop = ext->desc->properties[j]; - if (ext->get_property(ext, prop, &ret)) + if (power_supply_get_property(ext, prop, &ret)) continue; switch (prop) { case POWER_SUPPLY_PROP_STATUS: - switch (ext->type) { + switch (ext->desc->type) { case POWER_SUPPLY_TYPE_BATTERY: switch (ret.intval) { case POWER_SUPPLY_STATUS_UNKNOWN: @@ -2244,7 +2239,7 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) }; break; case POWER_SUPPLY_PROP_TECHNOLOGY: - switch (ext->type) { + switch (ext->desc->type) { case POWER_SUPPLY_TYPE_BATTERY: if (!di->flags.batt_id_received && di->bm->batt_id != BATTERY_UNKNOWN) { @@ -2274,7 +2269,7 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) } break; case POWER_SUPPLY_PROP_TEMP: - switch (ext->type) { + switch (ext->desc->type) { case POWER_SUPPLY_TYPE_BATTERY: if (di->flags.batt_id_received) di->bat_temp = ret.intval; @@ -2399,10 +2394,10 @@ out: */ static void ab8500_fg_external_power_changed(struct power_supply *psy) { - struct ab8500_fg *di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); class_for_each_device(power_supply_class, NULL, - &di->fg_psy, ab8500_fg_get_ext_psy_data); + di->fg_psy, ab8500_fg_get_ext_psy_data); } /** @@ -2580,9 +2575,7 @@ static ssize_t ab8505_powercut_flagtime_read(struct device *dev, int ret; u8 reg_value; struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, AB8505_RTC_PCUT_FLAG_TIME_REG, ®_value); @@ -2605,9 +2598,7 @@ static ssize_t ab8505_powercut_flagtime_write(struct device *dev, int ret; long unsigned reg_value; struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); reg_value = simple_strtoul(buf, NULL, 10); @@ -2633,9 +2624,7 @@ static ssize_t ab8505_powercut_maxtime_read(struct device *dev, int ret; u8 reg_value; struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, AB8505_RTC_PCUT_MAX_TIME_REG, ®_value); @@ -2659,9 +2648,7 @@ static ssize_t ab8505_powercut_maxtime_write(struct device *dev, int ret; int reg_value; struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); reg_value = simple_strtoul(buf, NULL, 10); if (reg_value > 0x7F) { @@ -2686,9 +2673,7 @@ static ssize_t ab8505_powercut_restart_read(struct device *dev, int ret; u8 reg_value; struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, AB8505_RTC_PCUT_RESTART_REG, ®_value); @@ -2711,9 +2696,7 @@ static ssize_t ab8505_powercut_restart_write(struct device *dev, int ret; int reg_value; struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); reg_value = simple_strtoul(buf, NULL, 10); if (reg_value > 0xF) { @@ -2739,9 +2722,7 @@ static ssize_t ab8505_powercut_timer_read(struct device *dev, int ret; u8 reg_value; struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, AB8505_RTC_PCUT_TIME_REG, ®_value); @@ -2764,9 +2745,7 @@ static ssize_t ab8505_powercut_restart_counter_read(struct device *dev, int ret; u8 reg_value; struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, AB8505_RTC_PCUT_RESTART_REG, ®_value); @@ -2789,9 +2768,7 @@ static ssize_t ab8505_powercut_read(struct device *dev, int ret; u8 reg_value; struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value); @@ -2812,9 +2789,7 @@ static ssize_t ab8505_powercut_write(struct device *dev, int ret; int reg_value; struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); reg_value = simple_strtoul(buf, NULL, 10); if (reg_value > 0x1) { @@ -2840,9 +2815,7 @@ static ssize_t ab8505_powercut_flag_read(struct device *dev, int ret; u8 reg_value; struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value); @@ -2865,9 +2838,7 @@ static ssize_t ab8505_powercut_debounce_read(struct device *dev, int ret; u8 reg_value; struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, AB8505_RTC_PCUT_DEBOUNCE_REG, ®_value); @@ -2890,9 +2861,7 @@ static ssize_t ab8505_powercut_debounce_write(struct device *dev, int ret; int reg_value; struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); reg_value = simple_strtoul(buf, NULL, 10); if (reg_value > 0x7) { @@ -2917,9 +2886,7 @@ static ssize_t ab8505_powercut_enable_status_read(struct device *dev, int ret; u8 reg_value; struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); + struct ab8500_fg *di = power_supply_get_drvdata(psy); ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value); @@ -2954,44 +2921,38 @@ static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = { ab8505_powercut_enable_status_read, NULL), }; -static int ab8500_fg_sysfs_psy_create_attrs(struct device *dev) +static int ab8500_fg_sysfs_psy_create_attrs(struct ab8500_fg *di) { unsigned int i; - struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); if (((is_ab8505(di->parent) || is_ab9540(di->parent)) && - abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0) + abx500_get_chip_id(di->dev) >= AB8500_CUT2P0) || is_ab8540(di->parent)) { for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++) - if (device_create_file(dev, + if (device_create_file(&di->fg_psy->dev, &ab8505_fg_sysfs_psy_attrs[i])) goto sysfs_psy_create_attrs_failed_ab8505; } return 0; sysfs_psy_create_attrs_failed_ab8505: - dev_err(dev, "Failed creating sysfs psy attrs for ab8505.\n"); + dev_err(&di->fg_psy->dev, "Failed creating sysfs psy attrs for ab8505.\n"); while (i--) - device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]); + device_remove_file(&di->fg_psy->dev, + &ab8505_fg_sysfs_psy_attrs[i]); return -EIO; } -static void ab8500_fg_sysfs_psy_remove_attrs(struct device *dev) +static void ab8500_fg_sysfs_psy_remove_attrs(struct ab8500_fg *di) { unsigned int i; - struct power_supply *psy = dev_get_drvdata(dev); - struct ab8500_fg *di; - - di = to_ab8500_fg_device_info(psy); if (((is_ab8505(di->parent) || is_ab9540(di->parent)) && - abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0) + abx500_get_chip_id(di->dev) >= AB8500_CUT2P0) || is_ab8540(di->parent)) { for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++) - (void)device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]); + (void)device_remove_file(&di->fg_psy->dev, + &ab8505_fg_sysfs_psy_attrs[i]); } } @@ -3056,17 +3017,20 @@ static int ab8500_fg_remove(struct platform_device *pdev) ab8500_fg_sysfs_exit(di); flush_scheduled_work(); - ab8500_fg_sysfs_psy_remove_attrs(di->fg_psy.dev); - power_supply_unregister(&di->fg_psy); + ab8500_fg_sysfs_psy_remove_attrs(di); + power_supply_unregister(di->fg_psy); return ret; } /* ab8500 fg driver interrupts and their respective isr */ -static struct ab8500_fg_interrupts ab8500_fg_irq[] = { +static struct ab8500_fg_interrupts ab8500_fg_irq_th[] = { {"NCONV_ACCU", ab8500_fg_cc_convend_handler}, {"BATT_OVV", ab8500_fg_batt_ovv_handler}, {"LOW_BAT_F", ab8500_fg_lowbatf_handler}, {"CC_INT_CALIB", ab8500_fg_cc_int_calib_handler}, +}; + +static struct ab8500_fg_interrupts ab8500_fg_irq_bh[] = { {"CCEOC", ab8500_fg_cc_data_end_handler}, }; @@ -3075,10 +3039,20 @@ static char *supply_interface[] = { "ab8500_usb", }; +static const struct power_supply_desc ab8500_fg_desc = { + .name = "ab8500_fg", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = ab8500_fg_props, + .num_properties = ARRAY_SIZE(ab8500_fg_props), + .get_property = ab8500_fg_get_property, + .external_power_changed = ab8500_fg_external_power_changed, +}; + static int ab8500_fg_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct abx500_bm_data *plat = pdev->dev.platform_data; + struct power_supply_config psy_cfg = {}; struct ab8500_fg *di; int i, irq; int ret = 0; @@ -3110,14 +3084,9 @@ static int ab8500_fg_probe(struct platform_device *pdev) di->parent = dev_get_drvdata(pdev->dev.parent); di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - di->fg_psy.name = "ab8500_fg"; - di->fg_psy.type = POWER_SUPPLY_TYPE_BATTERY; - di->fg_psy.properties = ab8500_fg_props; - di->fg_psy.num_properties = ARRAY_SIZE(ab8500_fg_props); - di->fg_psy.get_property = ab8500_fg_get_property; - di->fg_psy.supplied_to = supply_interface; - di->fg_psy.num_supplicants = ARRAY_SIZE(supply_interface), - di->fg_psy.external_power_changed = ab8500_fg_external_power_changed; + psy_cfg.supplied_to = supply_interface; + psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); + psy_cfg.drv_data = di; di->bat_cap.max_mah_design = MILLI_TO_MICRO * di->bm->bat_type[di->bm->batt_id].charge_full_design; @@ -3178,9 +3147,10 @@ static int ab8500_fg_probe(struct platform_device *pdev) di->flags.batt_id_received = false; /* Register FG power supply class */ - ret = power_supply_register(di->dev, &di->fg_psy); - if (ret) { + di->fg_psy = power_supply_register(di->dev, &ab8500_fg_desc, &psy_cfg); + if (IS_ERR(di->fg_psy)) { dev_err(di->dev, "failed to register FG psy\n"); + ret = PTR_ERR(di->fg_psy); goto free_inst_curr_wq; } @@ -3194,21 +3164,36 @@ static int ab8500_fg_probe(struct platform_device *pdev) init_completion(&di->ab8500_fg_started); init_completion(&di->ab8500_fg_complete); - /* Register interrupts */ - for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq); i++) { - irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name); - ret = request_threaded_irq(irq, NULL, ab8500_fg_irq[i].isr, - IRQF_SHARED | IRQF_NO_SUSPEND, - ab8500_fg_irq[i].name, di); + /* Register primary interrupt handlers */ + for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq_th); i++) { + irq = platform_get_irq_byname(pdev, ab8500_fg_irq_th[i].name); + ret = request_irq(irq, ab8500_fg_irq_th[i].isr, + IRQF_SHARED | IRQF_NO_SUSPEND, + ab8500_fg_irq_th[i].name, di); if (ret != 0) { - dev_err(di->dev, "failed to request %s IRQ %d: %d\n" - , ab8500_fg_irq[i].name, irq, ret); + dev_err(di->dev, "failed to request %s IRQ %d: %d\n", + ab8500_fg_irq_th[i].name, irq, ret); goto free_irq; } dev_dbg(di->dev, "Requested %s IRQ %d: %d\n", - ab8500_fg_irq[i].name, irq, ret); + ab8500_fg_irq_th[i].name, irq, ret); } + + /* Register threaded interrupt handler */ + irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name); + ret = request_threaded_irq(irq, NULL, ab8500_fg_irq_bh[0].isr, + IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, + ab8500_fg_irq_bh[0].name, di); + + if (ret != 0) { + dev_err(di->dev, "failed to request %s IRQ %d: %d\n", + ab8500_fg_irq_bh[0].name, irq, ret); + goto free_irq; + } + dev_dbg(di->dev, "Requested %s IRQ %d: %d\n", + ab8500_fg_irq_bh[0].name, irq, ret); + di->irq = platform_get_irq_byname(pdev, "CCEOC"); disable_irq(di->irq); di->nbr_cceoc_irq_cnt = 0; @@ -3221,7 +3206,7 @@ static int ab8500_fg_probe(struct platform_device *pdev) goto free_irq; } - ret = ab8500_fg_sysfs_psy_create_attrs(di->fg_psy.dev); + ret = ab8500_fg_sysfs_psy_create_attrs(di); if (ret) { dev_err(di->dev, "failed to create FG psy\n"); ab8500_fg_sysfs_exit(di); @@ -3243,13 +3228,15 @@ static int ab8500_fg_probe(struct platform_device *pdev) return ret; free_irq: - power_supply_unregister(&di->fg_psy); + power_supply_unregister(di->fg_psy); - /* We also have to free all successfully registered irqs */ - for (i = i - 1; i >= 0; i--) { - irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name); + /* We also have to free all registered irqs */ + for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq_th); i++) { + irq = platform_get_irq_byname(pdev, ab8500_fg_irq_th[i].name); free_irq(irq, di); } + irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name); + free_irq(irq, di); free_inst_curr_wq: destroy_workqueue(di->fg_wq); return ret; diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c index ab54b8d..541f702 100644 --- a/drivers/power/abx500_chargalg.c +++ b/drivers/power/abx500_chargalg.c @@ -50,9 +50,6 @@ #define CHARGALG_CURR_STEP_LOW 0 #define CHARGALG_CURR_STEP_HIGH 100 -#define to_abx500_chargalg_device_info(x) container_of((x), \ - struct abx500_chargalg, chargalg_psy); - enum abx500_chargers { NO_CHG, AC_CHG, @@ -256,7 +253,7 @@ struct abx500_chargalg { struct ab8500 *parent; struct abx500_chargalg_current_step_status curr_status; struct abx500_bm_data *bm; - struct power_supply chargalg_psy; + struct power_supply *chargalg_psy; struct ux500_charger *ac_chg; struct ux500_charger *usb_chg; struct abx500_chargalg_events events; @@ -695,7 +692,7 @@ static void abx500_chargalg_stop_charging(struct abx500_chargalg *di) di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; di->maintenance_chg = false; cancel_delayed_work(&di->chargalg_wd_work); - power_supply_changed(&di->chargalg_psy); + power_supply_changed(di->chargalg_psy); } /** @@ -715,7 +712,7 @@ static void abx500_chargalg_hold_charging(struct abx500_chargalg *di) di->charge_status = POWER_SUPPLY_STATUS_CHARGING; di->maintenance_chg = false; cancel_delayed_work(&di->chargalg_wd_work); - power_supply_changed(&di->chargalg_psy); + power_supply_changed(di->chargalg_psy); } /** @@ -842,7 +839,7 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di) di->charge_status = POWER_SUPPLY_STATUS_FULL; di->maintenance_chg = true; dev_dbg(di->dev, "EOC reached!\n"); - power_supply_changed(&di->chargalg_psy); + power_supply_changed(di->chargalg_psy); } else { dev_dbg(di->dev, " EOC limit reached for the %d" @@ -987,10 +984,10 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) psy = (struct power_supply *)data; ext = dev_get_drvdata(dev); - di = to_abx500_chargalg_device_info(psy); + di = power_supply_get_drvdata(psy); /* For all psy where the driver name appears in any supplied_to */ for (i = 0; i < ext->num_supplicants; i++) { - if (!strcmp(ext->supplied_to[i], psy->name)) + if (!strcmp(ext->supplied_to[i], psy->desc->name)) psy_found = true; } if (!psy_found) @@ -1001,29 +998,31 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) * property because of handling that sysfs entry on its own, this is * the place to get the battery capacity. */ - if (!ext->get_property(ext, POWER_SUPPLY_PROP_CAPACITY, &ret)) { + if (!power_supply_get_property(ext, POWER_SUPPLY_PROP_CAPACITY, &ret)) { di->batt_data.percent = ret.intval; capacity_updated = true; } /* Go through all properties for the psy */ - for (j = 0; j < ext->num_properties; j++) { + for (j = 0; j < ext->desc->num_properties; j++) { enum power_supply_property prop; - prop = ext->properties[j]; + prop = ext->desc->properties[j]; - /* Initialize chargers if not already done */ + /* + * Initialize chargers if not already done. + * The ab8500_charger*/ if (!di->ac_chg && - ext->type == POWER_SUPPLY_TYPE_MAINS) + ext->desc->type == POWER_SUPPLY_TYPE_MAINS) di->ac_chg = psy_to_ux500_charger(ext); else if (!di->usb_chg && - ext->type == POWER_SUPPLY_TYPE_USB) + ext->desc->type == POWER_SUPPLY_TYPE_USB) di->usb_chg = psy_to_ux500_charger(ext); - if (ext->get_property(ext, prop, &ret)) + if (power_supply_get_property(ext, prop, &ret)) continue; switch (prop) { case POWER_SUPPLY_PROP_PRESENT: - switch (ext->type) { + switch (ext->desc->type) { case POWER_SUPPLY_TYPE_BATTERY: /* Battery present */ if (ret.intval) @@ -1070,7 +1069,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) break; case POWER_SUPPLY_PROP_ONLINE: - switch (ext->type) { + switch (ext->desc->type) { case POWER_SUPPLY_TYPE_BATTERY: break; case POWER_SUPPLY_TYPE_MAINS: @@ -1115,7 +1114,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) break; case POWER_SUPPLY_PROP_HEALTH: - switch (ext->type) { + switch (ext->desc->type) { case POWER_SUPPLY_TYPE_BATTERY: break; case POWER_SUPPLY_TYPE_MAINS: @@ -1198,7 +1197,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - switch (ext->type) { + switch (ext->desc->type) { case POWER_SUPPLY_TYPE_BATTERY: di->batt_data.volt = ret.intval / 1000; break; @@ -1214,7 +1213,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) break; case POWER_SUPPLY_PROP_VOLTAGE_AVG: - switch (ext->type) { + switch (ext->desc->type) { case POWER_SUPPLY_TYPE_MAINS: /* AVG is used to indicate when we are * in CV mode */ @@ -1239,7 +1238,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) break; case POWER_SUPPLY_PROP_TECHNOLOGY: - switch (ext->type) { + switch (ext->desc->type) { case POWER_SUPPLY_TYPE_BATTERY: if (ret.intval) di->events.batt_unknown = false; @@ -1257,7 +1256,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) break; case POWER_SUPPLY_PROP_CURRENT_NOW: - switch (ext->type) { + switch (ext->desc->type) { case POWER_SUPPLY_TYPE_MAINS: di->chg_info.ac_curr = ret.intval / 1000; @@ -1275,7 +1274,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) break; case POWER_SUPPLY_PROP_CURRENT_AVG: - switch (ext->type) { + switch (ext->desc->type) { case POWER_SUPPLY_TYPE_BATTERY: di->batt_data.avg_curr = ret.intval / 1000; break; @@ -1311,7 +1310,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) */ static void abx500_chargalg_external_power_changed(struct power_supply *psy) { - struct abx500_chargalg *di = to_abx500_chargalg_device_info(psy); + struct abx500_chargalg *di = power_supply_get_drvdata(psy); /* * Trigger execution of the algorithm instantly and read @@ -1336,7 +1335,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) /* Collect data from all power_supply class devices */ class_for_each_device(power_supply_class, NULL, - &di->chargalg_psy, abx500_chargalg_get_ext_psy_data); + di->chargalg_psy, abx500_chargalg_get_ext_psy_data); abx500_chargalg_end_of_charge(di); abx500_chargalg_check_temp(di); @@ -1478,7 +1477,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; di->maintenance_chg = false; abx500_chargalg_state_to(di, STATE_SUSPENDED); - power_supply_changed(&di->chargalg_psy); + power_supply_changed(di->chargalg_psy); /* Intentional fallthrough */ case STATE_SUSPENDED: @@ -1576,7 +1575,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) di->charge_status = POWER_SUPPLY_STATUS_CHARGING; di->eoc_cnt = 0; di->maintenance_chg = false; - power_supply_changed(&di->chargalg_psy); + power_supply_changed(di->chargalg_psy); break; @@ -1624,7 +1623,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) di->bm->bat_type[ di->bm->batt_id].maint_a_cur_lvl); abx500_chargalg_state_to(di, STATE_MAINTENANCE_A); - power_supply_changed(&di->chargalg_psy); + power_supply_changed(di->chargalg_psy); /* Intentional fallthrough*/ case STATE_MAINTENANCE_A: @@ -1644,7 +1643,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) di->bm->bat_type[ di->bm->batt_id].maint_b_cur_lvl); abx500_chargalg_state_to(di, STATE_MAINTENANCE_B); - power_supply_changed(&di->chargalg_psy); + power_supply_changed(di->chargalg_psy); /* Intentional fallthrough*/ case STATE_MAINTENANCE_B: @@ -1663,7 +1662,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) abx500_chargalg_stop_maintenance_timer(di); di->charge_status = POWER_SUPPLY_STATUS_CHARGING; abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH); - power_supply_changed(&di->chargalg_psy); + power_supply_changed(di->chargalg_psy); /* Intentional fallthrough */ case STATE_TEMP_LOWHIGH: @@ -1779,9 +1778,7 @@ static int abx500_chargalg_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct abx500_chargalg *di; - - di = to_abx500_chargalg_device_info(psy); + struct abx500_chargalg *di = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_STATUS: @@ -2034,7 +2031,7 @@ static int abx500_chargalg_remove(struct platform_device *pdev) /* Delete the work queue */ destroy_workqueue(di->chargalg_wq); - power_supply_unregister(&di->chargalg_psy); + power_supply_unregister(di->chargalg_psy); return 0; } @@ -2043,10 +2040,20 @@ static char *supply_interface[] = { "ab8500_fg", }; +static const struct power_supply_desc abx500_chargalg_desc = { + .name = "abx500_chargalg", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = abx500_chargalg_props, + .num_properties = ARRAY_SIZE(abx500_chargalg_props), + .get_property = abx500_chargalg_get_property, + .external_power_changed = abx500_chargalg_external_power_changed, +}; + static int abx500_chargalg_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct abx500_bm_data *plat = pdev->dev.platform_data; + struct power_supply_config psy_cfg = {}; struct abx500_chargalg *di; int ret = 0; @@ -2074,16 +2081,9 @@ static int abx500_chargalg_probe(struct platform_device *pdev) di->dev = &pdev->dev; di->parent = dev_get_drvdata(pdev->dev.parent); - /* chargalg supply */ - di->chargalg_psy.name = "abx500_chargalg"; - di->chargalg_psy.type = POWER_SUPPLY_TYPE_BATTERY; - di->chargalg_psy.properties = abx500_chargalg_props; - di->chargalg_psy.num_properties = ARRAY_SIZE(abx500_chargalg_props); - di->chargalg_psy.get_property = abx500_chargalg_get_property; - di->chargalg_psy.supplied_to = supply_interface; - di->chargalg_psy.num_supplicants = ARRAY_SIZE(supply_interface), - di->chargalg_psy.external_power_changed = - abx500_chargalg_external_power_changed; + psy_cfg.supplied_to = supply_interface; + psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); + psy_cfg.drv_data = di; /* Initilialize safety timer */ hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); @@ -2115,9 +2115,11 @@ static int abx500_chargalg_probe(struct platform_device *pdev) di->chg_info.prev_conn_chg = -1; /* Register chargalg power supply class */ - ret = power_supply_register(di->dev, &di->chargalg_psy); - if (ret) { + di->chargalg_psy = power_supply_register(di->dev, &abx500_chargalg_desc, + &psy_cfg); + if (IS_ERR(di->chargalg_psy)) { dev_err(di->dev, "failed to register chargalg psy\n"); + ret = PTR_ERR(di->chargalg_psy); goto free_chargalg_wq; } @@ -2138,7 +2140,7 @@ static int abx500_chargalg_probe(struct platform_device *pdev) return ret; free_psy: - power_supply_unregister(&di->chargalg_psy); + power_supply_unregister(di->chargalg_psy); free_chargalg_wq: destroy_workqueue(di->chargalg_wq); return ret; diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c index 39763015b..9d1a7fb 100644 --- a/drivers/power/apm_power.c +++ b/drivers/power/apm_power.c @@ -15,10 +15,10 @@ #include <linux/apm-emulation.h> -#define PSY_PROP(psy, prop, val) (psy->get_property(psy, \ +#define PSY_PROP(psy, prop, val) (power_supply_get_property(psy, \ POWER_SUPPLY_PROP_##prop, val)) -#define _MPSY_PROP(prop, val) (main_battery->get_property(main_battery, \ +#define _MPSY_PROP(prop, val) (power_supply_get_property(main_battery, \ prop, val)) #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val) @@ -48,7 +48,7 @@ static int __find_main_battery(struct device *dev, void *data) bp->bat = dev_get_drvdata(dev); - if (bp->bat->use_for_apm) { + if (bp->bat->desc->use_for_apm) { /* nice, we explicitly asked to report this battery. */ bp->main = bp->bat; return 1; diff --git a/drivers/power/axp288_fuel_gauge.c b/drivers/power/axp288_fuel_gauge.c new file mode 100644 index 0000000..ca1cc5a --- /dev/null +++ b/drivers/power/axp288_fuel_gauge.c @@ -0,0 +1,1154 @@ +/* + * axp288_fuel_gauge.c - Xpower AXP288 PMIC Fuel Gauge Driver + * + * Copyright (C) 2014 Intel Corporation + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; 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/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/regmap.h> +#include <linux/jiffies.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/workqueue.h> +#include <linux/mfd/axp20x.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/iio/consumer.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> + +#define CHRG_STAT_BAT_SAFE_MODE (1 << 3) +#define CHRG_STAT_BAT_VALID (1 << 4) +#define CHRG_STAT_BAT_PRESENT (1 << 5) +#define CHRG_STAT_CHARGING (1 << 6) +#define CHRG_STAT_PMIC_OTP (1 << 7) + +#define CHRG_CCCV_CC_MASK 0xf /* 4 bits */ +#define CHRG_CCCV_CC_BIT_POS 0 +#define CHRG_CCCV_CC_OFFSET 200 /* 200mA */ +#define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */ +#define CHRG_CCCV_ITERM_20P (1 << 4) /* 20% of CC */ +#define CHRG_CCCV_CV_MASK 0x60 /* 2 bits */ +#define CHRG_CCCV_CV_BIT_POS 5 +#define CHRG_CCCV_CV_4100MV 0x0 /* 4.10V */ +#define CHRG_CCCV_CV_4150MV 0x1 /* 4.15V */ +#define CHRG_CCCV_CV_4200MV 0x2 /* 4.20V */ +#define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */ +#define CHRG_CCCV_CHG_EN (1 << 7) + +#define CV_4100 4100 /* 4100mV */ +#define CV_4150 4150 /* 4150mV */ +#define CV_4200 4200 /* 4200mV */ +#define CV_4350 4350 /* 4350mV */ + +#define TEMP_IRQ_CFG_QWBTU (1 << 0) +#define TEMP_IRQ_CFG_WBTU (1 << 1) +#define TEMP_IRQ_CFG_QWBTO (1 << 2) +#define TEMP_IRQ_CFG_WBTO (1 << 3) +#define TEMP_IRQ_CFG_MASK 0xf + +#define FG_IRQ_CFG_LOWBATT_WL2 (1 << 0) +#define FG_IRQ_CFG_LOWBATT_WL1 (1 << 1) +#define FG_IRQ_CFG_LOWBATT_MASK 0x3 +#define LOWBAT_IRQ_STAT_LOWBATT_WL2 (1 << 0) +#define LOWBAT_IRQ_STAT_LOWBATT_WL1 (1 << 1) + +#define FG_CNTL_OCV_ADJ_STAT (1 << 2) +#define FG_CNTL_OCV_ADJ_EN (1 << 3) +#define FG_CNTL_CAP_ADJ_STAT (1 << 4) +#define FG_CNTL_CAP_ADJ_EN (1 << 5) +#define FG_CNTL_CC_EN (1 << 6) +#define FG_CNTL_GAUGE_EN (1 << 7) + +#define FG_REP_CAP_VALID (1 << 7) +#define FG_REP_CAP_VAL_MASK 0x7F + +#define FG_DES_CAP1_VALID (1 << 7) +#define FG_DES_CAP1_VAL_MASK 0x7F +#define FG_DES_CAP0_VAL_MASK 0xFF +#define FG_DES_CAP_RES_LSB 1456 /* 1.456mAhr */ + +#define FG_CC_MTR1_VALID (1 << 7) +#define FG_CC_MTR1_VAL_MASK 0x7F +#define FG_CC_MTR0_VAL_MASK 0xFF +#define FG_DES_CC_RES_LSB 1456 /* 1.456mAhr */ + +#define FG_OCV_CAP_VALID (1 << 7) +#define FG_OCV_CAP_VAL_MASK 0x7F +#define FG_CC_CAP_VALID (1 << 7) +#define FG_CC_CAP_VAL_MASK 0x7F + +#define FG_LOW_CAP_THR1_MASK 0xf0 /* 5% tp 20% */ +#define FG_LOW_CAP_THR1_VAL 0xa0 /* 15 perc */ +#define FG_LOW_CAP_THR2_MASK 0x0f /* 0% to 15% */ +#define FG_LOW_CAP_WARN_THR 14 /* 14 perc */ +#define FG_LOW_CAP_CRIT_THR 4 /* 4 perc */ +#define FG_LOW_CAP_SHDN_THR 0 /* 0 perc */ + +#define STATUS_MON_DELAY_JIFFIES (HZ * 60) /*60 sec */ +#define NR_RETRY_CNT 3 +#define DEV_NAME "axp288_fuel_gauge" + +/* 1.1mV per LSB expressed in uV */ +#define VOLTAGE_FROM_ADC(a) ((a * 11) / 10) +/* properties converted to tenths of degrees, uV, uA, uW */ +#define PROP_TEMP(a) ((a) * 10) +#define UNPROP_TEMP(a) ((a) / 10) +#define PROP_VOLT(a) ((a) * 1000) +#define PROP_CURR(a) ((a) * 1000) + +#define AXP288_FG_INTR_NUM 6 +enum { + QWBTU_IRQ = 0, + WBTU_IRQ, + QWBTO_IRQ, + WBTO_IRQ, + WL2_IRQ, + WL1_IRQ, +}; + +struct axp288_fg_info { + struct platform_device *pdev; + struct axp20x_fg_pdata *pdata; + struct regmap *regmap; + struct regmap_irq_chip_data *regmap_irqc; + int irq[AXP288_FG_INTR_NUM]; + struct power_supply *bat; + struct mutex lock; + int status; + struct delayed_work status_monitor; + struct dentry *debug_file; +}; + +static enum power_supply_property fuel_gauge_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TEMP_MAX, + POWER_SUPPLY_PROP_TEMP_MIN, + POWER_SUPPLY_PROP_TEMP_ALERT_MIN, + POWER_SUPPLY_PROP_TEMP_ALERT_MAX, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_MODEL_NAME, +}; + +static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg) +{ + int ret, i; + unsigned int val; + + for (i = 0; i < NR_RETRY_CNT; i++) { + ret = regmap_read(info->regmap, reg, &val); + if (ret == -EBUSY) + continue; + else + break; + } + + if (ret < 0) + dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret); + + return val; +} + +static int fuel_gauge_reg_writeb(struct axp288_fg_info *info, int reg, u8 val) +{ + int ret; + + ret = regmap_write(info->regmap, reg, (unsigned int)val); + + if (ret < 0) + dev_err(&info->pdev->dev, "axp288 reg write err:%d\n", ret); + + return ret; +} + +static int pmic_read_adc_val(const char *name, int *raw_val, + struct axp288_fg_info *info) +{ + int ret, val = 0; + struct iio_channel *indio_chan; + + indio_chan = iio_channel_get(NULL, name); + if (IS_ERR_OR_NULL(indio_chan)) { + ret = PTR_ERR(indio_chan); + goto exit; + } + ret = iio_read_channel_raw(indio_chan, &val); + if (ret < 0) { + dev_err(&info->pdev->dev, + "IIO channel read error: %x, %x\n", ret, val); + goto err_exit; + } + + dev_dbg(&info->pdev->dev, "adc raw val=%x\n", val); + *raw_val = val; + +err_exit: + iio_channel_release(indio_chan); +exit: + return ret; +} + +#ifdef CONFIG_DEBUG_FS +static int fuel_gauge_debug_show(struct seq_file *s, void *data) +{ + struct axp288_fg_info *info = s->private; + int raw_val, ret; + + seq_printf(s, " PWR_STATUS[%02x] : %02x\n", + AXP20X_PWR_INPUT_STATUS, + fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS)); + seq_printf(s, "PWR_OP_MODE[%02x] : %02x\n", + AXP20X_PWR_OP_MODE, + fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE)); + seq_printf(s, " CHRG_CTRL1[%02x] : %02x\n", + AXP20X_CHRG_CTRL1, + fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1)); + seq_printf(s, " VLTF[%02x] : %02x\n", + AXP20X_V_LTF_DISCHRG, + fuel_gauge_reg_readb(info, AXP20X_V_LTF_DISCHRG)); + seq_printf(s, " VHTF[%02x] : %02x\n", + AXP20X_V_HTF_DISCHRG, + fuel_gauge_reg_readb(info, AXP20X_V_HTF_DISCHRG)); + seq_printf(s, " CC_CTRL[%02x] : %02x\n", + AXP20X_CC_CTRL, + fuel_gauge_reg_readb(info, AXP20X_CC_CTRL)); + seq_printf(s, "BATTERY CAP[%02x] : %02x\n", + AXP20X_FG_RES, + fuel_gauge_reg_readb(info, AXP20X_FG_RES)); + seq_printf(s, " FG_RDC1[%02x] : %02x\n", + AXP288_FG_RDC1_REG, + fuel_gauge_reg_readb(info, AXP288_FG_RDC1_REG)); + seq_printf(s, " FG_RDC0[%02x] : %02x\n", + AXP288_FG_RDC0_REG, + fuel_gauge_reg_readb(info, AXP288_FG_RDC0_REG)); + seq_printf(s, " FG_OCVH[%02x] : %02x\n", + AXP288_FG_OCVH_REG, + fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG)); + seq_printf(s, " FG_OCVL[%02x] : %02x\n", + AXP288_FG_OCVL_REG, + fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG)); + seq_printf(s, "FG_DES_CAP1[%02x] : %02x\n", + AXP288_FG_DES_CAP1_REG, + fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG)); + seq_printf(s, "FG_DES_CAP0[%02x] : %02x\n", + AXP288_FG_DES_CAP0_REG, + fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP0_REG)); + seq_printf(s, " FG_CC_MTR1[%02x] : %02x\n", + AXP288_FG_CC_MTR1_REG, + fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR1_REG)); + seq_printf(s, " FG_CC_MTR0[%02x] : %02x\n", + AXP288_FG_CC_MTR0_REG, + fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR0_REG)); + seq_printf(s, " FG_OCV_CAP[%02x] : %02x\n", + AXP288_FG_OCV_CAP_REG, + fuel_gauge_reg_readb(info, AXP288_FG_OCV_CAP_REG)); + seq_printf(s, " FG_CC_CAP[%02x] : %02x\n", + AXP288_FG_CC_CAP_REG, + fuel_gauge_reg_readb(info, AXP288_FG_CC_CAP_REG)); + seq_printf(s, " FG_LOW_CAP[%02x] : %02x\n", + AXP288_FG_LOW_CAP_REG, + fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG)); + seq_printf(s, "TUNING_CTL0[%02x] : %02x\n", + AXP288_FG_TUNE0, + fuel_gauge_reg_readb(info, AXP288_FG_TUNE0)); + seq_printf(s, "TUNING_CTL1[%02x] : %02x\n", + AXP288_FG_TUNE1, + fuel_gauge_reg_readb(info, AXP288_FG_TUNE1)); + seq_printf(s, "TUNING_CTL2[%02x] : %02x\n", + AXP288_FG_TUNE2, + fuel_gauge_reg_readb(info, AXP288_FG_TUNE2)); + seq_printf(s, "TUNING_CTL3[%02x] : %02x\n", + AXP288_FG_TUNE3, + fuel_gauge_reg_readb(info, AXP288_FG_TUNE3)); + seq_printf(s, "TUNING_CTL4[%02x] : %02x\n", + AXP288_FG_TUNE4, + fuel_gauge_reg_readb(info, AXP288_FG_TUNE4)); + seq_printf(s, "TUNING_CTL5[%02x] : %02x\n", + AXP288_FG_TUNE5, + fuel_gauge_reg_readb(info, AXP288_FG_TUNE5)); + + ret = pmic_read_adc_val("axp288-batt-temp", &raw_val, info); + if (ret >= 0) + seq_printf(s, "axp288-batttemp : %d\n", raw_val); + ret = pmic_read_adc_val("axp288-pmic-temp", &raw_val, info); + if (ret >= 0) + seq_printf(s, "axp288-pmictemp : %d\n", raw_val); + ret = pmic_read_adc_val("axp288-system-temp", &raw_val, info); + if (ret >= 0) + seq_printf(s, "axp288-systtemp : %d\n", raw_val); + ret = pmic_read_adc_val("axp288-chrg-curr", &raw_val, info); + if (ret >= 0) + seq_printf(s, "axp288-chrgcurr : %d\n", raw_val); + ret = pmic_read_adc_val("axp288-chrg-d-curr", &raw_val, info); + if (ret >= 0) + seq_printf(s, "axp288-dchrgcur : %d\n", raw_val); + ret = pmic_read_adc_val("axp288-batt-volt", &raw_val, info); + if (ret >= 0) + seq_printf(s, "axp288-battvolt : %d\n", raw_val); + + return 0; +} + +static int debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, fuel_gauge_debug_show, inode->i_private); +} + +static const struct file_operations fg_debug_fops = { + .open = debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void fuel_gauge_create_debugfs(struct axp288_fg_info *info) +{ + info->debug_file = debugfs_create_file("fuelgauge", 0666, NULL, + info, &fg_debug_fops); +} + +static void fuel_gauge_remove_debugfs(struct axp288_fg_info *info) +{ + debugfs_remove(info->debug_file); +} +#else +static inline void fuel_gauge_create_debugfs(struct axp288_fg_info *info) +{ +} +static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info) +{ +} +#endif + +static void fuel_gauge_get_status(struct axp288_fg_info *info) +{ + int pwr_stat, ret; + int charge, discharge; + + pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS); + if (pwr_stat < 0) { + dev_err(&info->pdev->dev, + "PWR STAT read failed:%d\n", pwr_stat); + return; + } + ret = pmic_read_adc_val("axp288-chrg-curr", &charge, info); + if (ret < 0) { + dev_err(&info->pdev->dev, + "ADC charge current read failed:%d\n", ret); + return; + } + ret = pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info); + if (ret < 0) { + dev_err(&info->pdev->dev, + "ADC discharge current read failed:%d\n", ret); + return; + } + + if (charge > 0) + info->status = POWER_SUPPLY_STATUS_CHARGING; + else if (discharge > 0) + info->status = POWER_SUPPLY_STATUS_DISCHARGING; + else { + if (pwr_stat & CHRG_STAT_BAT_PRESENT) + info->status = POWER_SUPPLY_STATUS_FULL; + else + info->status = POWER_SUPPLY_STATUS_NOT_CHARGING; + } +} + +static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt) +{ + int ret = 0, raw_val; + + ret = pmic_read_adc_val("axp288-batt-volt", &raw_val, info); + if (ret < 0) + goto vbatt_read_fail; + + *vbatt = VOLTAGE_FROM_ADC(raw_val); +vbatt_read_fail: + return ret; +} + +static int fuel_gauge_get_current(struct axp288_fg_info *info, int *cur) +{ + int ret, value = 0; + int charge, discharge; + + ret = pmic_read_adc_val("axp288-chrg-curr", &charge, info); + if (ret < 0) + goto current_read_fail; + ret = pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info); + if (ret < 0) + goto current_read_fail; + + if (charge > 0) + value = charge; + else if (discharge > 0) + value = -1 * discharge; + + *cur = value; +current_read_fail: + return ret; +} + +static int temp_to_adc(struct axp288_fg_info *info, int tval) +{ + int rntc = 0, i, ret, adc_val; + int rmin, rmax, tmin, tmax; + int tcsz = info->pdata->tcsz; + + /* get the Rntc resitance value for this temp */ + if (tval > info->pdata->thermistor_curve[0][1]) { + rntc = info->pdata->thermistor_curve[0][0]; + } else if (tval <= info->pdata->thermistor_curve[tcsz-1][1]) { + rntc = info->pdata->thermistor_curve[tcsz-1][0]; + } else { + for (i = 1; i < tcsz; i++) { + if (tval > info->pdata->thermistor_curve[i][1]) { + rmin = info->pdata->thermistor_curve[i-1][0]; + rmax = info->pdata->thermistor_curve[i][0]; + tmin = info->pdata->thermistor_curve[i-1][1]; + tmax = info->pdata->thermistor_curve[i][1]; + rntc = rmin + ((rmax - rmin) * + (tval - tmin) / (tmax - tmin)); + break; + } + } + } + + /* we need the current to calculate the proper adc voltage */ + ret = fuel_gauge_reg_readb(info, AXP20X_ADC_RATE); + if (ret < 0) { + dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret); + ret = 0x30; + } + + /* + * temperature is proportional to NTS thermistor resistance + * ADC_RATE[5-4] determines current, 00=20uA,01=40uA,10=60uA,11=80uA + * [12-bit ADC VAL] = R_NTC(Ω) * current / 800 + */ + adc_val = rntc * (20 + (20 * ((ret >> 4) & 0x3))) / 800; + + return adc_val; +} + +static int adc_to_temp(struct axp288_fg_info *info, int adc_val) +{ + int ret, r, i, tval = 0; + int rmin, rmax, tmin, tmax; + int tcsz = info->pdata->tcsz; + + ret = fuel_gauge_reg_readb(info, AXP20X_ADC_RATE); + if (ret < 0) { + dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret); + ret = 0x30; + } + + /* + * temperature is proportional to NTS thermistor resistance + * ADC_RATE[5-4] determines current, 00=20uA,01=40uA,10=60uA,11=80uA + * R_NTC(Ω) = [12-bit ADC VAL] * 800 / current + */ + r = adc_val * 800 / (20 + (20 * ((ret >> 4) & 0x3))); + + if (r < info->pdata->thermistor_curve[0][0]) { + tval = info->pdata->thermistor_curve[0][1]; + } else if (r >= info->pdata->thermistor_curve[tcsz-1][0]) { + tval = info->pdata->thermistor_curve[tcsz-1][1]; + } else { + for (i = 1; i < tcsz; i++) { + if (r < info->pdata->thermistor_curve[i][0]) { + rmin = info->pdata->thermistor_curve[i-1][0]; + rmax = info->pdata->thermistor_curve[i][0]; + tmin = info->pdata->thermistor_curve[i-1][1]; + tmax = info->pdata->thermistor_curve[i][1]; + tval = tmin + ((tmax - tmin) * + (r - rmin) / (rmax - rmin)); + break; + } + } + } + + return tval; +} + +static int fuel_gauge_get_btemp(struct axp288_fg_info *info, int *btemp) +{ + int ret, raw_val = 0; + + ret = pmic_read_adc_val("axp288-batt-temp", &raw_val, info); + if (ret < 0) + goto temp_read_fail; + + *btemp = adc_to_temp(info, raw_val); + +temp_read_fail: + return ret; +} + +static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv) +{ + int ret, value; + + /* 12-bit data value, upper 8 in OCVH, lower 4 in OCVL */ + ret = fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG); + if (ret < 0) + goto vocv_read_fail; + value = ret << 4; + + ret = fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG); + if (ret < 0) + goto vocv_read_fail; + value |= (ret & 0xf); + + *vocv = VOLTAGE_FROM_ADC(value); +vocv_read_fail: + return ret; +} + +static int fuel_gauge_battery_health(struct axp288_fg_info *info) +{ + int temp, vocv; + int ret, health = POWER_SUPPLY_HEALTH_UNKNOWN; + + ret = fuel_gauge_get_btemp(info, &temp); + if (ret < 0) + goto health_read_fail; + + ret = fuel_gauge_get_vocv(info, &vocv); + if (ret < 0) + goto health_read_fail; + + if (vocv > info->pdata->max_volt) + health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else if (temp > info->pdata->max_temp) + health = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (temp < info->pdata->min_temp) + health = POWER_SUPPLY_HEALTH_COLD; + else if (vocv < info->pdata->min_volt) + health = POWER_SUPPLY_HEALTH_DEAD; + else + health = POWER_SUPPLY_HEALTH_GOOD; + +health_read_fail: + return health; +} + +static int fuel_gauge_set_high_btemp_alert(struct axp288_fg_info *info) +{ + int ret, adc_val; + + /* program temperature threshold as 1/16 ADC value */ + adc_val = temp_to_adc(info, info->pdata->max_temp); + ret = fuel_gauge_reg_writeb(info, AXP20X_V_HTF_DISCHRG, adc_val >> 4); + + return ret; +} + +static int fuel_gauge_set_low_btemp_alert(struct axp288_fg_info *info) +{ + int ret, adc_val; + + /* program temperature threshold as 1/16 ADC value */ + adc_val = temp_to_adc(info, info->pdata->min_temp); + ret = fuel_gauge_reg_writeb(info, AXP20X_V_LTF_DISCHRG, adc_val >> 4); + + return ret; +} + +static int fuel_gauge_get_property(struct power_supply *ps, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct axp288_fg_info *info = power_supply_get_drvdata(ps); + int ret = 0, value; + + mutex_lock(&info->lock); + switch (prop) { + case POWER_SUPPLY_PROP_STATUS: + fuel_gauge_get_status(info); + val->intval = info->status; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = fuel_gauge_battery_health(info); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = fuel_gauge_get_vbatt(info, &value); + if (ret < 0) + goto fuel_gauge_read_err; + val->intval = PROP_VOLT(value); + break; + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + ret = fuel_gauge_get_vocv(info, &value); + if (ret < 0) + goto fuel_gauge_read_err; + val->intval = PROP_VOLT(value); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = fuel_gauge_get_current(info, &value); + if (ret < 0) + goto fuel_gauge_read_err; + val->intval = PROP_CURR(value); + break; + case POWER_SUPPLY_PROP_PRESENT: + ret = fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE); + if (ret < 0) + goto fuel_gauge_read_err; + + if (ret & CHRG_STAT_BAT_PRESENT) + val->intval = 1; + else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES); + if (ret < 0) + goto fuel_gauge_read_err; + + if (!(ret & FG_REP_CAP_VALID)) + dev_err(&info->pdev->dev, + "capacity measurement not valid\n"); + val->intval = (ret & FG_REP_CAP_VAL_MASK); + break; + case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: + ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG); + if (ret < 0) + goto fuel_gauge_read_err; + val->intval = (ret & 0x0f); + break; + case POWER_SUPPLY_PROP_TEMP: + ret = fuel_gauge_get_btemp(info, &value); + if (ret < 0) + goto fuel_gauge_read_err; + val->intval = PROP_TEMP(value); + break; + case POWER_SUPPLY_PROP_TEMP_MAX: + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + val->intval = PROP_TEMP(info->pdata->max_temp); + break; + case POWER_SUPPLY_PROP_TEMP_MIN: + case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: + val->intval = PROP_TEMP(info->pdata->min_temp); + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + ret = fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR1_REG); + if (ret < 0) + goto fuel_gauge_read_err; + + value = (ret & FG_CC_MTR1_VAL_MASK) << 8; + ret = fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR0_REG); + if (ret < 0) + goto fuel_gauge_read_err; + value |= (ret & FG_CC_MTR0_VAL_MASK); + val->intval = value * FG_DES_CAP_RES_LSB; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG); + if (ret < 0) + goto fuel_gauge_read_err; + + value = (ret & FG_DES_CAP1_VAL_MASK) << 8; + ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP0_REG); + if (ret < 0) + goto fuel_gauge_read_err; + value |= (ret & FG_DES_CAP0_VAL_MASK); + val->intval = value * FG_DES_CAP_RES_LSB; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = PROP_CURR(info->pdata->design_cap); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = PROP_VOLT(info->pdata->max_volt); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = PROP_VOLT(info->pdata->min_volt); + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = info->pdata->battid; + break; + default: + mutex_unlock(&info->lock); + return -EINVAL; + } + + mutex_unlock(&info->lock); + return 0; + +fuel_gauge_read_err: + mutex_unlock(&info->lock); + return ret; +} + +static int fuel_gauge_set_property(struct power_supply *ps, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct axp288_fg_info *info = power_supply_get_drvdata(ps); + int ret = 0; + + mutex_lock(&info->lock); + switch (prop) { + case POWER_SUPPLY_PROP_STATUS: + info->status = val->intval; + break; + case POWER_SUPPLY_PROP_TEMP_MIN: + case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: + if ((val->intval < PD_DEF_MIN_TEMP) || + (val->intval > PD_DEF_MAX_TEMP)) { + ret = -EINVAL; + break; + } + info->pdata->min_temp = UNPROP_TEMP(val->intval); + ret = fuel_gauge_set_low_btemp_alert(info); + if (ret < 0) + dev_err(&info->pdev->dev, + "temp alert min set fail:%d\n", ret); + break; + case POWER_SUPPLY_PROP_TEMP_MAX: + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + if ((val->intval < PD_DEF_MIN_TEMP) || + (val->intval > PD_DEF_MAX_TEMP)) { + ret = -EINVAL; + break; + } + info->pdata->max_temp = UNPROP_TEMP(val->intval); + ret = fuel_gauge_set_high_btemp_alert(info); + if (ret < 0) + dev_err(&info->pdev->dev, + "temp alert max set fail:%d\n", ret); + break; + case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: + if ((val->intval < 0) || (val->intval > 15)) { + ret = -EINVAL; + break; + } + ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG); + if (ret < 0) + break; + ret &= 0xf0; + ret |= (val->intval & 0xf); + ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, ret); + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&info->lock); + return ret; +} + +static int fuel_gauge_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_TEMP_MIN: + case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: + case POWER_SUPPLY_PROP_TEMP_MAX: + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: + ret = 1; + break; + default: + ret = 0; + } + + return ret; +} + +static void fuel_gauge_status_monitor(struct work_struct *work) +{ + struct axp288_fg_info *info = container_of(work, + struct axp288_fg_info, status_monitor.work); + + fuel_gauge_get_status(info); + power_supply_changed(info->bat); + schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES); +} + +static irqreturn_t fuel_gauge_thread_handler(int irq, void *dev) +{ + struct axp288_fg_info *info = dev; + int i; + + for (i = 0; i < AXP288_FG_INTR_NUM; i++) { + if (info->irq[i] == irq) + break; + } + + if (i >= AXP288_FG_INTR_NUM) { + dev_warn(&info->pdev->dev, "spurious interrupt!!\n"); + return IRQ_NONE; + } + + switch (i) { + case QWBTU_IRQ: + dev_info(&info->pdev->dev, + "Quit Battery under temperature in work mode IRQ (QWBTU)\n"); + break; + case WBTU_IRQ: + dev_info(&info->pdev->dev, + "Battery under temperature in work mode IRQ (WBTU)\n"); + break; + case QWBTO_IRQ: + dev_info(&info->pdev->dev, + "Quit Battery over temperature in work mode IRQ (QWBTO)\n"); + break; + case WBTO_IRQ: + dev_info(&info->pdev->dev, + "Battery over temperature in work mode IRQ (WBTO)\n"); + break; + case WL2_IRQ: + dev_info(&info->pdev->dev, "Low Batt Warning(2) INTR\n"); + break; + case WL1_IRQ: + dev_info(&info->pdev->dev, "Low Batt Warning(1) INTR\n"); + break; + default: + dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n"); + } + + power_supply_changed(info->bat); + return IRQ_HANDLED; +} + +static void fuel_gauge_external_power_changed(struct power_supply *psy) +{ + struct axp288_fg_info *info = power_supply_get_drvdata(psy); + + power_supply_changed(info->bat); +} + +static const struct power_supply_desc fuel_gauge_desc = { + .name = DEV_NAME, + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = fuel_gauge_props, + .num_properties = ARRAY_SIZE(fuel_gauge_props), + .get_property = fuel_gauge_get_property, + .set_property = fuel_gauge_set_property, + .property_is_writeable = fuel_gauge_property_is_writeable, + .external_power_changed = fuel_gauge_external_power_changed, +}; + +static int fuel_gauge_set_lowbatt_thresholds(struct axp288_fg_info *info) +{ + int ret; + u8 reg_val; + + ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES); + if (ret < 0) { + dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret); + return ret; + } + ret = (ret & FG_REP_CAP_VAL_MASK); + + if (ret > FG_LOW_CAP_WARN_THR) + reg_val = FG_LOW_CAP_WARN_THR; + else if (ret > FG_LOW_CAP_CRIT_THR) + reg_val = FG_LOW_CAP_CRIT_THR; + else + reg_val = FG_LOW_CAP_SHDN_THR; + + reg_val |= FG_LOW_CAP_THR1_VAL; + ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, reg_val); + if (ret < 0) + dev_err(&info->pdev->dev, "%s:write err:%d\n", __func__, ret); + + return ret; +} + +static int fuel_gauge_program_vbatt_full(struct axp288_fg_info *info) +{ + int ret; + u8 val; + + ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1); + if (ret < 0) + goto fg_prog_ocv_fail; + else + val = (ret & ~CHRG_CCCV_CV_MASK); + + switch (info->pdata->max_volt) { + case CV_4100: + val |= (CHRG_CCCV_CV_4100MV << CHRG_CCCV_CV_BIT_POS); + break; + case CV_4150: + val |= (CHRG_CCCV_CV_4150MV << CHRG_CCCV_CV_BIT_POS); + break; + case CV_4200: + val |= (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS); + break; + case CV_4350: + val |= (CHRG_CCCV_CV_4350MV << CHRG_CCCV_CV_BIT_POS); + break; + default: + val |= (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS); + break; + } + + ret = fuel_gauge_reg_writeb(info, AXP20X_CHRG_CTRL1, val); +fg_prog_ocv_fail: + return ret; +} + +static int fuel_gauge_program_design_cap(struct axp288_fg_info *info) +{ + int ret; + + ret = fuel_gauge_reg_writeb(info, + AXP288_FG_DES_CAP1_REG, info->pdata->cap1); + if (ret < 0) + goto fg_prog_descap_fail; + + ret = fuel_gauge_reg_writeb(info, + AXP288_FG_DES_CAP0_REG, info->pdata->cap0); + +fg_prog_descap_fail: + return ret; +} + +static int fuel_gauge_program_ocv_curve(struct axp288_fg_info *info) +{ + int ret = 0, i; + + for (i = 0; i < OCV_CURVE_SIZE; i++) { + ret = fuel_gauge_reg_writeb(info, + AXP288_FG_OCV_CURVE_REG + i, info->pdata->ocv_curve[i]); + if (ret < 0) + goto fg_prog_ocv_fail; + } + +fg_prog_ocv_fail: + return ret; +} + +static int fuel_gauge_program_rdc_vals(struct axp288_fg_info *info) +{ + int ret; + + ret = fuel_gauge_reg_writeb(info, + AXP288_FG_RDC1_REG, info->pdata->rdc1); + if (ret < 0) + goto fg_prog_ocv_fail; + + ret = fuel_gauge_reg_writeb(info, + AXP288_FG_RDC0_REG, info->pdata->rdc0); + +fg_prog_ocv_fail: + return ret; +} + +static void fuel_gauge_init_config_regs(struct axp288_fg_info *info) +{ + int ret; + + /* + * check if the config data is already + * programmed and if so just return. + */ + + ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG); + if (ret < 0) { + dev_warn(&info->pdev->dev, "CAP1 reg read err!!\n"); + } else if (!(ret & FG_DES_CAP1_VALID)) { + dev_info(&info->pdev->dev, "FG data needs to be initialized\n"); + } else { + dev_info(&info->pdev->dev, "FG data is already initialized\n"); + return; + } + + ret = fuel_gauge_program_vbatt_full(info); + if (ret < 0) + dev_err(&info->pdev->dev, "set vbatt full fail:%d\n", ret); + + ret = fuel_gauge_program_design_cap(info); + if (ret < 0) + dev_err(&info->pdev->dev, "set design cap fail:%d\n", ret); + + ret = fuel_gauge_program_rdc_vals(info); + if (ret < 0) + dev_err(&info->pdev->dev, "set rdc fail:%d\n", ret); + + ret = fuel_gauge_program_ocv_curve(info); + if (ret < 0) + dev_err(&info->pdev->dev, "set ocv curve fail:%d\n", ret); + + ret = fuel_gauge_set_lowbatt_thresholds(info); + if (ret < 0) + dev_err(&info->pdev->dev, "lowbatt thr set fail:%d\n", ret); + + ret = fuel_gauge_reg_writeb(info, AXP20X_CC_CTRL, 0xef); + if (ret < 0) + dev_err(&info->pdev->dev, "gauge cntl set fail:%d\n", ret); +} + +static void fuel_gauge_init_irq(struct axp288_fg_info *info) +{ + int ret, i, pirq; + + for (i = 0; i < AXP288_FG_INTR_NUM; i++) { + pirq = platform_get_irq(info->pdev, i); + info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq); + if (info->irq[i] < 0) { + dev_warn(&info->pdev->dev, + "regmap_irq get virq failed for IRQ %d: %d\n", + pirq, info->irq[i]); + info->irq[i] = -1; + goto intr_failed; + } + ret = request_threaded_irq(info->irq[i], + NULL, fuel_gauge_thread_handler, + IRQF_ONESHOT, DEV_NAME, info); + if (ret) { + dev_warn(&info->pdev->dev, + "request irq failed for IRQ %d: %d\n", + pirq, info->irq[i]); + info->irq[i] = -1; + goto intr_failed; + } else { + dev_info(&info->pdev->dev, "HW IRQ %d -> VIRQ %d\n", + pirq, info->irq[i]); + } + } + return; + +intr_failed: + for (; i > 0; i--) { + free_irq(info->irq[i - 1], info); + info->irq[i - 1] = -1; + } +} + +static void fuel_gauge_init_hw_regs(struct axp288_fg_info *info) +{ + int ret; + unsigned int val; + + ret = fuel_gauge_set_high_btemp_alert(info); + if (ret < 0) + dev_err(&info->pdev->dev, "high batt temp set fail:%d\n", ret); + + ret = fuel_gauge_set_low_btemp_alert(info); + if (ret < 0) + dev_err(&info->pdev->dev, "low batt temp set fail:%d\n", ret); + + /* enable interrupts */ + val = fuel_gauge_reg_readb(info, AXP20X_IRQ3_EN); + val |= TEMP_IRQ_CFG_MASK; + fuel_gauge_reg_writeb(info, AXP20X_IRQ3_EN, val); + + val = fuel_gauge_reg_readb(info, AXP20X_IRQ4_EN); + val |= FG_IRQ_CFG_LOWBATT_MASK; + val = fuel_gauge_reg_writeb(info, AXP20X_IRQ4_EN, val); +} + +static int axp288_fuel_gauge_probe(struct platform_device *pdev) +{ + int ret = 0; + struct axp288_fg_info *info; + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + struct power_supply_config psy_cfg = {}; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pdev = pdev; + info->regmap = axp20x->regmap; + info->regmap_irqc = axp20x->regmap_irqc; + info->status = POWER_SUPPLY_STATUS_UNKNOWN; + info->pdata = pdev->dev.platform_data; + if (!info->pdata) + return -ENODEV; + + platform_set_drvdata(pdev, info); + + mutex_init(&info->lock); + INIT_DELAYED_WORK(&info->status_monitor, fuel_gauge_status_monitor); + + psy_cfg.drv_data = info; + info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg); + if (IS_ERR(info->bat)) { + ret = PTR_ERR(info->bat); + dev_err(&pdev->dev, "failed to register battery: %d\n", ret); + return ret; + } + + fuel_gauge_create_debugfs(info); + fuel_gauge_init_config_regs(info); + fuel_gauge_init_irq(info); + fuel_gauge_init_hw_regs(info); + schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES); + + return ret; +} + +static struct platform_device_id axp288_fg_id_table[] = { + { .name = DEV_NAME }, + {}, +}; + +static int axp288_fuel_gauge_remove(struct platform_device *pdev) +{ + struct axp288_fg_info *info = platform_get_drvdata(pdev); + int i; + + cancel_delayed_work_sync(&info->status_monitor); + power_supply_unregister(info->bat); + fuel_gauge_remove_debugfs(info); + + for (i = 0; i < AXP288_FG_INTR_NUM; i++) + if (info->irq[i] >= 0) + free_irq(info->irq[i], info); + + return 0; +} + +static struct platform_driver axp288_fuel_gauge_driver = { + .probe = axp288_fuel_gauge_probe, + .remove = axp288_fuel_gauge_remove, + .id_table = axp288_fg_id_table, + .driver = { + .name = DEV_NAME, + }, +}; + +module_platform_driver(axp288_fuel_gauge_driver); + +MODULE_AUTHOR("Todd Brandt <todd.e.brandt@linux.intel.com>"); +MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c index 1f49986..6c534dc 100644 --- a/drivers/power/bq2415x_charger.c +++ b/drivers/power/bq2415x_charger.c @@ -13,12 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* * Datasheets: * http://www.ti.com/product/bq24150 * http://www.ti.com/product/bq24150a @@ -26,6 +20,8 @@ * http://www.ti.com/product/bq24153 * http://www.ti.com/product/bq24153a * http://www.ti.com/product/bq24155 + * http://www.ti.com/product/bq24157s + * http://www.ti.com/product/bq24158 */ #include <linux/kernel.h> @@ -147,6 +143,7 @@ enum bq2415x_chip { BQ24155, BQ24156, BQ24156A, + BQ24157S, BQ24158, }; @@ -162,18 +159,20 @@ static char *bq2415x_chip_name[] = { "bq24155", "bq24156", "bq24156a", + "bq24157s", "bq24158", }; struct bq2415x_device { struct device *dev; struct bq2415x_platform_data init_data; - struct power_supply charger; + struct power_supply *charger; + struct power_supply_desc charger_desc; struct delayed_work work; struct power_supply *notify_psy; struct notifier_block nb; enum bq2415x_mode reported_mode;/* mode reported by hook function */ - enum bq2415x_mode mode; /* current configured mode */ + enum bq2415x_mode mode; /* currently configured mode */ enum bq2415x_chip chip; const char *timer_error; char *model; @@ -352,8 +351,7 @@ static int bq2415x_exec_command(struct bq2415x_device *bq, BQ2415X_BIT_CE); if (ret < 0) return ret; - else - return ret > 0 ? 0 : 1; + return ret > 0 ? 0 : 1; case BQ2415X_CHARGER_ENABLE: return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL, 0, BQ2415X_BIT_CE); @@ -426,20 +424,17 @@ static enum bq2415x_chip bq2415x_detect_chip(struct bq2415x_device *bq) case 0: if (bq->chip == BQ24151A) return bq->chip; - else - return BQ24151; + return BQ24151; case 1: if (bq->chip == BQ24150A || bq->chip == BQ24152 || bq->chip == BQ24155) return bq->chip; - else - return BQ24150; + return BQ24150; case 2: if (bq->chip == BQ24153A) return bq->chip; - else - return BQ24153; + return BQ24153; default: return BQUNKNOWN; } @@ -450,9 +445,10 @@ static enum bq2415x_chip bq2415x_detect_chip(struct bq2415x_device *bq) case 0: if (bq->chip == BQ24156A) return bq->chip; - else - return BQ24156; + return BQ24156; case 2: + if (bq->chip == BQ24157S) + return bq->chip; return BQ24158; default: return BQUNKNOWN; @@ -480,24 +476,22 @@ static int bq2415x_detect_revision(struct bq2415x_device *bq) case BQ24152: if (ret >= 0 && ret <= 3) return ret; - else - return -1; + return -1; case BQ24153: case BQ24153A: case BQ24156: case BQ24156A: + case BQ24157S: case BQ24158: if (ret == 3) return 0; else if (ret == 1) return 1; - else - return -1; + return -1; case BQ24155: if (ret == 3) return 3; - else - return -1; + return -1; case BQUNKNOWN: return -1; } @@ -791,7 +785,7 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode) bq2415x_set_default_value(bq, battery_regulation_voltage); bq->mode = mode; - sysfs_notify(&bq->charger.dev->kobj, NULL, "mode"); + sysfs_notify(&bq->charger->dev.kobj, NULL, "mode"); return 0; @@ -816,7 +810,8 @@ static int bq2415x_notifier_call(struct notifier_block *nb, dev_dbg(bq->dev, "notifier call was called\n"); - ret = psy->get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX, &prop); + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX, + &prop); if (ret != 0) return NOTIFY_OK; @@ -874,7 +869,7 @@ static void bq2415x_set_autotimer(struct bq2415x_device *bq, int state) static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg) { bq->timer_error = msg; - sysfs_notify(&bq->charger.dev->kobj, NULL, "timer"); + sysfs_notify(&bq->charger->dev.kobj, NULL, "timer"); dev_err(bq->dev, "%s\n", msg); if (bq->automode > 0) bq->automode = 0; @@ -892,7 +887,7 @@ static void bq2415x_timer_work(struct work_struct *work) int boost; if (bq->automode > 0 && (bq->reported_mode != bq->mode)) { - sysfs_notify(&bq->charger.dev->kobj, NULL, "reported_mode"); + sysfs_notify(&bq->charger->dev.kobj, NULL, "reported_mode"); bq2415x_set_mode(bq, bq->reported_mode); } @@ -998,8 +993,7 @@ static int bq2415x_power_supply_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + struct bq2415x_device *bq = power_supply_get_drvdata(psy); int ret; switch (psp) { @@ -1030,12 +1024,14 @@ static int bq2415x_power_supply_init(struct bq2415x_device *bq) int ret; int chip; char revstr[8]; + struct power_supply_config psy_cfg = { .drv_data = bq, }; - bq->charger.name = bq->name; - bq->charger.type = POWER_SUPPLY_TYPE_USB; - bq->charger.properties = bq2415x_power_supply_props; - bq->charger.num_properties = ARRAY_SIZE(bq2415x_power_supply_props); - bq->charger.get_property = bq2415x_power_supply_get_property; + bq->charger_desc.name = bq->name; + bq->charger_desc.type = POWER_SUPPLY_TYPE_USB; + bq->charger_desc.properties = bq2415x_power_supply_props; + bq->charger_desc.num_properties = + ARRAY_SIZE(bq2415x_power_supply_props); + bq->charger_desc.get_property = bq2415x_power_supply_get_property; ret = bq2415x_detect_chip(bq); if (ret < 0) @@ -1058,10 +1054,11 @@ static int bq2415x_power_supply_init(struct bq2415x_device *bq) return -ENOMEM; } - ret = power_supply_register(bq->dev, &bq->charger); - if (ret) { + bq->charger = power_supply_register(bq->dev, &bq->charger_desc, + &psy_cfg); + if (IS_ERR(bq->charger)) { kfree(bq->model); - return ret; + return PTR_ERR(bq->charger); } return 0; @@ -1073,7 +1070,7 @@ static void bq2415x_power_supply_exit(struct bq2415x_device *bq) if (bq->automode > 0) bq->automode = 0; cancel_delayed_work_sync(&bq->work); - power_supply_unregister(&bq->charger); + power_supply_unregister(bq->charger); kfree(bq->model); } @@ -1085,8 +1082,7 @@ static ssize_t bq2415x_sysfs_show_status(struct device *dev, char *buf) { struct power_supply *psy = dev_get_drvdata(dev); - struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + struct bq2415x_device *bq = power_supply_get_drvdata(psy); enum bq2415x_command command; int ret; @@ -1119,8 +1115,7 @@ static ssize_t bq2415x_sysfs_set_timer(struct device *dev, size_t count) { struct power_supply *psy = dev_get_drvdata(dev); - struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + struct bq2415x_device *bq = power_supply_get_drvdata(psy); int ret = 0; if (strncmp(buf, "auto", 4) == 0) @@ -1141,8 +1136,7 @@ static ssize_t bq2415x_sysfs_show_timer(struct device *dev, char *buf) { struct power_supply *psy = dev_get_drvdata(dev); - struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + struct bq2415x_device *bq = power_supply_get_drvdata(psy); if (bq->timer_error) return sprintf(buf, "%s\n", bq->timer_error); @@ -1166,8 +1160,7 @@ static ssize_t bq2415x_sysfs_set_mode(struct device *dev, size_t count) { struct power_supply *psy = dev_get_drvdata(dev); - struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + struct bq2415x_device *bq = power_supply_get_drvdata(psy); enum bq2415x_mode mode; int ret = 0; @@ -1219,8 +1212,7 @@ static ssize_t bq2415x_sysfs_show_mode(struct device *dev, char *buf) { struct power_supply *psy = dev_get_drvdata(dev); - struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + struct bq2415x_device *bq = power_supply_get_drvdata(psy); ssize_t ret = 0; if (bq->automode > 0) @@ -1257,8 +1249,7 @@ static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev, char *buf) { struct power_supply *psy = dev_get_drvdata(dev); - struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + struct bq2415x_device *bq = power_supply_get_drvdata(psy); if (bq->automode < 0) return -EINVAL; @@ -1286,8 +1277,7 @@ static ssize_t bq2415x_sysfs_set_registers(struct device *dev, size_t count) { struct power_supply *psy = dev_get_drvdata(dev); - struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + struct bq2415x_device *bq = power_supply_get_drvdata(psy); ssize_t ret = 0; unsigned int reg; unsigned int val; @@ -1322,8 +1312,7 @@ static ssize_t bq2415x_sysfs_show_registers(struct device *dev, char *buf) { struct power_supply *psy = dev_get_drvdata(dev); - struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + struct bq2415x_device *bq = power_supply_get_drvdata(psy); ssize_t ret = 0; ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_STATUS, buf+ret); @@ -1341,8 +1330,7 @@ static ssize_t bq2415x_sysfs_set_limit(struct device *dev, size_t count) { struct power_supply *psy = dev_get_drvdata(dev); - struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + struct bq2415x_device *bq = power_supply_get_drvdata(psy); long val; int ret; @@ -1373,8 +1361,7 @@ static ssize_t bq2415x_sysfs_show_limit(struct device *dev, char *buf) { struct power_supply *psy = dev_get_drvdata(dev); - struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + struct bq2415x_device *bq = power_supply_get_drvdata(psy); int ret; if (strcmp(attr->attr.name, "current_limit") == 0) @@ -1402,8 +1389,7 @@ static ssize_t bq2415x_sysfs_set_enable(struct device *dev, size_t count) { struct power_supply *psy = dev_get_drvdata(dev); - struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + struct bq2415x_device *bq = power_supply_get_drvdata(psy); enum bq2415x_command command; long val; int ret; @@ -1438,8 +1424,7 @@ static ssize_t bq2415x_sysfs_show_enable(struct device *dev, char *buf) { struct power_supply *psy = dev_get_drvdata(dev); - struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, - charger); + struct bq2415x_device *bq = power_supply_get_drvdata(psy); enum bq2415x_command command; int ret; @@ -1530,13 +1515,13 @@ static const struct attribute_group bq2415x_sysfs_attr_group = { static int bq2415x_sysfs_init(struct bq2415x_device *bq) { - return sysfs_create_group(&bq->charger.dev->kobj, + return sysfs_create_group(&bq->charger->dev.kobj, &bq2415x_sysfs_attr_group); } static void bq2415x_sysfs_exit(struct bq2415x_device *bq) { - sysfs_remove_group(&bq->charger.dev->kobj, &bq2415x_sysfs_attr_group); + sysfs_remove_group(&bq->charger->dev.kobj, &bq2415x_sysfs_attr_group); } /* main bq2415x probe function */ @@ -1609,27 +1594,27 @@ static int bq2415x_probe(struct i2c_client *client, ret = of_property_read_u32(np, "ti,current-limit", &bq->init_data.current_limit); if (ret) - goto error_2; + goto error_3; ret = of_property_read_u32(np, "ti,weak-battery-voltage", &bq->init_data.weak_battery_voltage); if (ret) - goto error_2; + goto error_3; ret = of_property_read_u32(np, "ti,battery-regulation-voltage", &bq->init_data.battery_regulation_voltage); if (ret) - goto error_2; + goto error_3; ret = of_property_read_u32(np, "ti,charge-current", &bq->init_data.charge_current); if (ret) - goto error_2; + goto error_3; ret = of_property_read_u32(np, "ti,termination-current", &bq->init_data.termination_current); if (ret) - goto error_2; + goto error_3; ret = of_property_read_u32(np, "ti,resistor-sense", &bq->init_data.resistor_sense); if (ret) - goto error_2; + goto error_3; } else { memcpy(&bq->init_data, pdata, sizeof(bq->init_data)); } @@ -1639,19 +1624,19 @@ static int bq2415x_probe(struct i2c_client *client, ret = bq2415x_power_supply_init(bq); if (ret) { dev_err(bq->dev, "failed to register power supply: %d\n", ret); - goto error_2; + goto error_3; } ret = bq2415x_sysfs_init(bq); if (ret) { dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret); - goto error_3; + goto error_4; } ret = bq2415x_set_defaults(bq); if (ret) { dev_err(bq->dev, "failed to set default values: %d\n", ret); - goto error_4; + goto error_5; } if (bq->notify_psy) { @@ -1659,7 +1644,7 @@ static int bq2415x_probe(struct i2c_client *client, ret = power_supply_reg_notifier(&bq->nb); if (ret) { dev_err(bq->dev, "failed to reg notifier: %d\n", ret); - goto error_5; + goto error_6; } /* Query for initial reported_mode and set it */ @@ -1679,11 +1664,14 @@ static int bq2415x_probe(struct i2c_client *client, dev_info(bq->dev, "driver registered\n"); return 0; +error_6: error_5: -error_4: bq2415x_sysfs_exit(bq); -error_3: +error_4: bq2415x_power_supply_exit(bq); +error_3: + if (bq->notify_psy) + power_supply_put(bq->notify_psy); error_2: kfree(name); error_1: @@ -1700,8 +1688,10 @@ static int bq2415x_remove(struct i2c_client *client) { struct bq2415x_device *bq = i2c_get_clientdata(client); - if (bq->notify_psy) + if (bq->notify_psy) { power_supply_unreg_notifier(&bq->nb); + power_supply_put(bq->notify_psy); + } bq2415x_sysfs_exit(bq); bq2415x_power_supply_exit(bq); @@ -1731,6 +1721,7 @@ static const struct i2c_device_id bq2415x_i2c_id_table[] = { { "bq24155", BQ24155 }, { "bq24156", BQ24156 }, { "bq24156a", BQ24156A }, + { "bq24157s", BQ24157S }, { "bq24158", BQ24158 }, {}, }; diff --git a/drivers/power/bq24190_charger.c b/drivers/power/bq24190_charger.c index d0e8236..407c4af 100644 --- a/drivers/power/bq24190_charger.c +++ b/drivers/power/bq24190_charger.c @@ -152,8 +152,8 @@ struct bq24190_dev_info { struct i2c_client *client; struct device *dev; - struct power_supply charger; - struct power_supply battery; + struct power_supply *charger; + struct power_supply *battery; char model_name[I2C_NAME_SIZE]; kernel_ulong_t model; unsigned int gpio_int; @@ -423,8 +423,7 @@ static ssize_t bq24190_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf) { struct power_supply *psy = dev_get_drvdata(dev); - struct bq24190_dev_info *bdi = - container_of(psy, struct bq24190_dev_info, charger); + struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); struct bq24190_sysfs_field_info *info; int ret; u8 v; @@ -444,8 +443,7 @@ static ssize_t bq24190_sysfs_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct power_supply *psy = dev_get_drvdata(dev); - struct bq24190_dev_info *bdi = - container_of(psy, struct bq24190_dev_info, charger); + struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); struct bq24190_sysfs_field_info *info; int ret; u8 v; @@ -469,13 +467,13 @@ static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi) { bq24190_sysfs_init_attrs(); - return sysfs_create_group(&bdi->charger.dev->kobj, + return sysfs_create_group(&bdi->charger->dev.kobj, &bq24190_sysfs_attr_group); } static void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) { - sysfs_remove_group(&bdi->charger.dev->kobj, &bq24190_sysfs_attr_group); + sysfs_remove_group(&bdi->charger->dev.kobj, &bq24190_sysfs_attr_group); } #else static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi) @@ -807,8 +805,7 @@ static int bq24190_charger_set_voltage(struct bq24190_dev_info *bdi, static int bq24190_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct bq24190_dev_info *bdi = - container_of(psy, struct bq24190_dev_info, charger); + struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); int ret; dev_dbg(bdi->dev, "prop: %d\n", psp); @@ -861,8 +858,7 @@ static int bq24190_charger_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { - struct bq24190_dev_info *bdi = - container_of(psy, struct bq24190_dev_info, charger); + struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); int ret; dev_dbg(bdi->dev, "prop: %d\n", psp); @@ -922,18 +918,15 @@ static char *bq24190_charger_supplied_to[] = { "main-battery", }; -static void bq24190_charger_init(struct power_supply *charger) -{ - charger->name = "bq24190-charger"; - charger->type = POWER_SUPPLY_TYPE_USB; - charger->properties = bq24190_charger_properties; - charger->num_properties = ARRAY_SIZE(bq24190_charger_properties); - charger->supplied_to = bq24190_charger_supplied_to; - charger->num_supplicants = ARRAY_SIZE(bq24190_charger_supplied_to); - charger->get_property = bq24190_charger_get_property; - charger->set_property = bq24190_charger_set_property; - charger->property_is_writeable = bq24190_charger_property_is_writeable; -} +static const struct power_supply_desc bq24190_charger_desc = { + .name = "bq24190-charger", + .type = POWER_SUPPLY_TYPE_USB, + .properties = bq24190_charger_properties, + .num_properties = ARRAY_SIZE(bq24190_charger_properties), + .get_property = bq24190_charger_get_property, + .set_property = bq24190_charger_set_property, + .property_is_writeable = bq24190_charger_property_is_writeable, +}; /* Battery power supply property routines */ @@ -1102,8 +1095,7 @@ static int bq24190_battery_set_temp_alert_max(struct bq24190_dev_info *bdi, static int bq24190_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct bq24190_dev_info *bdi = - container_of(psy, struct bq24190_dev_info, battery); + struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); int ret; dev_dbg(bdi->dev, "prop: %d\n", psp); @@ -1144,8 +1136,7 @@ static int bq24190_battery_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { - struct bq24190_dev_info *bdi = - container_of(psy, struct bq24190_dev_info, battery); + struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); int ret; dev_dbg(bdi->dev, "prop: %d\n", psp); @@ -1193,16 +1184,15 @@ static enum power_supply_property bq24190_battery_properties[] = { POWER_SUPPLY_PROP_SCOPE, }; -static void bq24190_battery_init(struct power_supply *battery) -{ - battery->name = "bq24190-battery"; - battery->type = POWER_SUPPLY_TYPE_BATTERY; - battery->properties = bq24190_battery_properties; - battery->num_properties = ARRAY_SIZE(bq24190_battery_properties); - battery->get_property = bq24190_battery_get_property; - battery->set_property = bq24190_battery_set_property; - battery->property_is_writeable = bq24190_battery_property_is_writeable; -} +static const struct power_supply_desc bq24190_battery_desc = { + .name = "bq24190-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = bq24190_battery_properties, + .num_properties = ARRAY_SIZE(bq24190_battery_properties), + .get_property = bq24190_battery_get_property, + .set_property = bq24190_battery_set_property, + .property_is_writeable = bq24190_battery_property_is_writeable, +}; static irqreturn_t bq24190_irq_handler_thread(int irq, void *data) { @@ -1269,8 +1259,8 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data) * interrupt received). */ if (alert_userspace && !bdi->first_time) { - power_supply_changed(&bdi->charger); - power_supply_changed(&bdi->battery); + power_supply_changed(bdi->charger); + power_supply_changed(bdi->battery); bdi->first_time = false; } @@ -1362,6 +1352,7 @@ static int bq24190_probe(struct i2c_client *client, struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct device *dev = &client->dev; struct bq24190_platform_data *pdata = client->dev.platform_data; + struct power_supply_config charger_cfg = {}, battery_cfg = {}; struct bq24190_dev_info *bdi; int ret; @@ -1416,19 +1407,23 @@ static int bq24190_probe(struct i2c_client *client, goto out2; } - bq24190_charger_init(&bdi->charger); - - ret = power_supply_register(dev, &bdi->charger); - if (ret) { + charger_cfg.drv_data = bdi; + charger_cfg.supplied_to = bq24190_charger_supplied_to; + charger_cfg.num_supplicants = ARRAY_SIZE(bq24190_charger_supplied_to), + bdi->charger = power_supply_register(dev, &bq24190_charger_desc, + &charger_cfg); + if (IS_ERR(bdi->charger)) { dev_err(dev, "Can't register charger\n"); + ret = PTR_ERR(bdi->charger); goto out2; } - bq24190_battery_init(&bdi->battery); - - ret = power_supply_register(dev, &bdi->battery); - if (ret) { + battery_cfg.drv_data = bdi; + bdi->battery = power_supply_register(dev, &bq24190_battery_desc, + &battery_cfg); + if (IS_ERR(bdi->battery)) { dev_err(dev, "Can't register battery\n"); + ret = PTR_ERR(bdi->battery); goto out3; } @@ -1441,9 +1436,9 @@ static int bq24190_probe(struct i2c_client *client, return 0; out4: - power_supply_unregister(&bdi->battery); + power_supply_unregister(bdi->battery); out3: - power_supply_unregister(&bdi->charger); + power_supply_unregister(bdi->charger); out2: pm_runtime_disable(dev); out1: @@ -1462,8 +1457,8 @@ static int bq24190_remove(struct i2c_client *client) pm_runtime_put_sync(bdi->dev); bq24190_sysfs_remove_group(bdi); - power_supply_unregister(&bdi->battery); - power_supply_unregister(&bdi->charger); + power_supply_unregister(bdi->battery); + power_supply_unregister(bdi->charger); pm_runtime_disable(bdi->dev); if (bdi->gpio_int) @@ -1499,8 +1494,8 @@ static int bq24190_pm_resume(struct device *dev) pm_runtime_put_sync(bdi->dev); /* Things may have changed while suspended so alert upper layer */ - power_supply_changed(&bdi->charger); - power_supply_changed(&bdi->battery); + power_supply_changed(bdi->charger); + power_supply_changed(bdi->battery); return 0; } diff --git a/drivers/power/bq24735-charger.c b/drivers/power/bq24735-charger.c index d022b82..961a189 100644 --- a/drivers/power/bq24735-charger.c +++ b/drivers/power/bq24735-charger.c @@ -44,14 +44,15 @@ #define BQ24735_DEVICE_ID 0xff struct bq24735 { - struct power_supply charger; - struct i2c_client *client; - struct bq24735_platform *pdata; + struct power_supply *charger; + struct power_supply_desc charger_desc; + struct i2c_client *client; + struct bq24735_platform *pdata; }; static inline struct bq24735 *to_bq24735(struct power_supply *psy) { - return container_of(psy, struct bq24735, charger); + return power_supply_get_drvdata(psy); } static enum power_supply_property bq24735_charger_properties[] = { @@ -192,9 +193,7 @@ static int bq24735_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct bq24735 *charger; - - charger = container_of(psy, struct bq24735, charger); + struct bq24735 *charger = to_bq24735(psy); switch (psp) { case POWER_SUPPLY_PROP_ONLINE: @@ -248,7 +247,8 @@ static int bq24735_charger_probe(struct i2c_client *client, { int ret; struct bq24735 *charger; - struct power_supply *supply; + struct power_supply_desc *supply_desc; + struct power_supply_config psy_cfg = {}; char *name; charger = devm_kzalloc(&client->dev, sizeof(*charger), GFP_KERNEL); @@ -277,16 +277,18 @@ static int bq24735_charger_probe(struct i2c_client *client, charger->client = client; - supply = &charger->charger; + supply_desc = &charger->charger_desc; - supply->name = name; - supply->type = POWER_SUPPLY_TYPE_MAINS; - supply->properties = bq24735_charger_properties; - supply->num_properties = ARRAY_SIZE(bq24735_charger_properties); - supply->get_property = bq24735_charger_get_property; - supply->supplied_to = charger->pdata->supplied_to; - supply->num_supplicants = charger->pdata->num_supplicants; - supply->of_node = client->dev.of_node; + supply_desc->name = name; + supply_desc->type = POWER_SUPPLY_TYPE_MAINS; + supply_desc->properties = bq24735_charger_properties; + supply_desc->num_properties = ARRAY_SIZE(bq24735_charger_properties); + supply_desc->get_property = bq24735_charger_get_property; + + psy_cfg.supplied_to = charger->pdata->supplied_to; + psy_cfg.num_supplicants = charger->pdata->num_supplicants; + psy_cfg.of_node = client->dev.of_node; + psy_cfg.drv_data = charger; i2c_set_clientdata(client, charger); @@ -341,8 +343,10 @@ static int bq24735_charger_probe(struct i2c_client *client, } } - ret = power_supply_register(&client->dev, supply); - if (ret < 0) { + charger->charger = power_supply_register(&client->dev, supply_desc, + &psy_cfg); + if (IS_ERR(charger->charger)) { + ret = PTR_ERR(charger->charger); dev_err(&client->dev, "Failed to register power supply: %d\n", ret); goto err_free_name; @@ -354,7 +358,8 @@ static int bq24735_charger_probe(struct i2c_client *client, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - supply->name, supply); + supply_desc->name, + charger->charger); if (ret) { dev_err(&client->dev, "Unable to register IRQ %d err %d\n", @@ -365,7 +370,7 @@ static int bq24735_charger_probe(struct i2c_client *client, return 0; err_unregister_supply: - power_supply_unregister(supply); + power_supply_unregister(charger->charger); err_free_name: if (name != charger->pdata->name) kfree(name); @@ -381,10 +386,10 @@ static int bq24735_charger_remove(struct i2c_client *client) devm_free_irq(&charger->client->dev, charger->client->irq, &charger->charger); - power_supply_unregister(&charger->charger); + power_supply_unregister(charger->charger); - if (charger->charger.name != charger->pdata->name) - kfree(charger->charger.name); + if (charger->charger_desc.name != charger->pdata->name) + kfree(charger->charger_desc.name); return 0; } diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index b72ba7c..a57433d 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -16,14 +16,12 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - */ - -/* * Datasheets: * http://focus.ti.com/docs/prod/folders/print/bq27000.html * http://focus.ti.com/docs/prod/folders/print/bq27500.html * http://www.ti.com/product/bq27425-g1 * http://www.ti.com/product/BQ27742-G1 + * http://www.ti.com/product/BQ27510-G3 */ #include <linux/device.h> @@ -74,6 +72,10 @@ #define BQ27742_POWER_AVG 0x76 +#define BQ27510_REG_SOC 0x20 +#define BQ27510_REG_DCAP 0x2E /* Design capacity */ +#define BQ27510_REG_CYCT 0x1E /* Cycle count total */ + /* bq27425 register addresses are same as bq27x00 addresses minus 4 */ #define BQ27425_REG_OFFSET 0x04 #define BQ27425_REG_SOC (0x1C + BQ27425_REG_OFFSET) @@ -87,7 +89,7 @@ struct bq27x00_access_methods { int (*read)(struct bq27x00_device_info *di, u8 reg, bool single); }; -enum bq27x00_chip { BQ27000, BQ27500, BQ27425, BQ27742}; +enum bq27x00_chip { BQ27000, BQ27500, BQ27425, BQ27742, BQ27510}; struct bq27x00_reg_cache { int temperature; @@ -114,7 +116,7 @@ struct bq27x00_device_info { unsigned long last_update; struct delayed_work work; - struct power_supply bat; + struct power_supply *bat; struct bq27x00_access_methods bus; @@ -174,6 +176,24 @@ static enum power_supply_property bq27742_battery_props[] = { POWER_SUPPLY_PROP_HEALTH, }; +static enum power_supply_property bq27510_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_POWER_AVG, + POWER_SUPPLY_PROP_HEALTH, +}; + static unsigned int poll_interval = 360; module_param(poll_interval, uint, 0644); MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \ @@ -198,7 +218,8 @@ static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg, */ static bool bq27xxx_is_chip_version_higher(struct bq27x00_device_info *di) { - if (di->chip == BQ27425 || di->chip == BQ27500 || di->chip == BQ27742) + if (di->chip == BQ27425 || di->chip == BQ27500 || di->chip == BQ27742 + || di->chip == BQ27510) return true; return false; } @@ -213,6 +234,8 @@ static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di) if (di->chip == BQ27500 || di->chip == BQ27742) rsoc = bq27x00_read(di, BQ27500_REG_SOC, false); + else if (di->chip == BQ27510) + rsoc = bq27x00_read(di, BQ27510_REG_SOC, false); else if (di->chip == BQ27425) rsoc = bq27x00_read(di, BQ27425_REG_SOC, false); else @@ -286,6 +309,8 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) if (bq27xxx_is_chip_version_higher(di)) { if (di->chip == BQ27425) ilmd = bq27x00_read(di, BQ27425_REG_DCAP, false); + else if (di->chip == BQ27510) + ilmd = bq27x00_read(di, BQ27510_REG_DCAP, false); else ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false); } else @@ -354,7 +379,10 @@ static int bq27x00_battery_read_cyct(struct bq27x00_device_info *di) { int cyct; - cyct = bq27x00_read(di, BQ27x00_REG_CYCT, false); + if (di->chip == BQ27510) + cyct = bq27x00_read(di, BQ27510_REG_CYCT, false); + else + cyct = bq27x00_read(di, BQ27x00_REG_CYCT, false); if (cyct < 0) dev_err(di->dev, "error reading cycle count total\n"); @@ -425,6 +453,10 @@ static int bq27x00_battery_read_health(struct bq27x00_device_info *di) else tval = POWER_SUPPLY_HEALTH_GOOD; return tval; + } else if (di->chip == BQ27510) { + if (tval & BQ27500_FLAG_OTC) + return POWER_SUPPLY_HEALTH_OVERHEAT; + return POWER_SUPPLY_HEALTH_GOOD; } else { if (tval & BQ27000_FLAG_EDV1) tval = POWER_SUPPLY_HEALTH_DEAD; @@ -440,6 +472,7 @@ static void bq27x00_update(struct bq27x00_device_info *di) { struct bq27x00_reg_cache cache = {0, }; bool is_bq27500 = di->chip == BQ27500; + bool is_bq27510 = di->chip == BQ27510; bool is_bq27425 = di->chip == BQ27425; bool is_bq27742 = di->chip == BQ27742; bool flags_1b = !(is_bq27500 || is_bq27742); @@ -449,7 +482,7 @@ static void bq27x00_update(struct bq27x00_device_info *di) /* read error */ cache.flags = -1; if (cache.flags >= 0) { - if (!is_bq27500 && !is_bq27425 && !is_bq27742 + if (!is_bq27500 && !is_bq27425 && !is_bq27742 && !is_bq27510 && (cache.flags & BQ27000_FLAG_CI)) { dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n"); cache.capacity = -ENODATA; @@ -461,7 +494,7 @@ static void bq27x00_update(struct bq27x00_device_info *di) cache.health = -ENODATA; } else { cache.capacity = bq27x00_battery_read_rsoc(di); - if (is_bq27742) + if (is_bq27742 || is_bq27510) cache.time_to_empty = bq27x00_battery_read_time(di, BQ27x00_REG_TTE); @@ -498,7 +531,7 @@ static void bq27x00_update(struct bq27x00_device_info *di) } if (di->cache.capacity != cache.capacity) - power_supply_changed(&di->bat); + power_supply_changed(di->bat); if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) di->cache = cache; @@ -570,7 +603,7 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di, status = POWER_SUPPLY_STATUS_FULL; else if (di->cache.flags & BQ27000_FLAG_CHGS) status = POWER_SUPPLY_STATUS_CHARGING; - else if (power_supply_am_i_supplied(&di->bat)) + else if (power_supply_am_i_supplied(di->bat)) status = POWER_SUPPLY_STATUS_NOT_CHARGING; else status = POWER_SUPPLY_STATUS_DISCHARGING; @@ -642,15 +675,12 @@ static int bq27x00_simple_value(int value, return 0; } -#define to_bq27x00_device_info(x) container_of((x), \ - struct bq27x00_device_info, bat); - static int bq27x00_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { int ret = 0; - struct bq27x00_device_info *di = to_bq27x00_device_info(psy); + struct bq27x00_device_info *di = power_supply_get_drvdata(psy); mutex_lock(&di->lock); if (time_is_before_jiffies(di->last_update + 5 * HZ)) { @@ -728,35 +758,47 @@ static int bq27x00_battery_get_property(struct power_supply *psy, static void bq27x00_external_power_changed(struct power_supply *psy) { - struct bq27x00_device_info *di = to_bq27x00_device_info(psy); + struct bq27x00_device_info *di = power_supply_get_drvdata(psy); cancel_delayed_work_sync(&di->work); schedule_delayed_work(&di->work, 0); } -static int bq27x00_powersupply_init(struct bq27x00_device_info *di) +static int bq27x00_powersupply_init(struct bq27x00_device_info *di, + const char *name) { int ret; + struct power_supply_desc *psy_desc; + struct power_supply_config psy_cfg = { .drv_data = di, }; - di->bat.type = POWER_SUPPLY_TYPE_BATTERY; + psy_desc = devm_kzalloc(di->dev, sizeof(*psy_desc), GFP_KERNEL); + if (!psy_desc) + return -ENOMEM; + + psy_desc->name = name; + psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; if (di->chip == BQ27425) { - di->bat.properties = bq27425_battery_props; - di->bat.num_properties = ARRAY_SIZE(bq27425_battery_props); + psy_desc->properties = bq27425_battery_props; + psy_desc->num_properties = ARRAY_SIZE(bq27425_battery_props); } else if (di->chip == BQ27742) { - di->bat.properties = bq27742_battery_props; - di->bat.num_properties = ARRAY_SIZE(bq27742_battery_props); + psy_desc->properties = bq27742_battery_props; + psy_desc->num_properties = ARRAY_SIZE(bq27742_battery_props); + } else if (di->chip == BQ27510) { + psy_desc->properties = bq27510_battery_props; + psy_desc->num_properties = ARRAY_SIZE(bq27510_battery_props); } else { - di->bat.properties = bq27x00_battery_props; - di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props); + psy_desc->properties = bq27x00_battery_props; + psy_desc->num_properties = ARRAY_SIZE(bq27x00_battery_props); } - di->bat.get_property = bq27x00_battery_get_property; - di->bat.external_power_changed = bq27x00_external_power_changed; + psy_desc->get_property = bq27x00_battery_get_property; + psy_desc->external_power_changed = bq27x00_external_power_changed; INIT_DELAYED_WORK(&di->work, bq27x00_battery_poll); mutex_init(&di->lock); - ret = power_supply_register(di->dev, &di->bat); - if (ret) { + di->bat = power_supply_register_no_ws(di->dev, psy_desc, &psy_cfg); + if (IS_ERR(di->bat)) { + ret = PTR_ERR(di->bat); dev_err(di->dev, "failed to register battery: %d\n", ret); return ret; } @@ -780,7 +822,7 @@ static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di) cancel_delayed_work_sync(&di->work); - power_supply_unregister(&di->bat); + power_supply_unregister(di->bat); mutex_destroy(&di->lock); } @@ -844,37 +886,34 @@ static int bq27x00_battery_probe(struct i2c_client *client, if (num < 0) return num; - name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num); + name = devm_kasprintf(&client->dev, GFP_KERNEL, "%s-%d", id->name, num); if (!name) { dev_err(&client->dev, "failed to allocate device name\n"); retval = -ENOMEM; - goto batt_failed_1; + goto batt_failed; } di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL); if (!di) { dev_err(&client->dev, "failed to allocate device info data\n"); retval = -ENOMEM; - goto batt_failed_2; + goto batt_failed; } di->id = num; di->dev = &client->dev; di->chip = id->driver_data; - di->bat.name = name; di->bus.read = &bq27x00_read_i2c; - retval = bq27x00_powersupply_init(di); + retval = bq27x00_powersupply_init(di, name); if (retval) - goto batt_failed_2; + goto batt_failed; i2c_set_clientdata(client, di); return 0; -batt_failed_2: - kfree(name); -batt_failed_1: +batt_failed: mutex_lock(&battery_mutex); idr_remove(&battery_id, num); mutex_unlock(&battery_mutex); @@ -888,8 +927,6 @@ static int bq27x00_battery_remove(struct i2c_client *client) bq27x00_powersupply_unregister(di); - kfree(di->bat.name); - mutex_lock(&battery_mutex); idr_remove(&battery_id, di->id); mutex_unlock(&battery_mutex); @@ -902,6 +939,7 @@ static const struct i2c_device_id bq27x00_id[] = { { "bq27500", BQ27500 }, { "bq27425", BQ27425 }, { "bq27742", BQ27742 }, + { "bq27510", BQ27510 }, {}, }; MODULE_DEVICE_TABLE(i2c, bq27x00_id); @@ -977,6 +1015,7 @@ static int bq27000_battery_probe(struct platform_device *pdev) { struct bq27x00_device_info *di; struct bq27000_platform_data *pdata = pdev->dev.platform_data; + const char *name; if (!pdata) { dev_err(&pdev->dev, "no platform_data supplied\n"); @@ -999,10 +1038,10 @@ static int bq27000_battery_probe(struct platform_device *pdev) di->dev = &pdev->dev; di->chip = BQ27000; - di->bat.name = pdata->name ?: dev_name(&pdev->dev); + name = pdata->name ?: dev_name(&pdev->dev); di->bus.read = &bq27000_read_platform; - return bq27x00_powersupply_init(di); + return bq27x00_powersupply_init(di, name); } static int bq27000_battery_remove(struct platform_device *pdev) diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 14b0d85..0aed13f 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -103,10 +103,11 @@ static bool is_batt_present(struct charger_manager *cm) if (!psy) break; - ret = psy->get_property(psy, - POWER_SUPPLY_PROP_PRESENT, &val); + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, + &val); if (ret == 0 && val.intval) present = true; + power_supply_put(psy); break; case CM_CHARGER_STAT: for (i = 0; cm->desc->psy_charger_stat[i]; i++) { @@ -118,8 +119,9 @@ static bool is_batt_present(struct charger_manager *cm) continue; } - ret = psy->get_property(psy, POWER_SUPPLY_PROP_PRESENT, - &val); + ret = power_supply_get_property(psy, + POWER_SUPPLY_PROP_PRESENT, &val); + power_supply_put(psy); if (ret == 0 && val.intval) { present = true; break; @@ -155,7 +157,9 @@ static bool is_ext_pwr_online(struct charger_manager *cm) continue; } - ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val); + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, + &val); + power_supply_put(psy); if (ret == 0 && val.intval) { online = true; break; @@ -183,8 +187,9 @@ static int get_batt_uV(struct charger_manager *cm, int *uV) if (!fuel_gauge) return -ENODEV; - ret = fuel_gauge->get_property(fuel_gauge, + ret = power_supply_get_property(fuel_gauge, POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); + power_supply_put(fuel_gauge); if (ret) return ret; @@ -223,20 +228,26 @@ static bool is_charging(struct charger_manager *cm) } /* 2. The charger should be online (ext-power) */ - ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val); + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, + &val); if (ret) { dev_warn(cm->dev, "Cannot read ONLINE value from %s\n", cm->desc->psy_charger_stat[i]); + power_supply_put(psy); continue; } - if (val.intval == 0) + if (val.intval == 0) { + power_supply_put(psy); continue; + } /* * 3. The charger should not be FULL, DISCHARGING, * or NOT_CHARGING. */ - ret = psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &val); + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, + &val); + power_supply_put(psy); if (ret) { dev_warn(cm->dev, "Cannot read STATUS value from %s\n", cm->desc->psy_charger_stat[i]); @@ -264,6 +275,7 @@ static bool is_full_charged(struct charger_manager *cm) struct charger_desc *desc = cm->desc; union power_supply_propval val; struct power_supply *fuel_gauge; + bool is_full = false; int ret = 0; int uV; @@ -279,30 +291,38 @@ static bool is_full_charged(struct charger_manager *cm) val.intval = 0; /* Not full if capacity of fuel gauge isn't full */ - ret = fuel_gauge->get_property(fuel_gauge, + ret = power_supply_get_property(fuel_gauge, POWER_SUPPLY_PROP_CHARGE_FULL, &val); - if (!ret && val.intval > desc->fullbatt_full_capacity) - return true; + if (!ret && val.intval > desc->fullbatt_full_capacity) { + is_full = true; + goto out; + } } /* Full, if it's over the fullbatt voltage */ if (desc->fullbatt_uV > 0) { ret = get_batt_uV(cm, &uV); - if (!ret && uV >= desc->fullbatt_uV) - return true; + if (!ret && uV >= desc->fullbatt_uV) { + is_full = true; + goto out; + } } /* Full, if the capacity is more than fullbatt_soc */ if (desc->fullbatt_soc > 0) { val.intval = 0; - ret = fuel_gauge->get_property(fuel_gauge, + ret = power_supply_get_property(fuel_gauge, POWER_SUPPLY_PROP_CAPACITY, &val); - if (!ret && val.intval >= desc->fullbatt_soc) - return true; + if (!ret && val.intval >= desc->fullbatt_soc) { + is_full = true; + goto out; + } } - return false; +out: + power_supply_put(fuel_gauge); + return is_full; } /** @@ -575,14 +595,18 @@ static int cm_get_battery_temperature_by_psy(struct charger_manager *cm, int *temp) { struct power_supply *fuel_gauge; + int ret; fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); if (!fuel_gauge) return -ENODEV; - return fuel_gauge->get_property(fuel_gauge, + ret = power_supply_get_property(fuel_gauge, POWER_SUPPLY_PROP_TEMP, (union power_supply_propval *)temp); + power_supply_put(fuel_gauge); + + return ret; } static int cm_get_battery_temperature(struct charger_manager *cm, @@ -861,10 +885,9 @@ static int charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct charger_manager *cm = container_of(psy, - struct charger_manager, charger_psy); + struct charger_manager *cm = power_supply_get_drvdata(psy); struct charger_desc *desc = cm->desc; - struct power_supply *fuel_gauge; + struct power_supply *fuel_gauge = NULL; int ret = 0; int uV; @@ -900,26 +923,26 @@ static int charger_get_property(struct power_supply *psy, ret = -ENODEV; break; } - ret = fuel_gauge->get_property(fuel_gauge, + ret = power_supply_get_property(fuel_gauge, POWER_SUPPLY_PROP_CURRENT_NOW, val); break; case POWER_SUPPLY_PROP_TEMP: case POWER_SUPPLY_PROP_TEMP_AMBIENT: return cm_get_battery_temperature(cm, &val->intval); case POWER_SUPPLY_PROP_CAPACITY: - fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); - if (!fuel_gauge) { - ret = -ENODEV; - break; - } - if (!is_batt_present(cm)) { /* There is no battery. Assume 100% */ val->intval = 100; break; } - ret = fuel_gauge->get_property(fuel_gauge, + fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); + if (!fuel_gauge) { + ret = -ENODEV; + break; + } + + ret = power_supply_get_property(fuel_gauge, POWER_SUPPLY_PROP_CAPACITY, val); if (ret) break; @@ -975,7 +998,7 @@ static int charger_get_property(struct power_supply *psy, break; } - ret = fuel_gauge->get_property(fuel_gauge, + ret = power_supply_get_property(fuel_gauge, POWER_SUPPLY_PROP_CHARGE_NOW, val); if (ret) { @@ -993,6 +1016,8 @@ static int charger_get_property(struct power_supply *psy, default: return -EINVAL; } + if (fuel_gauge) + power_supply_put(fuel_gauge); return ret; } @@ -1015,7 +1040,7 @@ static enum power_supply_property default_charger_props[] = { */ }; -static struct power_supply psy_default = { +static const struct power_supply_desc psy_default = { .name = "battery", .type = POWER_SUPPLY_TYPE_BATTERY, .properties = default_charger_props, @@ -1396,7 +1421,7 @@ static int charger_manager_register_sysfs(struct charger_manager *cm) dev_info(cm->dev, "'%s' regulator's externally_control is %d\n", charger->regulator_name, charger->externally_control); - ret = sysfs_create_group(&cm->charger_psy.dev->kobj, + ret = sysfs_create_group(&cm->charger_psy->dev.kobj, &charger->attr_g); if (ret < 0) { dev_err(cm->dev, "Cannot create sysfs entry of %s regulator\n", @@ -1424,13 +1449,13 @@ static int cm_init_thermal_data(struct charger_manager *cm, int ret; /* Verify whether fuel gauge provides battery temperature */ - ret = fuel_gauge->get_property(fuel_gauge, + ret = power_supply_get_property(fuel_gauge, POWER_SUPPLY_PROP_TEMP, &val); if (!ret) { - cm->charger_psy.properties[cm->charger_psy.num_properties] = + cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = POWER_SUPPLY_PROP_TEMP; - cm->charger_psy.num_properties++; + cm->charger_psy_desc.num_properties++; cm->desc->measure_battery_temp = true; } #ifdef CONFIG_THERMAL @@ -1441,9 +1466,9 @@ static int cm_init_thermal_data(struct charger_manager *cm, return PTR_ERR(cm->tzd_batt); /* Use external thermometer */ - cm->charger_psy.properties[cm->charger_psy.num_properties] = + cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = POWER_SUPPLY_PROP_TEMP_AMBIENT; - cm->charger_psy.num_properties++; + cm->charger_psy_desc.num_properties++; cm->desc->measure_battery_temp = true; ret = 0; } @@ -1459,7 +1484,7 @@ static int cm_init_thermal_data(struct charger_manager *cm, return ret; } -static struct of_device_id charger_manager_match[] = { +static const struct of_device_id charger_manager_match[] = { { .compatible = "charger-manager", }, @@ -1603,6 +1628,7 @@ static int charger_manager_probe(struct platform_device *pdev) int j = 0; union power_supply_propval val; struct power_supply *fuel_gauge; + struct power_supply_config psy_cfg = {}; if (IS_ERR(desc)) { dev_err(&pdev->dev, "No platform data (desc) found\n"); @@ -1617,6 +1643,7 @@ static int charger_manager_probe(struct platform_device *pdev) /* Basic Values. Unspecified are Null or 0 */ cm->dev = &pdev->dev; cm->desc = desc; + psy_cfg.drv_data = cm; /* Initialize alarm timer */ if (alarmtimer_get_rtcdev()) { @@ -1672,13 +1699,7 @@ static int charger_manager_probe(struct platform_device *pdev) desc->psy_charger_stat[i]); return -ENODEV; } - } - - fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge); - if (!fuel_gauge) { - dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n", - desc->psy_fuel_gauge); - return -ENODEV; + power_supply_put(psy); } if (desc->polling_interval_ms == 0 || @@ -1696,40 +1717,46 @@ static int charger_manager_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cm); - memcpy(&cm->charger_psy, &psy_default, sizeof(psy_default)); + memcpy(&cm->charger_psy_desc, &psy_default, sizeof(psy_default)); if (!desc->psy_name) strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX); else strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX); - cm->charger_psy.name = cm->psy_name_buf; + cm->charger_psy_desc.name = cm->psy_name_buf; /* Allocate for psy properties because they may vary */ - cm->charger_psy.properties = devm_kzalloc(&pdev->dev, + cm->charger_psy_desc.properties = devm_kzalloc(&pdev->dev, sizeof(enum power_supply_property) * (ARRAY_SIZE(default_charger_props) + NUM_CHARGER_PSY_OPTIONAL), GFP_KERNEL); - if (!cm->charger_psy.properties) + if (!cm->charger_psy_desc.properties) return -ENOMEM; - memcpy(cm->charger_psy.properties, default_charger_props, + memcpy(cm->charger_psy_desc.properties, default_charger_props, sizeof(enum power_supply_property) * ARRAY_SIZE(default_charger_props)); - cm->charger_psy.num_properties = psy_default.num_properties; + cm->charger_psy_desc.num_properties = psy_default.num_properties; /* Find which optional psy-properties are available */ - if (!fuel_gauge->get_property(fuel_gauge, + fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge); + if (!fuel_gauge) { + dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n", + desc->psy_fuel_gauge); + return -ENODEV; + } + if (!power_supply_get_property(fuel_gauge, POWER_SUPPLY_PROP_CHARGE_NOW, &val)) { - cm->charger_psy.properties[cm->charger_psy.num_properties] = + cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = POWER_SUPPLY_PROP_CHARGE_NOW; - cm->charger_psy.num_properties++; + cm->charger_psy_desc.num_properties++; } - if (!fuel_gauge->get_property(fuel_gauge, + if (!power_supply_get_property(fuel_gauge, POWER_SUPPLY_PROP_CURRENT_NOW, &val)) { - cm->charger_psy.properties[cm->charger_psy.num_properties] = + cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = POWER_SUPPLY_PROP_CURRENT_NOW; - cm->charger_psy.num_properties++; + cm->charger_psy_desc.num_properties++; } ret = cm_init_thermal_data(cm, fuel_gauge); @@ -1737,14 +1764,16 @@ static int charger_manager_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to initialize thermal data\n"); cm->desc->measure_battery_temp = false; } + power_supply_put(fuel_gauge); INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk); - ret = power_supply_register(NULL, &cm->charger_psy); - if (ret) { + cm->charger_psy = power_supply_register(NULL, &cm->charger_psy_desc, + &psy_cfg); + if (IS_ERR(cm->charger_psy)) { dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n", - cm->charger_psy.name); - return ret; + cm->charger_psy_desc.name); + return PTR_ERR(cm->charger_psy); } /* Register extcon device for charger cable */ @@ -1790,7 +1819,7 @@ err_reg_sysfs: struct charger_regulator *charger; charger = &desc->charger_regulators[i]; - sysfs_remove_group(&cm->charger_psy.dev->kobj, + sysfs_remove_group(&cm->charger_psy->dev.kobj, &charger->attr_g); } err_reg_extcon: @@ -1808,7 +1837,7 @@ err_reg_extcon: regulator_put(desc->charger_regulators[i].consumer); } - power_supply_unregister(&cm->charger_psy); + power_supply_unregister(cm->charger_psy); return ret; } @@ -1840,7 +1869,7 @@ static int charger_manager_remove(struct platform_device *pdev) for (i = 0 ; i < desc->num_charger_regulators ; i++) regulator_put(desc->charger_regulators[i].consumer); - power_supply_unregister(&cm->charger_psy); + power_supply_unregister(cm->charger_psy); try_charger_enable(cm, false); @@ -1999,7 +2028,7 @@ static bool find_power_supply(struct charger_manager *cm, bool found = false; for (i = 0; cm->desc->psy_charger_stat[i]; i++) { - if (!strcmp(psy->name, cm->desc->psy_charger_stat[i])) { + if (!strcmp(psy->desc->name, cm->desc->psy_charger_stat[i])) { found = true; break; } diff --git a/drivers/power/collie_battery.c b/drivers/power/collie_battery.c index 594e4db..2da9ed8 100644 --- a/drivers/power/collie_battery.c +++ b/drivers/power/collie_battery.c @@ -30,7 +30,7 @@ static int wakeup_enabled; struct collie_bat { int status; - struct power_supply psy; + struct power_supply *psy; int full_chrg; struct mutex work_lock; /* protects data */ @@ -98,7 +98,7 @@ static int collie_bat_get_property(struct power_supply *psy, union power_supply_propval *val) { int ret = 0; - struct collie_bat *bat = container_of(psy, struct collie_bat, psy); + struct collie_bat *bat = power_supply_get_drvdata(psy); if (bat->is_present && !bat->is_present(bat) && psp != POWER_SUPPLY_PROP_PRESENT) { @@ -155,14 +155,14 @@ static irqreturn_t collie_bat_gpio_isr(int irq, void *data) static void collie_bat_update(struct collie_bat *bat) { int old; - struct power_supply *psy = &bat->psy; + struct power_supply *psy = bat->psy; mutex_lock(&bat->work_lock); old = bat->status; if (bat->is_present && !bat->is_present(bat)) { - printk(KERN_NOTICE "%s not present\n", psy->name); + printk(KERN_NOTICE "%s not present\n", psy->desc->name); bat->status = POWER_SUPPLY_STATUS_UNKNOWN; bat->full_chrg = -1; } else if (power_supply_am_i_supplied(psy)) { @@ -220,18 +220,20 @@ static enum power_supply_property collie_bat_bu_props[] = { POWER_SUPPLY_PROP_PRESENT, }; +static const struct power_supply_desc collie_bat_main_desc = { + .name = "main-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = collie_bat_main_props, + .num_properties = ARRAY_SIZE(collie_bat_main_props), + .get_property = collie_bat_get_property, + .external_power_changed = collie_bat_external_power_changed, + .use_for_apm = 1, +}; + static struct collie_bat collie_bat_main = { .status = POWER_SUPPLY_STATUS_DISCHARGING, .full_chrg = -1, - .psy = { - .name = "main-battery", - .type = POWER_SUPPLY_TYPE_BATTERY, - .properties = collie_bat_main_props, - .num_properties = ARRAY_SIZE(collie_bat_main_props), - .get_property = collie_bat_get_property, - .external_power_changed = collie_bat_external_power_changed, - .use_for_apm = 1, - }, + .psy = NULL, .gpio_full = COLLIE_GPIO_CO, .gpio_charge_on = COLLIE_GPIO_CHARGE_ON, @@ -249,18 +251,19 @@ static struct collie_bat collie_bat_main = { .adc_temp_divider = 10000, }; +static const struct power_supply_desc collie_bat_bu_desc = { + .name = "backup-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = collie_bat_bu_props, + .num_properties = ARRAY_SIZE(collie_bat_bu_props), + .get_property = collie_bat_get_property, + .external_power_changed = collie_bat_external_power_changed, +}; + static struct collie_bat collie_bat_bu = { .status = POWER_SUPPLY_STATUS_UNKNOWN, .full_chrg = -1, - - .psy = { - .name = "backup-battery", - .type = POWER_SUPPLY_TYPE_BATTERY, - .properties = collie_bat_bu_props, - .num_properties = ARRAY_SIZE(collie_bat_bu_props), - .get_property = collie_bat_get_property, - .external_power_changed = collie_bat_external_power_changed, - }, + .psy = NULL, .gpio_full = -1, .gpio_charge_on = -1, @@ -319,6 +322,7 @@ static int collie_bat_resume(struct ucb1x00_dev *dev) static int collie_bat_probe(struct ucb1x00_dev *dev) { int ret; + struct power_supply_config psy_main_cfg = {}, psy_bu_cfg = {}; if (!machine_is_collie()) return -ENODEV; @@ -334,12 +338,23 @@ static int collie_bat_probe(struct ucb1x00_dev *dev) INIT_WORK(&bat_work, collie_bat_work); - ret = power_supply_register(&dev->ucb->dev, &collie_bat_main.psy); - if (ret) + psy_main_cfg.drv_data = &collie_bat_main; + collie_bat_main.psy = power_supply_register(&dev->ucb->dev, + &collie_bat_main_desc, + &psy_main_cfg); + if (IS_ERR(collie_bat_main.psy)) { + ret = PTR_ERR(collie_bat_main.psy); goto err_psy_reg_main; - ret = power_supply_register(&dev->ucb->dev, &collie_bat_bu.psy); - if (ret) + } + + psy_main_cfg.drv_data = &collie_bat_bu; + collie_bat_bu.psy = power_supply_register(&dev->ucb->dev, + &collie_bat_bu_desc, + &psy_bu_cfg); + if (IS_ERR(collie_bat_bu.psy)) { + ret = PTR_ERR(collie_bat_bu.psy); goto err_psy_reg_bu; + } ret = request_irq(gpio_to_irq(COLLIE_GPIO_CO), collie_bat_gpio_isr, @@ -354,9 +369,9 @@ static int collie_bat_probe(struct ucb1x00_dev *dev) return 0; err_irq: - power_supply_unregister(&collie_bat_bu.psy); + power_supply_unregister(collie_bat_bu.psy); err_psy_reg_bu: - power_supply_unregister(&collie_bat_main.psy); + power_supply_unregister(collie_bat_main.psy); err_psy_reg_main: /* see comment in collie_bat_remove */ @@ -369,8 +384,8 @@ static void collie_bat_remove(struct ucb1x00_dev *dev) { free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main); - power_supply_unregister(&collie_bat_bu.psy); - power_supply_unregister(&collie_bat_main.psy); + power_supply_unregister(collie_bat_bu.psy); + power_supply_unregister(collie_bat_main.psy); /* * Now cancel the bat_work. We won't get any more schedules, diff --git a/drivers/power/da9030_battery.c b/drivers/power/da9030_battery.c index 78cd5d6..5ca0f4d 100644 --- a/drivers/power/da9030_battery.c +++ b/drivers/power/da9030_battery.c @@ -89,7 +89,8 @@ struct da9030_battery_thresholds { }; struct da9030_charger { - struct power_supply psy; + struct power_supply *psy; + struct power_supply_desc psy_desc; struct device *master; @@ -245,7 +246,7 @@ static void da9030_set_charge(struct da9030_charger *charger, int on) da903x_write(charger->master, DA9030_CHARGE_CONTROL, val); - power_supply_changed(&charger->psy); + power_supply_changed(charger->psy); } static void da9030_charger_check_state(struct da9030_charger *charger) @@ -341,8 +342,7 @@ static int da9030_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct da9030_charger *charger; - charger = container_of(psy, struct da9030_charger, psy); + struct da9030_charger *charger = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_STATUS: @@ -447,16 +447,16 @@ static void da9030_battery_convert_thresholds(struct da9030_charger *charger, static void da9030_battery_setup_psy(struct da9030_charger *charger) { - struct power_supply *psy = &charger->psy; + struct power_supply_desc *psy_desc = &charger->psy_desc; struct power_supply_info *info = charger->battery_info; - psy->name = info->name; - psy->use_for_apm = info->use_for_apm; - psy->type = POWER_SUPPLY_TYPE_BATTERY; - psy->get_property = da9030_battery_get_property; + psy_desc->name = info->name; + psy_desc->use_for_apm = info->use_for_apm; + psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; + psy_desc->get_property = da9030_battery_get_property; - psy->properties = da9030_battery_props; - psy->num_properties = ARRAY_SIZE(da9030_battery_props); + psy_desc->properties = da9030_battery_props; + psy_desc->num_properties = ARRAY_SIZE(da9030_battery_props); }; static int da9030_battery_charger_init(struct da9030_charger *charger) @@ -494,6 +494,7 @@ static int da9030_battery_charger_init(struct da9030_charger *charger) static int da9030_battery_probe(struct platform_device *pdev) { struct da9030_charger *charger; + struct power_supply_config psy_cfg = {}; struct da9030_battery_info *pdata = pdev->dev.platform_data; int ret; @@ -541,9 +542,13 @@ static int da9030_battery_probe(struct platform_device *pdev) goto err_notifier; da9030_battery_setup_psy(charger); - ret = power_supply_register(&pdev->dev, &charger->psy); - if (ret) + psy_cfg.drv_data = charger; + charger->psy = power_supply_register(&pdev->dev, &charger->psy_desc, + &psy_cfg); + if (IS_ERR(charger->psy)) { + ret = PTR_ERR(charger->psy); goto err_ps_register; + } charger->debug_file = da9030_bat_create_debugfs(charger); platform_set_drvdata(pdev, charger); @@ -571,7 +576,7 @@ static int da9030_battery_remove(struct platform_device *dev) DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT); cancel_delayed_work_sync(&charger->work); da9030_set_charge(charger, 0); - power_supply_unregister(&charger->psy); + power_supply_unregister(charger->psy); return 0; } diff --git a/drivers/power/da9052-battery.c b/drivers/power/da9052-battery.c index d17250f..830ec46 100644 --- a/drivers/power/da9052-battery.c +++ b/drivers/power/da9052-battery.c @@ -169,7 +169,7 @@ static u32 const vc_tbl[3][68][2] = { struct da9052_battery { struct da9052 *da9052; - struct power_supply psy; + struct power_supply *psy; struct notifier_block nb; int charger_type; int status; @@ -452,7 +452,7 @@ static irqreturn_t da9052_bat_irq(int irq, void *data) if (irq == DA9052_IRQ_CHGEND || irq == DA9052_IRQ_DCIN || irq == DA9052_IRQ_VBUS || irq == DA9052_IRQ_TBAT) { - power_supply_changed(&bat->psy); + power_supply_changed(bat->psy); } return IRQ_HANDLED; @@ -499,8 +499,7 @@ static int da9052_bat_get_property(struct power_supply *psy, { int ret; int illegal; - struct da9052_battery *bat = container_of(psy, struct da9052_battery, - psy); + struct da9052_battery *bat = power_supply_get_drvdata(psy); ret = da9052_bat_check_presence(bat, &illegal); if (ret < 0) @@ -561,7 +560,7 @@ static enum power_supply_property da9052_bat_props[] = { POWER_SUPPLY_PROP_TECHNOLOGY, }; -static struct power_supply template_battery = { +static struct power_supply_desc psy_desc = { .name = "da9052-bat", .type = POWER_SUPPLY_TYPE_BATTERY, .properties = da9052_bat_props, @@ -591,6 +590,7 @@ static s32 da9052_bat_probe(struct platform_device *pdev) { struct da9052_pdata *pdata; struct da9052_battery *bat; + struct power_supply_config psy_cfg = {}; int ret; int i; @@ -599,8 +599,9 @@ static s32 da9052_bat_probe(struct platform_device *pdev) if (!bat) return -ENOMEM; + psy_cfg.drv_data = bat; + bat->da9052 = dev_get_drvdata(pdev->dev.parent); - bat->psy = template_battery; bat->charger_type = DA9052_NOCHARGER; bat->status = POWER_SUPPLY_STATUS_UNKNOWN; bat->health = POWER_SUPPLY_HEALTH_UNKNOWN; @@ -608,9 +609,9 @@ static s32 da9052_bat_probe(struct platform_device *pdev) pdata = bat->da9052->dev->platform_data; if (pdata != NULL && pdata->use_for_apm) - bat->psy.use_for_apm = pdata->use_for_apm; + psy_desc.use_for_apm = pdata->use_for_apm; else - bat->psy.use_for_apm = 1; + psy_desc.use_for_apm = 1; for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++) { ret = da9052_request_irq(bat->da9052, @@ -625,9 +626,11 @@ static s32 da9052_bat_probe(struct platform_device *pdev) } } - ret = power_supply_register(&pdev->dev, &bat->psy); - if (ret) + bat->psy = power_supply_register(&pdev->dev, &psy_desc, &psy_cfg); + if (IS_ERR(bat->psy)) { + ret = PTR_ERR(bat->psy); goto err; + } platform_set_drvdata(pdev, bat); return 0; @@ -646,7 +649,7 @@ static int da9052_bat_remove(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++) da9052_free_irq(bat->da9052, da9052_bat_irq_bits[i], bat); - power_supply_unregister(&bat->psy); + power_supply_unregister(bat->psy); return 0; } diff --git a/drivers/power/da9150-charger.c b/drivers/power/da9150-charger.c new file mode 100644 index 0000000..6009981 --- /dev/null +++ b/drivers/power/da9150-charger.c @@ -0,0 +1,694 @@ +/* + * DA9150 Charger Driver + * + * Copyright (c) 2014 Dialog Semiconductor + * + * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/interrupt.h> +#include <linux/power_supply.h> +#include <linux/notifier.h> +#include <linux/usb/phy.h> +#include <linux/iio/consumer.h> +#include <linux/mfd/da9150/core.h> +#include <linux/mfd/da9150/registers.h> + +/* Private data */ +struct da9150_charger { + struct da9150 *da9150; + struct device *dev; + + struct power_supply *usb; + struct power_supply *battery; + struct power_supply *supply_online; + + struct usb_phy *usb_phy; + struct notifier_block otg_nb; + struct work_struct otg_work; + unsigned long usb_event; + + struct iio_channel *ibus_chan; + struct iio_channel *vbus_chan; + struct iio_channel *tjunc_chan; + struct iio_channel *vbat_chan; +}; + +static inline int da9150_charger_supply_online(struct da9150_charger *charger, + struct power_supply *psy, + union power_supply_propval *val) +{ + val->intval = (psy == charger->supply_online) ? 1 : 0; + + return 0; +} + +/* Charger Properties */ +static int da9150_charger_vbus_voltage_now(struct da9150_charger *charger, + union power_supply_propval *val) +{ + int v_val, ret; + + /* Read processed value - mV units */ + ret = iio_read_channel_processed(charger->vbus_chan, &v_val); + if (ret < 0) + return ret; + + /* Convert voltage to expected uV units */ + val->intval = v_val * 1000; + + return 0; +} + +static int da9150_charger_ibus_current_avg(struct da9150_charger *charger, + union power_supply_propval *val) +{ + int i_val, ret; + + /* Read processed value - mA units */ + ret = iio_read_channel_processed(charger->ibus_chan, &i_val); + if (ret < 0) + return ret; + + /* Convert current to expected uA units */ + val->intval = i_val * 1000; + + return 0; +} + +static int da9150_charger_tjunc_temp(struct da9150_charger *charger, + union power_supply_propval *val) +{ + int t_val, ret; + + /* Read processed value - 0.001 degrees C units */ + ret = iio_read_channel_processed(charger->tjunc_chan, &t_val); + if (ret < 0) + return ret; + + /* Convert temp to expect 0.1 degrees C units */ + val->intval = t_val / 100; + + return 0; +} + +static enum power_supply_property da9150_charger_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_TEMP, +}; + +static int da9150_charger_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct da9150_charger *charger = dev_get_drvdata(psy->dev.parent); + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ret = da9150_charger_supply_online(charger, psy, val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = da9150_charger_vbus_voltage_now(charger, val); + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + ret = da9150_charger_ibus_current_avg(charger, val); + break; + case POWER_SUPPLY_PROP_TEMP: + ret = da9150_charger_tjunc_temp(charger, val); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/* Battery Properties */ +static int da9150_charger_battery_status(struct da9150_charger *charger, + union power_supply_propval *val) +{ + u8 reg; + + /* Check to see if battery is discharging */ + reg = da9150_reg_read(charger->da9150, DA9150_STATUS_H); + + if (((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_OFF) || + ((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_WAIT)) { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + + return 0; + } + + reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J); + + /* Now check for other states */ + switch (reg & DA9150_CHG_STAT_MASK) { + case DA9150_CHG_STAT_ACT: + case DA9150_CHG_STAT_PRE: + case DA9150_CHG_STAT_CC: + case DA9150_CHG_STAT_CV: + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + case DA9150_CHG_STAT_OFF: + case DA9150_CHG_STAT_SUSP: + case DA9150_CHG_STAT_TEMP: + case DA9150_CHG_STAT_TIME: + case DA9150_CHG_STAT_BAT: + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case DA9150_CHG_STAT_FULL: + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + default: + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + + return 0; +} + +static int da9150_charger_battery_health(struct da9150_charger *charger, + union power_supply_propval *val) +{ + u8 reg; + + reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J); + + /* Check if temperature limit reached */ + switch (reg & DA9150_CHG_TEMP_MASK) { + case DA9150_CHG_TEMP_UNDER: + val->intval = POWER_SUPPLY_HEALTH_COLD; + return 0; + case DA9150_CHG_TEMP_OVER: + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + return 0; + default: + break; + } + + /* Check for other health states */ + switch (reg & DA9150_CHG_STAT_MASK) { + case DA9150_CHG_STAT_ACT: + case DA9150_CHG_STAT_PRE: + val->intval = POWER_SUPPLY_HEALTH_DEAD; + break; + case DA9150_CHG_STAT_TIME: + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + default: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + } + + return 0; +} + +static int da9150_charger_battery_present(struct da9150_charger *charger, + union power_supply_propval *val) +{ + u8 reg; + + /* Check if battery present or removed */ + reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J); + if ((reg & DA9150_CHG_STAT_MASK) == DA9150_CHG_STAT_BAT) + val->intval = 0; + else + val->intval = 1; + + return 0; +} + +static int da9150_charger_battery_charge_type(struct da9150_charger *charger, + union power_supply_propval *val) +{ + u8 reg; + + reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J); + + switch (reg & DA9150_CHG_STAT_MASK) { + case DA9150_CHG_STAT_CC: + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case DA9150_CHG_STAT_ACT: + case DA9150_CHG_STAT_PRE: + case DA9150_CHG_STAT_CV: + val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + default: + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + } + + return 0; +} + +static int da9150_charger_battery_voltage_min(struct da9150_charger *charger, + union power_supply_propval *val) +{ + u8 reg; + + reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_C); + + /* Value starts at 2500 mV, 50 mV increments, presented in uV */ + val->intval = ((reg & DA9150_CHG_VFAULT_MASK) * 50000) + 2500000; + + return 0; +} + +static int da9150_charger_battery_voltage_now(struct da9150_charger *charger, + union power_supply_propval *val) +{ + int v_val, ret; + + /* Read processed value - mV units */ + ret = iio_read_channel_processed(charger->vbat_chan, &v_val); + if (ret < 0) + return ret; + + val->intval = v_val * 1000; + + return 0; +} + +static int da9150_charger_battery_current_max(struct da9150_charger *charger, + union power_supply_propval *val) +{ + int reg; + + reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_D); + + /* 25mA increments */ + val->intval = reg * 25000; + + return 0; +} + +static int da9150_charger_battery_voltage_max(struct da9150_charger *charger, + union power_supply_propval *val) +{ + u8 reg; + + reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_B); + + /* Value starts at 3650 mV, 25 mV increments, presented in uV */ + val->intval = ((reg & DA9150_CHG_VBAT_MASK) * 25000) + 3650000; + return 0; +} + +static enum power_supply_property da9150_charger_bat_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, +}; + +static int da9150_charger_battery_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct da9150_charger *charger = dev_get_drvdata(psy->dev.parent); + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + ret = da9150_charger_battery_status(charger, val); + break; + case POWER_SUPPLY_PROP_ONLINE: + ret = da9150_charger_supply_online(charger, psy, val); + break; + case POWER_SUPPLY_PROP_HEALTH: + ret = da9150_charger_battery_health(charger, val); + break; + case POWER_SUPPLY_PROP_PRESENT: + ret = da9150_charger_battery_present(charger, val); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + ret = da9150_charger_battery_charge_type(charger, val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + ret = da9150_charger_battery_voltage_min(charger, val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = da9150_charger_battery_voltage_now(charger, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + ret = da9150_charger_battery_current_max(charger, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + ret = da9150_charger_battery_voltage_max(charger, val); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static irqreturn_t da9150_charger_chg_irq(int irq, void *data) +{ + struct da9150_charger *charger = data; + + power_supply_changed(charger->battery); + + return IRQ_HANDLED; +} + +static irqreturn_t da9150_charger_tjunc_irq(int irq, void *data) +{ + struct da9150_charger *charger = data; + + /* Nothing we can really do except report this. */ + dev_crit(charger->dev, "TJunc over temperature!!!\n"); + power_supply_changed(charger->usb); + + return IRQ_HANDLED; +} + +static irqreturn_t da9150_charger_vfault_irq(int irq, void *data) +{ + struct da9150_charger *charger = data; + + /* Nothing we can really do except report this. */ + dev_crit(charger->dev, "VSYS under voltage!!!\n"); + power_supply_changed(charger->usb); + power_supply_changed(charger->battery); + + return IRQ_HANDLED; +} + +static irqreturn_t da9150_charger_vbus_irq(int irq, void *data) +{ + struct da9150_charger *charger = data; + u8 reg; + + reg = da9150_reg_read(charger->da9150, DA9150_STATUS_H); + + /* Charger plugged in or battery only */ + switch (reg & DA9150_VBUS_STAT_MASK) { + case DA9150_VBUS_STAT_OFF: + case DA9150_VBUS_STAT_WAIT: + charger->supply_online = charger->battery; + break; + case DA9150_VBUS_STAT_CHG: + charger->supply_online = charger->usb; + break; + default: + dev_warn(charger->dev, "Unknown VBUS state - reg = 0x%x\n", + reg); + charger->supply_online = NULL; + break; + } + + power_supply_changed(charger->usb); + power_supply_changed(charger->battery); + + return IRQ_HANDLED; +} + +static void da9150_charger_otg_work(struct work_struct *data) +{ + struct da9150_charger *charger = + container_of(data, struct da9150_charger, otg_work); + + switch (charger->usb_event) { + case USB_EVENT_ID: + /* Enable OTG Boost */ + da9150_set_bits(charger->da9150, DA9150_PPR_BKCTRL_A, + DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_OTG); + break; + case USB_EVENT_NONE: + /* Revert to charge mode */ + power_supply_changed(charger->usb); + power_supply_changed(charger->battery); + da9150_set_bits(charger->da9150, DA9150_PPR_BKCTRL_A, + DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_CHG); + break; + } +} + +static int da9150_charger_otg_ncb(struct notifier_block *nb, unsigned long val, + void *priv) +{ + struct da9150_charger *charger = + container_of(nb, struct da9150_charger, otg_nb); + + dev_dbg(charger->dev, "DA9150 OTG notify %lu\n", val); + + charger->usb_event = val; + schedule_work(&charger->otg_work); + + return NOTIFY_OK; +} + +static int da9150_charger_register_irq(struct platform_device *pdev, + irq_handler_t handler, + const char *irq_name) +{ + struct device *dev = &pdev->dev; + struct da9150_charger *charger = platform_get_drvdata(pdev); + int irq, ret; + + irq = platform_get_irq_byname(pdev, irq_name); + if (irq < 0) { + dev_err(dev, "Failed to get IRQ CHG_STATUS: %d\n", irq); + return irq; + } + + ret = request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, irq_name, + charger); + if (ret) + dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret); + + return ret; +} + +static void da9150_charger_unregister_irq(struct platform_device *pdev, + const char *irq_name) +{ + struct device *dev = &pdev->dev; + struct da9150_charger *charger = platform_get_drvdata(pdev); + int irq; + + irq = platform_get_irq_byname(pdev, irq_name); + if (irq < 0) { + dev_err(dev, "Failed to get IRQ CHG_STATUS: %d\n", irq); + return; + } + + free_irq(irq, charger); +} + +static const struct power_supply_desc usb_desc = { + .name = "da9150-usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = da9150_charger_props, + .num_properties = ARRAY_SIZE(da9150_charger_props), + .get_property = da9150_charger_get_prop, +}; + +static const struct power_supply_desc battery_desc = { + .name = "da9150-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = da9150_charger_bat_props, + .num_properties = ARRAY_SIZE(da9150_charger_bat_props), + .get_property = da9150_charger_battery_get_prop, +}; + +static int da9150_charger_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct da9150 *da9150 = dev_get_drvdata(dev->parent); + struct da9150_charger *charger; + u8 reg; + int ret; + + charger = devm_kzalloc(dev, sizeof(struct da9150_charger), GFP_KERNEL); + if (!charger) + return -ENOMEM; + + platform_set_drvdata(pdev, charger); + charger->da9150 = da9150; + charger->dev = dev; + + /* Acquire ADC channels */ + charger->ibus_chan = iio_channel_get(dev, "CHAN_IBUS"); + if (IS_ERR(charger->ibus_chan)) { + ret = PTR_ERR(charger->ibus_chan); + goto ibus_chan_fail; + } + + charger->vbus_chan = iio_channel_get(dev, "CHAN_VBUS"); + if (IS_ERR(charger->vbus_chan)) { + ret = PTR_ERR(charger->vbus_chan); + goto vbus_chan_fail; + } + + charger->tjunc_chan = iio_channel_get(dev, "CHAN_TJUNC"); + if (IS_ERR(charger->tjunc_chan)) { + ret = PTR_ERR(charger->tjunc_chan); + goto tjunc_chan_fail; + } + + charger->vbat_chan = iio_channel_get(dev, "CHAN_VBAT"); + if (IS_ERR(charger->vbat_chan)) { + ret = PTR_ERR(charger->vbat_chan); + goto vbat_chan_fail; + } + + /* Register power supplies */ + charger->usb = power_supply_register(dev, &usb_desc, NULL); + if (IS_ERR(charger->usb)) { + ret = PTR_ERR(charger->usb); + goto usb_fail; + } + + charger->battery = power_supply_register(dev, &battery_desc, NULL); + if (IS_ERR(charger->battery)) { + ret = PTR_ERR(charger->battery); + goto battery_fail; + } + + /* Get initial online supply */ + reg = da9150_reg_read(da9150, DA9150_STATUS_H); + + switch (reg & DA9150_VBUS_STAT_MASK) { + case DA9150_VBUS_STAT_OFF: + case DA9150_VBUS_STAT_WAIT: + charger->supply_online = charger->battery; + break; + case DA9150_VBUS_STAT_CHG: + charger->supply_online = charger->usb; + break; + default: + dev_warn(dev, "Unknown VBUS state - reg = 0x%x\n", reg); + charger->supply_online = NULL; + break; + } + + /* Setup OTG reporting & configuration */ + charger->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + if (!IS_ERR_OR_NULL(charger->usb_phy)) { + INIT_WORK(&charger->otg_work, da9150_charger_otg_work); + charger->otg_nb.notifier_call = da9150_charger_otg_ncb; + usb_register_notifier(charger->usb_phy, &charger->otg_nb); + } + + /* Register IRQs */ + ret = da9150_charger_register_irq(pdev, da9150_charger_chg_irq, + "CHG_STATUS"); + if (ret < 0) + goto chg_irq_fail; + + ret = da9150_charger_register_irq(pdev, da9150_charger_tjunc_irq, + "CHG_TJUNC"); + if (ret < 0) + goto tjunc_irq_fail; + + ret = da9150_charger_register_irq(pdev, da9150_charger_vfault_irq, + "CHG_VFAULT"); + if (ret < 0) + goto vfault_irq_fail; + + ret = da9150_charger_register_irq(pdev, da9150_charger_vbus_irq, + "CHG_VBUS"); + if (ret < 0) + goto vbus_irq_fail; + + return 0; + + +vbus_irq_fail: + da9150_charger_unregister_irq(pdev, "CHG_VFAULT"); +vfault_irq_fail: + da9150_charger_unregister_irq(pdev, "CHG_TJUNC"); +tjunc_irq_fail: + da9150_charger_unregister_irq(pdev, "CHG_STATUS"); +chg_irq_fail: + if (!IS_ERR_OR_NULL(charger->usb_phy)) + usb_unregister_notifier(charger->usb_phy, &charger->otg_nb); +battery_fail: + power_supply_unregister(charger->usb); + +usb_fail: + iio_channel_release(charger->vbat_chan); + +vbat_chan_fail: + iio_channel_release(charger->tjunc_chan); + +tjunc_chan_fail: + iio_channel_release(charger->vbus_chan); + +vbus_chan_fail: + iio_channel_release(charger->ibus_chan); + +ibus_chan_fail: + return ret; +} + +static int da9150_charger_remove(struct platform_device *pdev) +{ + struct da9150_charger *charger = platform_get_drvdata(pdev); + int irq; + + /* Make sure IRQs are released before unregistering power supplies */ + irq = platform_get_irq_byname(pdev, "CHG_VBUS"); + free_irq(irq, charger); + + irq = platform_get_irq_byname(pdev, "CHG_VFAULT"); + free_irq(irq, charger); + + irq = platform_get_irq_byname(pdev, "CHG_TJUNC"); + free_irq(irq, charger); + + irq = platform_get_irq_byname(pdev, "CHG_STATUS"); + free_irq(irq, charger); + + if (!IS_ERR_OR_NULL(charger->usb_phy)) + usb_unregister_notifier(charger->usb_phy, &charger->otg_nb); + + power_supply_unregister(charger->battery); + power_supply_unregister(charger->usb); + + /* Release ADC channels */ + iio_channel_release(charger->ibus_chan); + iio_channel_release(charger->vbus_chan); + iio_channel_release(charger->tjunc_chan); + iio_channel_release(charger->vbat_chan); + + return 0; +} + +static struct platform_driver da9150_charger_driver = { + .driver = { + .name = "da9150-charger", + }, + .probe = da9150_charger_probe, + .remove = da9150_charger_remove, +}; + +module_platform_driver(da9150_charger_driver); + +MODULE_DESCRIPTION("Charger Driver for DA9150"); +MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c index 85b4e6e..80f73cc 100644 --- a/drivers/power/ds2760_battery.c +++ b/drivers/power/ds2760_battery.c @@ -53,7 +53,8 @@ struct ds2760_device_info { int charge_status; /* POWER_SUPPLY_STATUS_* */ int full_counter; - struct power_supply bat; + struct power_supply *bat; + struct power_supply_desc bat_desc; struct device *w1_dev; struct workqueue_struct *monitor_wqueue; struct delayed_work monitor_work; @@ -254,7 +255,7 @@ static void ds2760_battery_update_status(struct ds2760_device_info *di) if (di->charge_status == POWER_SUPPLY_STATUS_UNKNOWN) di->full_counter = 0; - if (power_supply_am_i_supplied(&di->bat)) { + if (power_supply_am_i_supplied(di->bat)) { if (di->current_uA > 10000) { di->charge_status = POWER_SUPPLY_STATUS_CHARGING; di->full_counter = 0; @@ -287,7 +288,7 @@ static void ds2760_battery_update_status(struct ds2760_device_info *di) } if (di->charge_status != old_charge_status) - power_supply_changed(&di->bat); + power_supply_changed(di->bat); } static void ds2760_battery_write_status(struct ds2760_device_info *di, @@ -346,12 +347,9 @@ static void ds2760_battery_work(struct work_struct *work) queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval); } -#define to_ds2760_device_info(x) container_of((x), struct ds2760_device_info, \ - bat); - static void ds2760_battery_external_power_changed(struct power_supply *psy) { - struct ds2760_device_info *di = to_ds2760_device_info(psy); + struct ds2760_device_info *di = power_supply_get_drvdata(psy); dev_dbg(di->dev, "%s\n", __func__); @@ -377,7 +375,7 @@ static void ds2760_battery_set_charged_work(struct work_struct *work) * that error. */ - if (!power_supply_am_i_supplied(&di->bat)) + if (!power_supply_am_i_supplied(di->bat)) return; bias = (signed char) di->current_raw + @@ -396,7 +394,7 @@ static void ds2760_battery_set_charged_work(struct work_struct *work) static void ds2760_battery_set_charged(struct power_supply *psy) { - struct ds2760_device_info *di = to_ds2760_device_info(psy); + struct ds2760_device_info *di = power_supply_get_drvdata(psy); /* postpone the actual work by 20 secs. This is for debouncing GPIO * signals and to let the current value settle. See AN4188. */ @@ -407,7 +405,7 @@ static int ds2760_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct ds2760_device_info *di = to_ds2760_device_info(psy); + struct ds2760_device_info *di = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_STATUS: @@ -458,7 +456,7 @@ static int ds2760_battery_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { - struct ds2760_device_info *di = to_ds2760_device_info(psy); + struct ds2760_device_info *di = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_CHARGE_FULL: @@ -508,6 +506,7 @@ static enum power_supply_property ds2760_battery_props[] = { static int ds2760_battery_probe(struct platform_device *pdev) { + struct power_supply_config psy_cfg = {}; char status; int retval = 0; struct ds2760_device_info *di; @@ -520,20 +519,22 @@ static int ds2760_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, di); - di->dev = &pdev->dev; - di->w1_dev = pdev->dev.parent; - di->bat.name = dev_name(&pdev->dev); - di->bat.type = POWER_SUPPLY_TYPE_BATTERY; - di->bat.properties = ds2760_battery_props; - di->bat.num_properties = ARRAY_SIZE(ds2760_battery_props); - di->bat.get_property = ds2760_battery_get_property; - di->bat.set_property = ds2760_battery_set_property; - di->bat.property_is_writeable = + di->dev = &pdev->dev; + di->w1_dev = pdev->dev.parent; + di->bat_desc.name = dev_name(&pdev->dev); + di->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; + di->bat_desc.properties = ds2760_battery_props; + di->bat_desc.num_properties = ARRAY_SIZE(ds2760_battery_props); + di->bat_desc.get_property = ds2760_battery_get_property; + di->bat_desc.set_property = ds2760_battery_set_property; + di->bat_desc.property_is_writeable = ds2760_battery_property_is_writeable; - di->bat.set_charged = ds2760_battery_set_charged; - di->bat.external_power_changed = + di->bat_desc.set_charged = ds2760_battery_set_charged; + di->bat_desc.external_power_changed = ds2760_battery_external_power_changed; + psy_cfg.drv_data = di; + di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; /* enable sleep mode feature */ @@ -555,9 +556,10 @@ static int ds2760_battery_probe(struct platform_device *pdev) if (current_accum) ds2760_battery_set_current_accum(di, current_accum); - retval = power_supply_register(&pdev->dev, &di->bat); - if (retval) { + di->bat = power_supply_register(&pdev->dev, &di->bat_desc, &psy_cfg); + if (IS_ERR(di->bat)) { dev_err(di->dev, "failed to register battery\n"); + retval = PTR_ERR(di->bat); goto batt_failed; } @@ -574,7 +576,7 @@ static int ds2760_battery_probe(struct platform_device *pdev) goto success; workqueue_failed: - power_supply_unregister(&di->bat); + power_supply_unregister(di->bat); batt_failed: di_alloc_failed: success: @@ -588,7 +590,7 @@ static int ds2760_battery_remove(struct platform_device *pdev) cancel_delayed_work_sync(&di->monitor_work); cancel_delayed_work_sync(&di->set_charged_work); destroy_workqueue(di->monitor_wqueue); - power_supply_unregister(&di->bat); + power_supply_unregister(di->bat); return 0; } @@ -610,7 +612,7 @@ static int ds2760_battery_resume(struct platform_device *pdev) struct ds2760_device_info *di = platform_get_drvdata(pdev); di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; - power_supply_changed(&di->bat); + power_supply_changed(di->bat); mod_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ); diff --git a/drivers/power/ds2780_battery.c b/drivers/power/ds2780_battery.c index 9f418fa..a7a0427 100644 --- a/drivers/power/ds2780_battery.c +++ b/drivers/power/ds2780_battery.c @@ -37,7 +37,8 @@ struct ds2780_device_info { struct device *dev; - struct power_supply bat; + struct power_supply *bat; + struct power_supply_desc bat_desc; struct device *w1_dev; }; @@ -52,7 +53,7 @@ static const char manufacturer[] = "Maxim/Dallas"; static inline struct ds2780_device_info * to_ds2780_device_info(struct power_supply *psy) { - return container_of(psy, struct ds2780_device_info, bat); + return power_supply_get_drvdata(psy); } static inline struct power_supply *to_power_supply(struct device *dev) @@ -757,6 +758,7 @@ static const struct attribute_group ds2780_attr_group = { static int ds2780_battery_probe(struct platform_device *pdev) { + struct power_supply_config psy_cfg = {}; int ret = 0; struct ds2780_device_info *dev_info; @@ -770,25 +772,29 @@ static int ds2780_battery_probe(struct platform_device *pdev) dev_info->dev = &pdev->dev; dev_info->w1_dev = pdev->dev.parent; - dev_info->bat.name = dev_name(&pdev->dev); - dev_info->bat.type = POWER_SUPPLY_TYPE_BATTERY; - dev_info->bat.properties = ds2780_battery_props; - dev_info->bat.num_properties = ARRAY_SIZE(ds2780_battery_props); - dev_info->bat.get_property = ds2780_battery_get_property; + dev_info->bat_desc.name = dev_name(&pdev->dev); + dev_info->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; + dev_info->bat_desc.properties = ds2780_battery_props; + dev_info->bat_desc.num_properties = ARRAY_SIZE(ds2780_battery_props); + dev_info->bat_desc.get_property = ds2780_battery_get_property; - ret = power_supply_register(&pdev->dev, &dev_info->bat); - if (ret) { + psy_cfg.drv_data = dev_info; + + dev_info->bat = power_supply_register(&pdev->dev, &dev_info->bat_desc, + &psy_cfg); + if (IS_ERR(dev_info->bat)) { dev_err(dev_info->dev, "failed to register battery\n"); + ret = PTR_ERR(dev_info->bat); goto fail; } - ret = sysfs_create_group(&dev_info->bat.dev->kobj, &ds2780_attr_group); + ret = sysfs_create_group(&dev_info->bat->dev.kobj, &ds2780_attr_group); if (ret) { dev_err(dev_info->dev, "failed to create sysfs group\n"); goto fail_unregister; } - ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj, + ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj, &ds2780_param_eeprom_bin_attr); if (ret) { dev_err(dev_info->dev, @@ -796,7 +802,7 @@ static int ds2780_battery_probe(struct platform_device *pdev) goto fail_remove_group; } - ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj, + ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj, &ds2780_user_eeprom_bin_attr); if (ret) { dev_err(dev_info->dev, @@ -807,12 +813,12 @@ static int ds2780_battery_probe(struct platform_device *pdev) return 0; fail_remove_bin_file: - sysfs_remove_bin_file(&dev_info->bat.dev->kobj, + sysfs_remove_bin_file(&dev_info->bat->dev.kobj, &ds2780_param_eeprom_bin_attr); fail_remove_group: - sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2780_attr_group); + sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2780_attr_group); fail_unregister: - power_supply_unregister(&dev_info->bat); + power_supply_unregister(dev_info->bat); fail: return ret; } @@ -821,10 +827,13 @@ static int ds2780_battery_remove(struct platform_device *pdev) { struct ds2780_device_info *dev_info = platform_get_drvdata(pdev); - /* remove attributes */ - sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2780_attr_group); + /* + * Remove attributes before unregistering power supply + * because 'bat' will be freed on power_supply_unregister() call. + */ + sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2780_attr_group); - power_supply_unregister(&dev_info->bat); + power_supply_unregister(dev_info->bat); return 0; } diff --git a/drivers/power/ds2781_battery.c b/drivers/power/ds2781_battery.c index 0a5acc6..56d583d 100644 --- a/drivers/power/ds2781_battery.c +++ b/drivers/power/ds2781_battery.c @@ -35,7 +35,8 @@ struct ds2781_device_info { struct device *dev; - struct power_supply bat; + struct power_supply *bat; + struct power_supply_desc bat_desc; struct device *w1_dev; }; @@ -50,7 +51,7 @@ static const char manufacturer[] = "Maxim/Dallas"; static inline struct ds2781_device_info * to_ds2781_device_info(struct power_supply *psy) { - return container_of(psy, struct ds2781_device_info, bat); + return power_supply_get_drvdata(psy); } static inline struct power_supply *to_power_supply(struct device *dev) @@ -328,7 +329,7 @@ static int ds2781_get_status(struct ds2781_device_info *dev_info, int *status) if (ret < 0) return ret; - if (power_supply_am_i_supplied(&dev_info->bat)) { + if (power_supply_am_i_supplied(dev_info->bat)) { if (capacity == 100) *status = POWER_SUPPLY_STATUS_FULL; else if (current_uA > 50000) @@ -752,6 +753,7 @@ static const struct attribute_group ds2781_attr_group = { static int ds2781_battery_probe(struct platform_device *pdev) { + struct power_supply_config psy_cfg = {}; int ret = 0; struct ds2781_device_info *dev_info; @@ -763,25 +765,29 @@ static int ds2781_battery_probe(struct platform_device *pdev) dev_info->dev = &pdev->dev; dev_info->w1_dev = pdev->dev.parent; - dev_info->bat.name = dev_name(&pdev->dev); - dev_info->bat.type = POWER_SUPPLY_TYPE_BATTERY; - dev_info->bat.properties = ds2781_battery_props; - dev_info->bat.num_properties = ARRAY_SIZE(ds2781_battery_props); - dev_info->bat.get_property = ds2781_battery_get_property; + dev_info->bat_desc.name = dev_name(&pdev->dev); + dev_info->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; + dev_info->bat_desc.properties = ds2781_battery_props; + dev_info->bat_desc.num_properties = ARRAY_SIZE(ds2781_battery_props); + dev_info->bat_desc.get_property = ds2781_battery_get_property; - ret = power_supply_register(&pdev->dev, &dev_info->bat); - if (ret) { + psy_cfg.drv_data = dev_info; + + dev_info->bat = power_supply_register(&pdev->dev, &dev_info->bat_desc, + &psy_cfg); + if (IS_ERR(dev_info->bat)) { dev_err(dev_info->dev, "failed to register battery\n"); + ret = PTR_ERR(dev_info->bat); goto fail; } - ret = sysfs_create_group(&dev_info->bat.dev->kobj, &ds2781_attr_group); + ret = sysfs_create_group(&dev_info->bat->dev.kobj, &ds2781_attr_group); if (ret) { dev_err(dev_info->dev, "failed to create sysfs group\n"); goto fail_unregister; } - ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj, + ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj, &ds2781_param_eeprom_bin_attr); if (ret) { dev_err(dev_info->dev, @@ -789,7 +795,7 @@ static int ds2781_battery_probe(struct platform_device *pdev) goto fail_remove_group; } - ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj, + ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj, &ds2781_user_eeprom_bin_attr); if (ret) { dev_err(dev_info->dev, @@ -800,12 +806,12 @@ static int ds2781_battery_probe(struct platform_device *pdev) return 0; fail_remove_bin_file: - sysfs_remove_bin_file(&dev_info->bat.dev->kobj, + sysfs_remove_bin_file(&dev_info->bat->dev.kobj, &ds2781_param_eeprom_bin_attr); fail_remove_group: - sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2781_attr_group); + sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2781_attr_group); fail_unregister: - power_supply_unregister(&dev_info->bat); + power_supply_unregister(dev_info->bat); fail: return ret; } @@ -814,10 +820,13 @@ static int ds2781_battery_remove(struct platform_device *pdev) { struct ds2781_device_info *dev_info = platform_get_drvdata(pdev); - /* remove attributes */ - sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2781_attr_group); + /* + * Remove attributes before unregistering power supply + * because 'bat' will be freed on power_supply_unregister() call. + */ + sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2781_attr_group); - power_supply_unregister(&dev_info->bat); + power_supply_unregister(dev_info->bat); return 0; } diff --git a/drivers/power/ds2782_battery.c b/drivers/power/ds2782_battery.c index 3969488..ed4d756 100644 --- a/drivers/power/ds2782_battery.c +++ b/drivers/power/ds2782_battery.c @@ -53,11 +53,12 @@ struct ds278x_battery_ops { int (*get_battery_capacity)(struct ds278x_info *info, int *capacity); }; -#define to_ds278x_info(x) container_of(x, struct ds278x_info, battery) +#define to_ds278x_info(x) power_supply_get_drvdata(x) struct ds278x_info { struct i2c_client *client; - struct power_supply battery; + struct power_supply *battery; + struct power_supply_desc battery_desc; struct ds278x_battery_ops *ops; struct delayed_work bat_work; int id; @@ -285,7 +286,7 @@ static void ds278x_bat_update(struct ds278x_info *info) ds278x_get_status(info, &info->status); if ((old_status != info->status) || (old_capacity != info->capacity)) - power_supply_changed(&info->battery); + power_supply_changed(info->battery); } static void ds278x_bat_work(struct work_struct *work) @@ -306,7 +307,7 @@ static enum power_supply_property ds278x_battery_props[] = { POWER_SUPPLY_PROP_TEMP, }; -static void ds278x_power_supply_init(struct power_supply *battery) +static void ds278x_power_supply_init(struct power_supply_desc *battery) { battery->type = POWER_SUPPLY_TYPE_BATTERY; battery->properties = ds278x_battery_props; @@ -319,8 +320,8 @@ static int ds278x_battery_remove(struct i2c_client *client) { struct ds278x_info *info = i2c_get_clientdata(client); - power_supply_unregister(&info->battery); - kfree(info->battery.name); + power_supply_unregister(info->battery); + kfree(info->battery_desc.name); mutex_lock(&battery_lock); idr_remove(&battery_id, info->id); @@ -377,6 +378,7 @@ static int ds278x_battery_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ds278x_platform_data *pdata = client->dev.platform_data; + struct power_supply_config psy_cfg = {}; struct ds278x_info *info; int ret; int num; @@ -404,8 +406,9 @@ static int ds278x_battery_probe(struct i2c_client *client, goto fail_info; } - info->battery.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num); - if (!info->battery.name) { + info->battery_desc.name = kasprintf(GFP_KERNEL, "%s-%d", + client->name, num); + if (!info->battery_desc.name) { ret = -ENOMEM; goto fail_name; } @@ -417,16 +420,19 @@ static int ds278x_battery_probe(struct i2c_client *client, info->client = client; info->id = num; info->ops = &ds278x_ops[id->driver_data]; - ds278x_power_supply_init(&info->battery); + ds278x_power_supply_init(&info->battery_desc); + psy_cfg.drv_data = info; info->capacity = 100; info->status = POWER_SUPPLY_STATUS_FULL; INIT_DELAYED_WORK(&info->bat_work, ds278x_bat_work); - ret = power_supply_register(&client->dev, &info->battery); - if (ret) { + info->battery = power_supply_register(&client->dev, + &info->battery_desc, &psy_cfg); + if (IS_ERR(info->battery)) { dev_err(&client->dev, "failed to register battery\n"); + ret = PTR_ERR(info->battery); goto fail_register; } else { schedule_delayed_work(&info->bat_work, DS278x_DELAY); @@ -435,7 +441,7 @@ static int ds278x_battery_probe(struct i2c_client *client, return 0; fail_register: - kfree(info->battery.name); + kfree(info->battery_desc.name); fail_name: kfree(info); fail_info: diff --git a/drivers/power/generic-adc-battery.c b/drivers/power/generic-adc-battery.c index d72733e..fedc581 100644 --- a/drivers/power/generic-adc-battery.c +++ b/drivers/power/generic-adc-battery.c @@ -44,7 +44,8 @@ static const char *const gab_chan_name[] = { }; struct gab { - struct power_supply psy; + struct power_supply *psy; + struct power_supply_desc psy_desc; struct iio_channel *channel[GAB_MAX_CHAN_TYPE]; struct gab_platform_data *pdata; struct delayed_work bat_work; @@ -55,7 +56,7 @@ struct gab { static struct gab *to_generic_bat(struct power_supply *psy) { - return container_of(psy, struct gab, psy); + return power_supply_get_drvdata(psy); } static void gab_ext_power_changed(struct power_supply *psy) @@ -151,7 +152,7 @@ static int gab_get_property(struct power_supply *psy, adc_bat = to_generic_bat(psy); if (!adc_bat) { - dev_err(psy->dev, "no battery infos ?!\n"); + dev_err(&psy->dev, "no battery infos ?!\n"); return -EINVAL; } pdata = adc_bat->pdata; @@ -159,7 +160,7 @@ static int gab_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_STATUS: - gab_get_status(adc_bat); + val->intval = gab_get_status(adc_bat); break; case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN: val->intval = 0; @@ -210,7 +211,7 @@ static void gab_work(struct work_struct *work) pdata = adc_bat->pdata; status = adc_bat->status; - is_plugged = power_supply_am_i_supplied(&adc_bat->psy); + is_plugged = power_supply_am_i_supplied(adc_bat->psy); adc_bat->cable_plugged = is_plugged; if (!is_plugged) @@ -221,7 +222,7 @@ static void gab_work(struct work_struct *work) adc_bat->status = POWER_SUPPLY_STATUS_CHARGING; if (status != adc_bat->status) - power_supply_changed(&adc_bat->psy); + power_supply_changed(adc_bat->psy); } static irqreturn_t gab_charged(int irq, void *dev_id) @@ -239,7 +240,8 @@ static irqreturn_t gab_charged(int irq, void *dev_id) static int gab_probe(struct platform_device *pdev) { struct gab *adc_bat; - struct power_supply *psy; + struct power_supply_desc *psy_desc; + struct power_supply_config psy_cfg = {}; struct gab_platform_data *pdata = pdev->dev.platform_data; enum power_supply_property *properties; int ret = 0; @@ -252,32 +254,34 @@ static int gab_probe(struct platform_device *pdev) return -ENOMEM; } - psy = &adc_bat->psy; - psy->name = pdata->battery_info.name; + psy_cfg.drv_data = adc_bat; + psy_desc = &adc_bat->psy_desc; + psy_desc->name = pdata->battery_info.name; /* bootup default values for the battery */ adc_bat->cable_plugged = false; adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING; - psy->type = POWER_SUPPLY_TYPE_BATTERY; - psy->get_property = gab_get_property; - psy->external_power_changed = gab_ext_power_changed; + psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; + psy_desc->get_property = gab_get_property; + psy_desc->external_power_changed = gab_ext_power_changed; adc_bat->pdata = pdata; /* * copying the static properties and allocating extra memory for holding * the extra configurable properties received from platform data. */ - psy->properties = kcalloc(ARRAY_SIZE(gab_props) + + psy_desc->properties = kcalloc(ARRAY_SIZE(gab_props) + ARRAY_SIZE(gab_chan_name), - sizeof(*psy->properties), GFP_KERNEL); - if (!psy->properties) { + sizeof(*psy_desc->properties), + GFP_KERNEL); + if (!psy_desc->properties) { ret = -ENOMEM; goto first_mem_fail; } - memcpy(psy->properties, gab_props, sizeof(gab_props)); + memcpy(psy_desc->properties, gab_props, sizeof(gab_props)); properties = (enum power_supply_property *) - ((char *)psy->properties + sizeof(gab_props)); + ((char *)psy_desc->properties + sizeof(gab_props)); /* * getting channel from iio and copying the battery properties @@ -291,7 +295,7 @@ static int gab_probe(struct platform_device *pdev) adc_bat->channel[chan] = NULL; } else { /* copying properties for supported channels only */ - memcpy(properties + sizeof(*(psy->properties)) * index, + memcpy(properties + sizeof(*(psy_desc->properties)) * index, &gab_dyn_props[chan], sizeof(gab_dyn_props[chan])); index++; @@ -310,11 +314,13 @@ static int gab_probe(struct platform_device *pdev) * as come channels may be not be supported by the device.So * we need to take care of that. */ - psy->num_properties = ARRAY_SIZE(gab_props) + index; + psy_desc->num_properties = ARRAY_SIZE(gab_props) + index; - ret = power_supply_register(&pdev->dev, psy); - if (ret) + adc_bat->psy = power_supply_register(&pdev->dev, psy_desc, &psy_cfg); + if (IS_ERR(adc_bat->psy)) { + ret = PTR_ERR(adc_bat->psy); goto err_reg_fail; + } INIT_DELAYED_WORK(&adc_bat->bat_work, gab_work); @@ -342,14 +348,14 @@ static int gab_probe(struct platform_device *pdev) err_gpio: gpio_free(pdata->gpio_charge_finished); gpio_req_fail: - power_supply_unregister(psy); + power_supply_unregister(adc_bat->psy); err_reg_fail: for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) { if (adc_bat->channel[chan]) iio_channel_release(adc_bat->channel[chan]); } second_mem_fail: - kfree(psy->properties); + kfree(psy_desc->properties); first_mem_fail: return ret; } @@ -360,7 +366,7 @@ static int gab_remove(struct platform_device *pdev) struct gab *adc_bat = platform_get_drvdata(pdev); struct gab_platform_data *pdata = adc_bat->pdata; - power_supply_unregister(&adc_bat->psy); + power_supply_unregister(adc_bat->psy); if (gpio_is_valid(pdata->gpio_charge_finished)) { free_irq(gpio_to_irq(pdata->gpio_charge_finished), adc_bat); @@ -372,7 +378,7 @@ static int gab_remove(struct platform_device *pdev) iio_channel_release(adc_bat->channel[chan]); } - kfree(adc_bat->psy.properties); + kfree(adc_bat->psy_desc.properties); cancel_delayed_work(&adc_bat->bat_work); return 0; } diff --git a/drivers/power/goldfish_battery.c b/drivers/power/goldfish_battery.c index 29eba88..a50bb98 100644 --- a/drivers/power/goldfish_battery.c +++ b/drivers/power/goldfish_battery.c @@ -30,8 +30,8 @@ struct goldfish_battery_data { int irq; spinlock_t lock; - struct power_supply battery; - struct power_supply ac; + struct power_supply *battery; + struct power_supply *ac; }; #define GOLDFISH_BATTERY_READ(data, addr) \ @@ -67,8 +67,7 @@ static int goldfish_ac_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct goldfish_battery_data *data = container_of(psy, - struct goldfish_battery_data, ac); + struct goldfish_battery_data *data = power_supply_get_drvdata(psy); int ret = 0; switch (psp) { @@ -86,8 +85,7 @@ static int goldfish_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct goldfish_battery_data *data = container_of(psy, - struct goldfish_battery_data, battery); + struct goldfish_battery_data *data = power_supply_get_drvdata(psy); int ret = 0; switch (psp) { @@ -139,20 +137,36 @@ static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id) status &= BATTERY_INT_MASK; if (status & BATTERY_STATUS_CHANGED) - power_supply_changed(&data->battery); + power_supply_changed(data->battery); if (status & AC_STATUS_CHANGED) - power_supply_changed(&data->ac); + power_supply_changed(data->ac); spin_unlock_irqrestore(&data->lock, irq_flags); return status ? IRQ_HANDLED : IRQ_NONE; } +static const struct power_supply_desc battery_desc = { + .properties = goldfish_battery_props, + .num_properties = ARRAY_SIZE(goldfish_battery_props), + .get_property = goldfish_battery_get_property, + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, +}; + +static const struct power_supply_desc ac_desc = { + .properties = goldfish_ac_props, + .num_properties = ARRAY_SIZE(goldfish_ac_props), + .get_property = goldfish_ac_get_property, + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, +}; static int goldfish_battery_probe(struct platform_device *pdev) { int ret; struct resource *r; struct goldfish_battery_data *data; + struct power_supply_config psy_cfg = {}; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (data == NULL) @@ -160,18 +174,6 @@ static int goldfish_battery_probe(struct platform_device *pdev) spin_lock_init(&data->lock); - data->battery.properties = goldfish_battery_props; - data->battery.num_properties = ARRAY_SIZE(goldfish_battery_props); - data->battery.get_property = goldfish_battery_get_property; - data->battery.name = "battery"; - data->battery.type = POWER_SUPPLY_TYPE_BATTERY; - - data->ac.properties = goldfish_ac_props; - data->ac.num_properties = ARRAY_SIZE(goldfish_ac_props); - data->ac.get_property = goldfish_ac_get_property; - data->ac.name = "ac"; - data->ac.type = POWER_SUPPLY_TYPE_MAINS; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { dev_err(&pdev->dev, "platform_get_resource failed\n"); @@ -195,14 +197,17 @@ static int goldfish_battery_probe(struct platform_device *pdev) if (ret) return ret; - ret = power_supply_register(&pdev->dev, &data->ac); - if (ret) - return ret; + psy_cfg.drv_data = data; - ret = power_supply_register(&pdev->dev, &data->battery); - if (ret) { - power_supply_unregister(&data->ac); - return ret; + data->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg); + if (IS_ERR(data->ac)) + return PTR_ERR(data->ac); + + data->battery = power_supply_register(&pdev->dev, &battery_desc, + &psy_cfg); + if (IS_ERR(data->battery)) { + power_supply_unregister(data->ac); + return PTR_ERR(data->battery); } platform_set_drvdata(pdev, data); @@ -216,8 +221,8 @@ static int goldfish_battery_remove(struct platform_device *pdev) { struct goldfish_battery_data *data = platform_get_drvdata(pdev); - power_supply_unregister(&data->battery); - power_supply_unregister(&data->ac); + power_supply_unregister(data->battery); + power_supply_unregister(data->ac); battery_data = NULL; return 0; } diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c index b7424c8..c5869b1 100644 --- a/drivers/power/gpio-charger.c +++ b/drivers/power/gpio-charger.c @@ -32,7 +32,8 @@ struct gpio_charger { unsigned int irq; bool wakeup_enabled; - struct power_supply charger; + struct power_supply *charger; + struct power_supply_desc charger_desc; }; static irqreturn_t gpio_charger_irq(int irq, void *devid) @@ -46,7 +47,7 @@ static irqreturn_t gpio_charger_irq(int irq, void *devid) static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy) { - return container_of(psy, struct gpio_charger, charger); + return power_supply_get_drvdata(psy); } static int gpio_charger_get_property(struct power_supply *psy, @@ -127,8 +128,9 @@ struct gpio_charger_platform_data *gpio_charger_parse_dt(struct device *dev) static int gpio_charger_probe(struct platform_device *pdev) { const struct gpio_charger_platform_data *pdata = pdev->dev.platform_data; + struct power_supply_config psy_cfg = {}; struct gpio_charger *gpio_charger; - struct power_supply *charger; + struct power_supply_desc *charger_desc; int ret; int irq; @@ -154,16 +156,18 @@ static int gpio_charger_probe(struct platform_device *pdev) return -ENOMEM; } - charger = &gpio_charger->charger; + charger_desc = &gpio_charger->charger_desc; + + charger_desc->name = pdata->name ? pdata->name : "gpio-charger"; + charger_desc->type = pdata->type; + charger_desc->properties = gpio_charger_properties; + charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties); + charger_desc->get_property = gpio_charger_get_property; - charger->name = pdata->name ? pdata->name : "gpio-charger"; - charger->type = pdata->type; - charger->properties = gpio_charger_properties; - charger->num_properties = ARRAY_SIZE(gpio_charger_properties); - charger->get_property = gpio_charger_get_property; - charger->supplied_to = pdata->supplied_to; - charger->num_supplicants = pdata->num_supplicants; - charger->of_node = pdev->dev.of_node; + psy_cfg.supplied_to = pdata->supplied_to; + psy_cfg.num_supplicants = pdata->num_supplicants; + psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.drv_data = gpio_charger; ret = gpio_request(pdata->gpio, dev_name(&pdev->dev)); if (ret) { @@ -178,8 +182,10 @@ static int gpio_charger_probe(struct platform_device *pdev) gpio_charger->pdata = pdata; - ret = power_supply_register(&pdev->dev, charger); - if (ret < 0) { + gpio_charger->charger = power_supply_register(&pdev->dev, + charger_desc, &psy_cfg); + if (IS_ERR(gpio_charger->charger)) { + ret = PTR_ERR(gpio_charger->charger); dev_err(&pdev->dev, "Failed to register power supply: %d\n", ret); goto err_gpio_free; @@ -189,7 +195,7 @@ static int gpio_charger_probe(struct platform_device *pdev) if (irq > 0) { ret = request_any_context_irq(irq, gpio_charger_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - dev_name(&pdev->dev), charger); + dev_name(&pdev->dev), gpio_charger->charger); if (ret < 0) dev_warn(&pdev->dev, "Failed to request irq: %d\n", ret); else @@ -213,9 +219,9 @@ static int gpio_charger_remove(struct platform_device *pdev) struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); if (gpio_charger->irq) - free_irq(gpio_charger->irq, &gpio_charger->charger); + free_irq(gpio_charger->irq, gpio_charger->charger); - power_supply_unregister(&gpio_charger->charger); + power_supply_unregister(gpio_charger->charger); gpio_free(gpio_charger->pdata->gpio); @@ -241,7 +247,7 @@ static int gpio_charger_resume(struct device *dev) if (device_may_wakeup(dev) && gpio_charger->wakeup_enabled) disable_irq_wake(gpio_charger->irq); - power_supply_changed(&gpio_charger->charger); + power_supply_changed(gpio_charger->charger); return 0; } diff --git a/drivers/power/intel_mid_battery.c b/drivers/power/intel_mid_battery.c index de3f39e..9fa4acc 100644 --- a/drivers/power/intel_mid_battery.c +++ b/drivers/power/intel_mid_battery.c @@ -107,8 +107,8 @@ struct pmic_power_module_info { unsigned int batt_prev_charge_full; /* in mAS */ unsigned int batt_charge_rate; /* in units per second */ - struct power_supply usb; - struct power_supply batt; + struct power_supply *usb; + struct power_supply *batt; int irq; /* GPE_ID or IRQ# */ struct workqueue_struct *monitor_wqueue; struct delayed_work monitor_battery; @@ -404,8 +404,7 @@ static int pmic_usb_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct pmic_power_module_info *pbi = container_of(psy, - struct pmic_power_module_info, usb); + struct pmic_power_module_info *pbi = power_supply_get_drvdata(psy); /* update pmic_power_module_info members */ pmic_battery_read_status(pbi); @@ -444,8 +443,7 @@ static int pmic_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct pmic_power_module_info *pbi = container_of(psy, - struct pmic_power_module_info, batt); + struct pmic_power_module_info *pbi = power_supply_get_drvdata(psy); /* update pmic_power_module_info members */ pmic_battery_read_status(pbi); @@ -640,6 +638,25 @@ static void pmic_battery_handle_intrpt(struct work_struct *work) __func__); } +/* + * Description of power supplies + */ +static const struct power_supply_desc pmic_usb_desc = { + .name = "pmic-usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = pmic_usb_props, + .num_properties = ARRAY_SIZE(pmic_usb_props), + .get_property = pmic_usb_get_property, +}; + +static const struct power_supply_desc pmic_batt_desc = { + .name = "pmic-batt", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = pmic_battery_props, + .num_properties = ARRAY_SIZE(pmic_battery_props), + .get_property = pmic_battery_get_property, +}; + /** * pmic_battery_probe - pmic battery initialize * @irq: pmic battery device irq @@ -653,6 +670,7 @@ static int probe(int irq, struct device *dev) { int retval = 0; struct pmic_power_module_info *pbi; + struct power_supply_config psy_cfg = {}; dev_dbg(dev, "pmic-battery: found pmic battery device\n"); @@ -666,6 +684,7 @@ static int probe(int irq, struct device *dev) pbi->dev = dev; pbi->irq = irq; dev_set_drvdata(dev, pbi); + psy_cfg.drv_data = pbi; /* initialize all required framework before enabling interrupts */ INIT_WORK(&pbi->handler, pmic_battery_handle_intrpt); @@ -687,16 +706,12 @@ static int probe(int irq, struct device *dev) } /* register pmic-batt with power supply subsystem */ - pbi->batt.name = "pmic-batt"; - pbi->batt.type = POWER_SUPPLY_TYPE_BATTERY; - pbi->batt.properties = pmic_battery_props; - pbi->batt.num_properties = ARRAY_SIZE(pmic_battery_props); - pbi->batt.get_property = pmic_battery_get_property; - retval = power_supply_register(dev, &pbi->batt); - if (retval) { + pbi->batt = power_supply_register(dev, &pmic_usb_desc, &psy_cfg); + if (IS_ERR(pbi->batt)) { dev_err(dev, "%s(): failed to register pmic battery device with power supply subsystem\n", __func__); + retval = PTR_ERR(pbi->batt); goto power_reg_failed; } @@ -707,16 +722,12 @@ static int probe(int irq, struct device *dev) queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 1); /* register pmic-usb with power supply subsystem */ - pbi->usb.name = "pmic-usb"; - pbi->usb.type = POWER_SUPPLY_TYPE_USB; - pbi->usb.properties = pmic_usb_props; - pbi->usb.num_properties = ARRAY_SIZE(pmic_usb_props); - pbi->usb.get_property = pmic_usb_get_property; - retval = power_supply_register(dev, &pbi->usb); - if (retval) { + pbi->usb = power_supply_register(dev, &pmic_batt_desc, &psy_cfg); + if (IS_ERR(pbi->usb)) { dev_err(dev, "%s(): failed to register pmic usb device with power supply subsystem\n", __func__); + retval = PTR_ERR(pbi->usb); goto power_reg_failed_1; } @@ -728,7 +739,7 @@ static int probe(int irq, struct device *dev) return retval; power_reg_failed_1: - power_supply_unregister(&pbi->batt); + power_supply_unregister(pbi->batt); power_reg_failed: cancel_delayed_work_sync(&pbi->monitor_battery); requestirq_failed: @@ -762,8 +773,8 @@ static int platform_pmic_battery_remove(struct platform_device *pdev) cancel_delayed_work_sync(&pbi->monitor_battery); destroy_workqueue(pbi->monitor_wqueue); - power_supply_unregister(&pbi->usb); - power_supply_unregister(&pbi->batt); + power_supply_unregister(pbi->usb); + power_supply_unregister(pbi->batt); cancel_work_sync(&pbi->handler); kfree(pbi); diff --git a/drivers/power/ipaq_micro_battery.c b/drivers/power/ipaq_micro_battery.c index 9d69460..f03014e 100644 --- a/drivers/power/ipaq_micro_battery.c +++ b/drivers/power/ipaq_micro_battery.c @@ -93,7 +93,7 @@ static void micro_battery_work(struct work_struct *work) static int get_capacity(struct power_supply *b) { - struct micro_battery *mb = dev_get_drvdata(b->dev->parent); + struct micro_battery *mb = dev_get_drvdata(b->dev.parent); switch (mb->flag & 0x07) { case MICRO_BATT_STATUS_HIGH: @@ -113,7 +113,7 @@ static int get_capacity(struct power_supply *b) static int get_status(struct power_supply *b) { - struct micro_battery *mb = dev_get_drvdata(b->dev->parent); + struct micro_battery *mb = dev_get_drvdata(b->dev.parent); if (mb->flag == MICRO_BATT_STATUS_UNKNOWN) return POWER_SUPPLY_STATUS_UNKNOWN; @@ -132,7 +132,7 @@ static int micro_batt_get_property(struct power_supply *b, enum power_supply_property psp, union power_supply_propval *val) { - struct micro_battery *mb = dev_get_drvdata(b->dev->parent); + struct micro_battery *mb = dev_get_drvdata(b->dev.parent); switch (psp) { case POWER_SUPPLY_PROP_TECHNOLOGY: @@ -180,7 +180,7 @@ static int micro_ac_get_property(struct power_supply *b, enum power_supply_property psp, union power_supply_propval *val) { - struct micro_battery *mb = dev_get_drvdata(b->dev->parent); + struct micro_battery *mb = dev_get_drvdata(b->dev.parent); switch (psp) { case POWER_SUPPLY_PROP_ONLINE: @@ -202,7 +202,7 @@ static enum power_supply_property micro_batt_power_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, }; -static struct power_supply micro_batt_power = { +static const struct power_supply_desc micro_batt_power_desc = { .name = "main-battery", .type = POWER_SUPPLY_TYPE_BATTERY, .properties = micro_batt_power_props, @@ -215,7 +215,7 @@ static enum power_supply_property micro_ac_power_props[] = { POWER_SUPPLY_PROP_ONLINE, }; -static struct power_supply micro_ac_power = { +static const struct power_supply_desc micro_ac_power_desc = { .name = "ac", .type = POWER_SUPPLY_TYPE_MAINS, .properties = micro_ac_power_props, @@ -223,9 +223,12 @@ static struct power_supply micro_ac_power = { .get_property = micro_ac_get_property, }; +static struct power_supply *micro_batt_power, *micro_ac_power; + static int micro_batt_probe(struct platform_device *pdev) { struct micro_battery *mb; + int ret; mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL); if (!mb) @@ -233,14 +236,36 @@ static int micro_batt_probe(struct platform_device *pdev) mb->micro = dev_get_drvdata(pdev->dev.parent); mb->wq = create_singlethread_workqueue("ipaq-battery-wq"); + if (!mb->wq) + return -ENOMEM; + INIT_DELAYED_WORK(&mb->update, micro_battery_work); platform_set_drvdata(pdev, mb); queue_delayed_work(mb->wq, &mb->update, 1); - power_supply_register(&pdev->dev, µ_batt_power); - power_supply_register(&pdev->dev, µ_ac_power); + + micro_batt_power = power_supply_register(&pdev->dev, + µ_batt_power_desc, NULL); + if (IS_ERR(micro_batt_power)) { + ret = PTR_ERR(micro_batt_power); + goto batt_err; + } + + micro_ac_power = power_supply_register(&pdev->dev, + µ_ac_power_desc, NULL); + if (IS_ERR(micro_ac_power)) { + ret = PTR_ERR(micro_ac_power); + goto ac_err; + } dev_info(&pdev->dev, "iPAQ micro battery driver\n"); return 0; + +ac_err: + power_supply_unregister(micro_ac_power); +batt_err: + cancel_delayed_work_sync(&mb->update); + destroy_workqueue(mb->wq); + return ret; } static int micro_batt_remove(struct platform_device *pdev) @@ -248,9 +273,10 @@ static int micro_batt_remove(struct platform_device *pdev) { struct micro_battery *mb = platform_get_drvdata(pdev); - power_supply_unregister(µ_ac_power); - power_supply_unregister(µ_batt_power); + power_supply_unregister(micro_ac_power); + power_supply_unregister(micro_batt_power); cancel_delayed_work_sync(&mb->update); + destroy_workqueue(mb->wq); return 0; } diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index 0b4cf9d..f2a7d97 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -57,11 +57,12 @@ static u16 isp170x_id[] = { }; struct isp1704_charger { - struct device *dev; - struct power_supply psy; - struct usb_phy *phy; - struct notifier_block nb; - struct work_struct work; + struct device *dev; + struct power_supply *psy; + struct power_supply_desc psy_desc; + struct usb_phy *phy; + struct notifier_block nb; + struct work_struct work; /* properties */ char model[8]; @@ -259,10 +260,10 @@ static void isp1704_charger_work(struct work_struct *data) /* detect wall charger */ if (isp1704_charger_detect_dcp(isp)) { - isp->psy.type = POWER_SUPPLY_TYPE_USB_DCP; + isp->psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP; isp->current_max = 1800; } else { - isp->psy.type = POWER_SUPPLY_TYPE_USB; + isp->psy_desc.type = POWER_SUPPLY_TYPE_USB; isp->current_max = 500; } @@ -271,7 +272,7 @@ static void isp1704_charger_work(struct work_struct *data) usb_gadget_connect(isp->phy->otg->gadget); } - if (isp->psy.type != POWER_SUPPLY_TYPE_USB_DCP) { + if (isp->psy_desc.type != POWER_SUPPLY_TYPE_USB_DCP) { /* * Only 500mA here or high speed chirp * handshaking may break @@ -280,14 +281,14 @@ static void isp1704_charger_work(struct work_struct *data) isp->current_max = 500; if (isp->current_max > 100) - isp->psy.type = POWER_SUPPLY_TYPE_USB_CDP; + isp->psy_desc.type = POWER_SUPPLY_TYPE_USB_CDP; } break; case USB_EVENT_NONE: isp->online = false; isp->present = 0; isp->current_max = 0; - isp->psy.type = POWER_SUPPLY_TYPE_USB; + isp->psy_desc.type = POWER_SUPPLY_TYPE_USB; /* * Disable data pullups. We need to prevent the controller from @@ -306,7 +307,7 @@ static void isp1704_charger_work(struct work_struct *data) goto out; } - power_supply_changed(&isp->psy); + power_supply_changed(isp->psy); out: mutex_unlock(&lock); } @@ -326,8 +327,7 @@ static int isp1704_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct isp1704_charger *isp = - container_of(psy, struct isp1704_charger, psy); + struct isp1704_charger *isp = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_PRESENT: @@ -403,6 +403,7 @@ static int isp1704_charger_probe(struct platform_device *pdev) { struct isp1704_charger *isp; int ret = -ENODEV; + struct power_supply_config psy_cfg = {}; struct isp1704_charger_data *pdata = dev_get_platdata(&pdev->dev); struct device_node *np = pdev->dev.of_node; @@ -454,15 +455,19 @@ static int isp1704_charger_probe(struct platform_device *pdev) if (ret < 0) goto fail1; - isp->psy.name = "isp1704"; - isp->psy.type = POWER_SUPPLY_TYPE_USB; - isp->psy.properties = power_props; - isp->psy.num_properties = ARRAY_SIZE(power_props); - isp->psy.get_property = isp1704_charger_get_property; + isp->psy_desc.name = "isp1704"; + isp->psy_desc.type = POWER_SUPPLY_TYPE_USB; + isp->psy_desc.properties = power_props; + isp->psy_desc.num_properties = ARRAY_SIZE(power_props); + isp->psy_desc.get_property = isp1704_charger_get_property; - ret = power_supply_register(isp->dev, &isp->psy); - if (ret) + psy_cfg.drv_data = isp; + + isp->psy = power_supply_register(isp->dev, &isp->psy_desc, &psy_cfg); + if (IS_ERR(isp->psy)) { + ret = PTR_ERR(isp->psy); goto fail1; + } /* * REVISIT: using work in order to allow the usb notifications to be @@ -498,7 +503,7 @@ static int isp1704_charger_probe(struct platform_device *pdev) return 0; fail2: - power_supply_unregister(&isp->psy); + power_supply_unregister(isp->psy); fail1: isp1704_charger_set_power(isp, 0); fail0: @@ -512,7 +517,7 @@ static int isp1704_charger_remove(struct platform_device *pdev) struct isp1704_charger *isp = platform_get_drvdata(pdev); usb_unregister_notifier(isp->phy, &isp->nb); - power_supply_unregister(&isp->psy); + power_supply_unregister(isp->psy); isp1704_charger_set_power(isp, 0); return 0; diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c index 9cd391d..abdfc21 100644 --- a/drivers/power/jz4740-battery.c +++ b/drivers/power/jz4740-battery.c @@ -46,7 +46,8 @@ struct jz_battery { struct completion read_completion; - struct power_supply battery; + struct power_supply *battery; + struct power_supply_desc battery_desc; struct delayed_work work; struct mutex lock; @@ -54,7 +55,7 @@ struct jz_battery { static inline struct jz_battery *psy_to_jz_battery(struct power_supply *psy) { - return container_of(psy, struct jz_battery, battery); + return power_supply_get_drvdata(psy); } static irqreturn_t jz_battery_irq_handler(int irq, void *devid) @@ -213,7 +214,7 @@ static void jz_battery_update(struct jz_battery *jz_battery) } if (has_changed) - power_supply_changed(&jz_battery->battery); + power_supply_changed(jz_battery->battery); } static enum power_supply_property jz_battery_properties[] = { @@ -242,8 +243,9 @@ static int jz_battery_probe(struct platform_device *pdev) { int ret = 0; struct jz_battery_platform_data *pdata = pdev->dev.parent->platform_data; + struct power_supply_config psy_cfg = {}; struct jz_battery *jz_battery; - struct power_supply *battery; + struct power_supply_desc *battery_desc; struct resource *mem; if (!pdata) { @@ -271,14 +273,17 @@ static int jz_battery_probe(struct platform_device *pdev) if (IS_ERR(jz_battery->base)) return PTR_ERR(jz_battery->base); - battery = &jz_battery->battery; - battery->name = pdata->info.name; - battery->type = POWER_SUPPLY_TYPE_BATTERY; - battery->properties = jz_battery_properties; - battery->num_properties = ARRAY_SIZE(jz_battery_properties); - battery->get_property = jz_battery_get_property; - battery->external_power_changed = jz_battery_external_power_changed; - battery->use_for_apm = 1; + battery_desc = &jz_battery->battery_desc; + battery_desc->name = pdata->info.name; + battery_desc->type = POWER_SUPPLY_TYPE_BATTERY; + battery_desc->properties = jz_battery_properties; + battery_desc->num_properties = ARRAY_SIZE(jz_battery_properties); + battery_desc->get_property = jz_battery_get_property; + battery_desc->external_power_changed = + jz_battery_external_power_changed; + battery_desc->use_for_apm = 1; + + psy_cfg.drv_data = jz_battery; jz_battery->pdata = pdata; jz_battery->pdev = pdev; @@ -330,9 +335,11 @@ static int jz_battery_probe(struct platform_device *pdev) else jz4740_adc_set_config(pdev->dev.parent, JZ_ADC_CONFIG_BAT_MB, 0); - ret = power_supply_register(&pdev->dev, &jz_battery->battery); - if (ret) { + jz_battery->battery = power_supply_register(&pdev->dev, battery_desc, + &psy_cfg); + if (IS_ERR(jz_battery->battery)) { dev_err(&pdev->dev, "power supply battery register failed.\n"); + ret = PTR_ERR(jz_battery->battery); goto err_free_charge_irq; } @@ -364,7 +371,7 @@ static int jz_battery_remove(struct platform_device *pdev) gpio_free(jz_battery->pdata->gpio_charge); } - power_supply_unregister(&jz_battery->battery); + power_supply_unregister(jz_battery->battery); free_irq(jz_battery->irq, jz_battery); diff --git a/drivers/power/lp8727_charger.c b/drivers/power/lp8727_charger.c index 32de636..7e741f1 100644 --- a/drivers/power/lp8727_charger.c +++ b/drivers/power/lp8727_charger.c @@ -80,9 +80,9 @@ enum lp8727_die_temp { }; struct lp8727_psy { - struct power_supply ac; - struct power_supply usb; - struct power_supply batt; + struct power_supply *ac; + struct power_supply *usb; + struct power_supply *batt; }; struct lp8727_chg { @@ -242,9 +242,9 @@ static void lp8727_delayed_func(struct work_struct *_work) lp8727_id_detection(pchg, idno, vbus); lp8727_enable_chgdet(pchg); - power_supply_changed(&pchg->psy->ac); - power_supply_changed(&pchg->psy->usb); - power_supply_changed(&pchg->psy->batt); + power_supply_changed(pchg->psy->ac); + power_supply_changed(pchg->psy->usb); + power_supply_changed(pchg->psy->batt); } static irqreturn_t lp8727_isr_func(int irq, void *ptr) @@ -311,12 +311,12 @@ static int lp8727_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent); + struct lp8727_chg *pchg = dev_get_drvdata(psy->dev.parent); if (psp != POWER_SUPPLY_PROP_ONLINE) return -EINVAL; - val->intval = lp8727_is_charger_attached(psy->name, pchg->devid); + val->intval = lp8727_is_charger_attached(psy->desc->name, pchg->devid); return 0; } @@ -337,14 +337,14 @@ static int lp8727_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent); + struct lp8727_chg *pchg = dev_get_drvdata(psy->dev.parent); struct lp8727_platform_data *pdata = pchg->pdata; enum lp8727_die_temp temp; u8 read; switch (psp) { case POWER_SUPPLY_PROP_STATUS: - if (!lp8727_is_charger_attached(psy->name, pchg->devid)) { + if (!lp8727_is_charger_attached(psy->desc->name, pchg->devid)) { val->intval = POWER_SUPPLY_STATUS_DISCHARGING; return 0; } @@ -400,13 +400,13 @@ static int lp8727_battery_get_property(struct power_supply *psy, static void lp8727_charger_changed(struct power_supply *psy) { - struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent); + struct lp8727_chg *pchg = dev_get_drvdata(psy->dev.parent); u8 eoc_level; u8 ichg; u8 val; /* skip if no charger exists */ - if (!lp8727_is_charger_attached(psy->name, pchg->devid)) + if (!lp8727_is_charger_attached(psy->desc->name, pchg->devid)) return; /* update charging parameters */ @@ -418,8 +418,34 @@ static void lp8727_charger_changed(struct power_supply *psy) } } +static const struct power_supply_desc lp8727_ac_desc = { + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = lp8727_charger_prop, + .num_properties = ARRAY_SIZE(lp8727_charger_prop), + .get_property = lp8727_charger_get_property, +}; + +static const struct power_supply_desc lp8727_usb_desc = { + .name = "usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = lp8727_charger_prop, + .num_properties = ARRAY_SIZE(lp8727_charger_prop), + .get_property = lp8727_charger_get_property, +}; + +static const struct power_supply_desc lp8727_batt_desc = { + .name = "main_batt", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = lp8727_battery_prop, + .num_properties = ARRAY_SIZE(lp8727_battery_prop), + .get_property = lp8727_battery_get_property, + .external_power_changed = lp8727_charger_changed, +}; + static int lp8727_register_psy(struct lp8727_chg *pchg) { + struct power_supply_config psy_cfg = {}; /* Only for ac and usb */ struct lp8727_psy *psy; psy = devm_kzalloc(pchg->dev, sizeof(*psy), GFP_KERNEL); @@ -428,44 +454,28 @@ static int lp8727_register_psy(struct lp8727_chg *pchg) pchg->psy = psy; - psy->ac.name = "ac"; - psy->ac.type = POWER_SUPPLY_TYPE_MAINS; - psy->ac.properties = lp8727_charger_prop; - psy->ac.num_properties = ARRAY_SIZE(lp8727_charger_prop); - psy->ac.get_property = lp8727_charger_get_property; - psy->ac.supplied_to = battery_supplied_to; - psy->ac.num_supplicants = ARRAY_SIZE(battery_supplied_to); + psy_cfg.supplied_to = battery_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(battery_supplied_to); - if (power_supply_register(pchg->dev, &psy->ac)) + psy->ac = power_supply_register(pchg->dev, &lp8727_ac_desc, &psy_cfg); + if (IS_ERR(psy->ac)) goto err_psy_ac; - psy->usb.name = "usb"; - psy->usb.type = POWER_SUPPLY_TYPE_USB; - psy->usb.properties = lp8727_charger_prop; - psy->usb.num_properties = ARRAY_SIZE(lp8727_charger_prop); - psy->usb.get_property = lp8727_charger_get_property; - psy->usb.supplied_to = battery_supplied_to; - psy->usb.num_supplicants = ARRAY_SIZE(battery_supplied_to); - - if (power_supply_register(pchg->dev, &psy->usb)) + psy->usb = power_supply_register(pchg->dev, &lp8727_usb_desc, + &psy_cfg); + if (IS_ERR(psy->usb)) goto err_psy_usb; - psy->batt.name = "main_batt"; - psy->batt.type = POWER_SUPPLY_TYPE_BATTERY; - psy->batt.properties = lp8727_battery_prop; - psy->batt.num_properties = ARRAY_SIZE(lp8727_battery_prop); - psy->batt.get_property = lp8727_battery_get_property; - psy->batt.external_power_changed = lp8727_charger_changed; - - if (power_supply_register(pchg->dev, &psy->batt)) + psy->batt = power_supply_register(pchg->dev, &lp8727_batt_desc, NULL); + if (IS_ERR(psy->batt)) goto err_psy_batt; return 0; err_psy_batt: - power_supply_unregister(&psy->usb); + power_supply_unregister(psy->usb); err_psy_usb: - power_supply_unregister(&psy->ac); + power_supply_unregister(psy->ac); err_psy_ac: return -EPERM; } @@ -477,9 +487,9 @@ static void lp8727_unregister_psy(struct lp8727_chg *pchg) if (!psy) return; - power_supply_unregister(&psy->ac); - power_supply_unregister(&psy->usb); - power_supply_unregister(&psy->batt); + power_supply_unregister(psy->ac); + power_supply_unregister(psy->usb); + power_supply_unregister(psy->batt); } #ifdef CONFIG_OF diff --git a/drivers/power/lp8788-charger.c b/drivers/power/lp8788-charger.c index 21fc233..f5a48fd 100644 --- a/drivers/power/lp8788-charger.c +++ b/drivers/power/lp8788-charger.c @@ -105,8 +105,8 @@ struct lp8788_chg_irq { */ struct lp8788_charger { struct lp8788 *lp; - struct power_supply charger; - struct power_supply battery; + struct power_supply *charger; + struct power_supply *battery; struct work_struct charger_work; struct iio_channel *chan[LP8788_NUM_CHG_ADC]; struct lp8788_chg_irq irqs[LP8788_MAX_CHG_IRQS]; @@ -148,7 +148,7 @@ static int lp8788_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct lp8788_charger *pchg = dev_get_drvdata(psy->dev->parent); + struct lp8788_charger *pchg = dev_get_drvdata(psy->dev.parent); u8 read; switch (psp) { @@ -337,7 +337,7 @@ static int lp8788_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct lp8788_charger *pchg = dev_get_drvdata(psy->dev->parent); + struct lp8788_charger *pchg = dev_get_drvdata(psy->dev.parent); switch (psp) { case POWER_SUPPLY_PROP_STATUS: @@ -397,36 +397,50 @@ static int lp8788_update_charger_params(struct platform_device *pdev, return 0; } +static const struct power_supply_desc lp8788_psy_charger_desc = { + .name = LP8788_CHARGER_NAME, + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = lp8788_charger_prop, + .num_properties = ARRAY_SIZE(lp8788_charger_prop), + .get_property = lp8788_charger_get_property, +}; + +static const struct power_supply_desc lp8788_psy_battery_desc = { + .name = LP8788_BATTERY_NAME, + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = lp8788_battery_prop, + .num_properties = ARRAY_SIZE(lp8788_battery_prop), + .get_property = lp8788_battery_get_property, +}; + static int lp8788_psy_register(struct platform_device *pdev, struct lp8788_charger *pchg) { - pchg->charger.name = LP8788_CHARGER_NAME; - pchg->charger.type = POWER_SUPPLY_TYPE_MAINS; - pchg->charger.properties = lp8788_charger_prop; - pchg->charger.num_properties = ARRAY_SIZE(lp8788_charger_prop); - pchg->charger.get_property = lp8788_charger_get_property; - pchg->charger.supplied_to = battery_supplied_to; - pchg->charger.num_supplicants = ARRAY_SIZE(battery_supplied_to); - - if (power_supply_register(&pdev->dev, &pchg->charger)) - return -EPERM; + struct power_supply_config charger_cfg = {}; + + charger_cfg.supplied_to = battery_supplied_to; + charger_cfg.num_supplicants = ARRAY_SIZE(battery_supplied_to); - pchg->battery.name = LP8788_BATTERY_NAME; - pchg->battery.type = POWER_SUPPLY_TYPE_BATTERY; - pchg->battery.properties = lp8788_battery_prop; - pchg->battery.num_properties = ARRAY_SIZE(lp8788_battery_prop); - pchg->battery.get_property = lp8788_battery_get_property; + pchg->charger = power_supply_register(&pdev->dev, + &lp8788_psy_charger_desc, + &charger_cfg); + if (IS_ERR(pchg->charger)) + return -EPERM; - if (power_supply_register(&pdev->dev, &pchg->battery)) + pchg->battery = power_supply_register(&pdev->dev, + &lp8788_psy_battery_desc, NULL); + if (IS_ERR(pchg->battery)) { + power_supply_unregister(pchg->charger); return -EPERM; + } return 0; } static void lp8788_psy_unregister(struct lp8788_charger *pchg) { - power_supply_unregister(&pchg->battery); - power_supply_unregister(&pchg->charger); + power_supply_unregister(pchg->battery); + power_supply_unregister(pchg->charger); } static void lp8788_charger_event(struct work_struct *work) @@ -470,8 +484,8 @@ static irqreturn_t lp8788_charger_irq_thread(int virq, void *ptr) case LP8788_INT_EOC: case LP8788_INT_BATT_LOW: case LP8788_INT_NO_BATT: - power_supply_changed(&pchg->charger); - power_supply_changed(&pchg->battery); + power_supply_changed(pchg->charger); + power_supply_changed(pchg->battery); break; default: break; diff --git a/drivers/power/ltc2941-battery-gauge.c b/drivers/power/ltc2941-battery-gauge.c index e31c927..daeb086 100644 --- a/drivers/power/ltc2941-battery-gauge.c +++ b/drivers/power/ltc2941-battery-gauge.c @@ -59,7 +59,8 @@ enum ltc294x_reg { struct ltc294x_info { struct i2c_client *client; /* I2C Client pointer */ - struct power_supply supply; /* Supply pointer */ + struct power_supply *supply; /* Supply pointer */ + struct power_supply_desc supply_desc; /* Supply description */ struct delayed_work work; /* Work scheduler */ int num_regs; /* Number of registers (chip type) */ int id; /* Identifier of ltc294x chip */ @@ -294,8 +295,7 @@ static int ltc294x_get_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) { - struct ltc294x_info *info = - container_of(psy, struct ltc294x_info, supply); + struct ltc294x_info *info = power_supply_get_drvdata(psy); switch (prop) { case POWER_SUPPLY_PROP_CHARGE_NOW: @@ -317,8 +317,7 @@ static int ltc294x_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { - struct ltc294x_info *info = - container_of(psy, struct ltc294x_info, supply); + struct ltc294x_info *info = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_CHARGE_NOW: @@ -345,7 +344,7 @@ static void ltc294x_update(struct ltc294x_info *info) if (charge != info->charge) { info->charge = charge; - power_supply_changed(&info->supply); + power_supply_changed(info->supply); } } @@ -371,8 +370,8 @@ static int ltc294x_i2c_remove(struct i2c_client *client) struct ltc294x_info *info = i2c_get_clientdata(client); cancel_delayed_work(&info->work); - power_supply_unregister(&info->supply); - kfree(info->supply.name); + power_supply_unregister(info->supply); + kfree(info->supply_desc.name); mutex_lock(<c294x_lock); idr_remove(<c294x_id, info->id); mutex_unlock(<c294x_lock); @@ -382,6 +381,7 @@ static int ltc294x_i2c_remove(struct i2c_client *client) static int ltc294x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct power_supply_config psy_cfg = {}; struct ltc294x_info *info; int ret; int num; @@ -406,8 +406,9 @@ static int ltc294x_i2c_probe(struct i2c_client *client, i2c_set_clientdata(client, info); info->num_regs = id->driver_data; - info->supply.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num); - if (!info->supply.name) { + info->supply_desc.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, + num); + if (!info->supply_desc.name) { ret = -ENOMEM; goto fail_name; } @@ -440,30 +441,32 @@ static int ltc294x_i2c_probe(struct i2c_client *client, } else { if (prescaler_exp > LTC2941_MAX_PRESCALER_EXP) prescaler_exp = LTC2941_MAX_PRESCALER_EXP; - info->Qlsb = ((58 * 50000) / r_sense) / + info->Qlsb = ((85 * 50000) / r_sense) / (128 / (1 << prescaler_exp)); } info->client = client; info->id = num; - info->supply.type = POWER_SUPPLY_TYPE_BATTERY; - info->supply.properties = ltc294x_properties; + info->supply_desc.type = POWER_SUPPLY_TYPE_BATTERY; + info->supply_desc.properties = ltc294x_properties; if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB) - info->supply.num_properties = + info->supply_desc.num_properties = ARRAY_SIZE(ltc294x_properties); else if (info->num_regs >= LTC294X_REG_CURRENT_LSB) - info->supply.num_properties = + info->supply_desc.num_properties = ARRAY_SIZE(ltc294x_properties) - 1; else if (info->num_regs >= LTC294X_REG_VOLTAGE_LSB) - info->supply.num_properties = + info->supply_desc.num_properties = ARRAY_SIZE(ltc294x_properties) - 2; else - info->supply.num_properties = + info->supply_desc.num_properties = ARRAY_SIZE(ltc294x_properties) - 3; - info->supply.get_property = ltc294x_get_property; - info->supply.set_property = ltc294x_set_property; - info->supply.property_is_writeable = ltc294x_property_is_writeable; - info->supply.external_power_changed = NULL; + info->supply_desc.get_property = ltc294x_get_property; + info->supply_desc.set_property = ltc294x_set_property; + info->supply_desc.property_is_writeable = ltc294x_property_is_writeable; + info->supply_desc.external_power_changed = NULL; + + psy_cfg.drv_data = info; INIT_DELAYED_WORK(&info->work, ltc294x_work); @@ -473,9 +476,11 @@ static int ltc294x_i2c_probe(struct i2c_client *client, goto fail_comm; } - ret = power_supply_register(&client->dev, &info->supply); - if (ret) { + info->supply = power_supply_register(&client->dev, &info->supply_desc, + &psy_cfg); + if (IS_ERR(info->supply)) { dev_err(&client->dev, "failed to register ltc2941\n"); + ret = PTR_ERR(info->supply); goto fail_register; } else { schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ); @@ -484,7 +489,7 @@ static int ltc294x_i2c_probe(struct i2c_client *client, return 0; fail_register: - kfree(info->supply.name); + kfree(info->supply_desc.name); fail_comm: fail_name: fail_info: diff --git a/drivers/power/max14577_charger.c b/drivers/power/max14577_charger.c index ef4103e..a36bcaf 100644 --- a/drivers/power/max14577_charger.c +++ b/drivers/power/max14577_charger.c @@ -22,12 +22,9 @@ #include <linux/mfd/max14577.h> struct max14577_charger { - struct device *dev; - struct max14577 *max14577; - struct power_supply charger; - - unsigned int charging_state; - unsigned int battery_state; + struct device *dev; + struct max14577 *max14577; + struct power_supply *charger; struct max14577_charger_platform_data *pdata; }; @@ -57,10 +54,10 @@ static enum max14577_muic_charger_type maxim_get_charger_type( } } -static int max14577_get_charger_state(struct max14577_charger *chg) +static int max14577_get_charger_state(struct max14577_charger *chg, int *val) { struct regmap *rmap = chg->max14577->regmap; - int state = POWER_SUPPLY_STATUS_DISCHARGING; + int ret; u8 reg_data; /* @@ -74,23 +71,32 @@ static int max14577_get_charger_state(struct max14577_charger *chg) * - handle properly dead-battery charging (respect timer) * - handle timers (fast-charge and prequal) /MBCCHGERR/ */ - max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL2, ®_data); - if ((reg_data & CHGCTRL2_MBCHOSTEN_MASK) == 0) - goto state_set; + ret = max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL2, ®_data); + if (ret < 0) + goto out; + + if ((reg_data & CHGCTRL2_MBCHOSTEN_MASK) == 0) { + *val = POWER_SUPPLY_STATUS_DISCHARGING; + goto out; + } + + ret = max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, ®_data); + if (ret < 0) + goto out; - max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, ®_data); if (reg_data & STATUS3_CGMBC_MASK) { /* Charger or USB-cable is connected */ if (reg_data & STATUS3_EOC_MASK) - state = POWER_SUPPLY_STATUS_FULL; + *val = POWER_SUPPLY_STATUS_FULL; else - state = POWER_SUPPLY_STATUS_CHARGING; - goto state_set; + *val = POWER_SUPPLY_STATUS_CHARGING; + goto out; } -state_set: - chg->charging_state = state; - return state; + *val = POWER_SUPPLY_STATUS_DISCHARGING; + +out: + return ret; } /* @@ -98,8 +104,10 @@ state_set: * - POWER_SUPPLY_CHARGE_TYPE_NONE * - POWER_SUPPLY_CHARGE_TYPE_FAST */ -static int max14577_get_charge_type(struct max14577_charger *chg) +static int max14577_get_charge_type(struct max14577_charger *chg, int *val) { + int ret, charging; + /* * TODO: CHARGE_TYPE_TRICKLE (VCHGR_RC or EOC)? * As spec says: @@ -108,18 +116,29 @@ static int max14577_get_charge_type(struct max14577_charger *chg) * top-off timer starts. The device continues to trickle * charge the battery until the top-off timer runs out." */ - if (max14577_get_charger_state(chg) == POWER_SUPPLY_STATUS_CHARGING) - return POWER_SUPPLY_CHARGE_TYPE_FAST; - return POWER_SUPPLY_CHARGE_TYPE_NONE; + ret = max14577_get_charger_state(chg, &charging); + if (ret < 0) + return ret; + + if (charging == POWER_SUPPLY_STATUS_CHARGING) + *val = POWER_SUPPLY_CHARGE_TYPE_FAST; + else + *val = POWER_SUPPLY_CHARGE_TYPE_NONE; + + return 0; } -static int max14577_get_online(struct max14577_charger *chg) +static int max14577_get_online(struct max14577_charger *chg, int *val) { struct regmap *rmap = chg->max14577->regmap; u8 reg_data; + int ret; enum max14577_muic_charger_type chg_type; - max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, ®_data); + ret = max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, ®_data); + if (ret < 0) + return ret; + reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT); chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data); switch (chg_type) { @@ -129,14 +148,17 @@ static int max14577_get_online(struct max14577_charger *chg) case MAX14577_CHARGER_TYPE_SPECIAL_1A: case MAX14577_CHARGER_TYPE_DEAD_BATTERY: case MAX77836_CHARGER_TYPE_SPECIAL_BIAS: - return 1; + *val = 1; + break; case MAX14577_CHARGER_TYPE_NONE: case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT: case MAX14577_CHARGER_TYPE_RESERVED: case MAX77836_CHARGER_TYPE_RESERVED: default: - return 0; + *val = 0; } + + return 0; } /* @@ -145,30 +167,38 @@ static int max14577_get_online(struct max14577_charger *chg) * - POWER_SUPPLY_HEALTH_OVERVOLTAGE * - POWER_SUPPLY_HEALTH_GOOD */ -static int max14577_get_battery_health(struct max14577_charger *chg) +static int max14577_get_battery_health(struct max14577_charger *chg, int *val) { struct regmap *rmap = chg->max14577->regmap; - int state = POWER_SUPPLY_HEALTH_GOOD; + int ret; u8 reg_data; enum max14577_muic_charger_type chg_type; - max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, ®_data); + ret = max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, ®_data); + if (ret < 0) + goto out; + reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT); chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data); if (chg_type == MAX14577_CHARGER_TYPE_DEAD_BATTERY) { - state = POWER_SUPPLY_HEALTH_DEAD; - goto state_set; + *val = POWER_SUPPLY_HEALTH_DEAD; + goto out; } - max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, ®_data); + ret = max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, ®_data); + if (ret < 0) + goto out; + if (reg_data & STATUS3_OVP_MASK) { - state = POWER_SUPPLY_HEALTH_OVERVOLTAGE; - goto state_set; + *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + goto out; } -state_set: - chg->battery_state = state; - return state; + /* Not dead, not overvoltage */ + *val = POWER_SUPPLY_HEALTH_GOOD; + +out: + return ret; } /* @@ -176,9 +206,11 @@ state_set: * The max14577 chip doesn't report any status of battery presence. * Lets assume that it will always be used with some battery. */ -static int max14577_get_present(struct max14577_charger *chg) +static int max14577_get_present(struct max14577_charger *chg, int *val) { - return 1; + *val = 1; + + return 0; } static int max14577_set_fast_charge_timer(struct max14577_charger *chg, @@ -389,26 +421,24 @@ static int max14577_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct max14577_charger *chg = container_of(psy, - struct max14577_charger, - charger); + struct max14577_charger *chg = power_supply_get_drvdata(psy); int ret = 0; switch (psp) { case POWER_SUPPLY_PROP_STATUS: - val->intval = max14577_get_charger_state(chg); + ret = max14577_get_charger_state(chg, &val->intval); break; case POWER_SUPPLY_PROP_CHARGE_TYPE: - val->intval = max14577_get_charge_type(chg); + ret = max14577_get_charge_type(chg, &val->intval); break; case POWER_SUPPLY_PROP_HEALTH: - val->intval = max14577_get_battery_health(chg); + ret = max14577_get_battery_health(chg, &val->intval); break; case POWER_SUPPLY_PROP_PRESENT: - val->intval = max14577_get_present(chg); + ret = max14577_get_present(chg, &val->intval); break; case POWER_SUPPLY_PROP_ONLINE: - val->intval = max14577_get_online(chg); + ret = max14577_get_online(chg, &val->intval); break; case POWER_SUPPLY_PROP_MODEL_NAME: BUILD_BUG_ON(ARRAY_SIZE(model_names) != MAXIM_DEVICE_TYPE_NUM); @@ -424,6 +454,14 @@ static int max14577_charger_get_property(struct power_supply *psy, return ret; } +static const struct power_supply_desc max14577_charger_desc = { + .name = "max14577-charger", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = max14577_charger_props, + .num_properties = ARRAY_SIZE(max14577_charger_props), + .get_property = max14577_charger_get_property, +}; + #ifdef CONFIG_OF static struct max14577_charger_platform_data *max14577_charger_dt_init( struct platform_device *pdev) @@ -531,6 +569,7 @@ static DEVICE_ATTR(fast_charge_timer, S_IRUGO | S_IWUSR, static int max14577_charger_probe(struct platform_device *pdev) { struct max14577_charger *chg; + struct power_supply_config psy_cfg = {}; struct max14577 *max14577 = dev_get_drvdata(pdev->dev.parent); int ret; @@ -550,21 +589,18 @@ static int max14577_charger_probe(struct platform_device *pdev) if (ret) return ret; - chg->charger.name = "max14577-charger", - chg->charger.type = POWER_SUPPLY_TYPE_BATTERY, - chg->charger.properties = max14577_charger_props, - chg->charger.num_properties = ARRAY_SIZE(max14577_charger_props), - chg->charger.get_property = max14577_charger_get_property, - ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer); if (ret) { dev_err(&pdev->dev, "failed: create sysfs entry\n"); return ret; } - ret = power_supply_register(&pdev->dev, &chg->charger); - if (ret) { + psy_cfg.drv_data = chg; + chg->charger = power_supply_register(&pdev->dev, &max14577_charger_desc, + &psy_cfg); + if (IS_ERR(chg->charger)) { dev_err(&pdev->dev, "failed: power supply register\n"); + ret = PTR_ERR(chg->charger); goto err; } @@ -585,7 +621,7 @@ static int max14577_charger_remove(struct platform_device *pdev) struct max14577_charger *chg = platform_get_drvdata(pdev); device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); - power_supply_unregister(&chg->charger); + power_supply_unregister(chg->charger); return 0; } diff --git a/drivers/power/max17040_battery.c b/drivers/power/max17040_battery.c index 14d4470..8689c80 100644 --- a/drivers/power/max17040_battery.c +++ b/drivers/power/max17040_battery.c @@ -40,7 +40,7 @@ struct max17040_chip { struct i2c_client *client; struct delayed_work work; - struct power_supply battery; + struct power_supply *battery; struct max17040_platform_data *pdata; /* State Of Connect */ @@ -57,8 +57,7 @@ static int max17040_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct max17040_chip *chip = container_of(psy, - struct max17040_chip, battery); + struct max17040_chip *chip = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_STATUS: @@ -188,7 +187,8 @@ static void max17040_work(struct work_struct *work) max17040_get_online(chip->client); max17040_get_status(chip->client); - schedule_delayed_work(&chip->work, MAX17040_DELAY); + queue_delayed_work(system_power_efficient_wq, &chip->work, + MAX17040_DELAY); } static enum power_supply_property max17040_battery_props[] = { @@ -198,12 +198,20 @@ static enum power_supply_property max17040_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY, }; +static const struct power_supply_desc max17040_battery_desc = { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = max17040_get_property, + .properties = max17040_battery_props, + .num_properties = ARRAY_SIZE(max17040_battery_props), +}; + static int max17040_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct power_supply_config psy_cfg = {}; struct max17040_chip *chip; - int ret; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) return -EIO; @@ -216,24 +224,21 @@ static int max17040_probe(struct i2c_client *client, chip->pdata = client->dev.platform_data; i2c_set_clientdata(client, chip); + psy_cfg.drv_data = chip; - chip->battery.name = "battery"; - chip->battery.type = POWER_SUPPLY_TYPE_BATTERY; - chip->battery.get_property = max17040_get_property; - chip->battery.properties = max17040_battery_props; - chip->battery.num_properties = ARRAY_SIZE(max17040_battery_props); - - ret = power_supply_register(&client->dev, &chip->battery); - if (ret) { + chip->battery = power_supply_register(&client->dev, + &max17040_battery_desc, &psy_cfg); + if (IS_ERR(chip->battery)) { dev_err(&client->dev, "failed: power supply register\n"); - return ret; + return PTR_ERR(chip->battery); } max17040_reset(client); max17040_get_version(client); INIT_DEFERRABLE_WORK(&chip->work, max17040_work); - schedule_delayed_work(&chip->work, MAX17040_DELAY); + queue_delayed_work(system_power_efficient_wq, &chip->work, + MAX17040_DELAY); return 0; } @@ -242,7 +247,7 @@ static int max17040_remove(struct i2c_client *client) { struct max17040_chip *chip = i2c_get_clientdata(client); - power_supply_unregister(&chip->battery); + power_supply_unregister(chip->battery); cancel_delayed_work(&chip->work); return 0; } @@ -263,7 +268,8 @@ static int max17040_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct max17040_chip *chip = i2c_get_clientdata(client); - schedule_delayed_work(&chip->work, MAX17040_DELAY); + queue_delayed_work(system_power_efficient_wq, &chip->work, + MAX17040_DELAY); return 0; } diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 1da6c5f..6cc5e87 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -63,13 +63,10 @@ #define dP_ACC_100 0x1900 #define dP_ACC_200 0x3200 -#define MAX17042_IC_VERSION 0x0092 -#define MAX17047_IC_VERSION 0x00AC /* same for max17050 */ - struct max17042_chip { struct i2c_client *client; struct regmap *regmap; - struct power_supply battery; + struct power_supply *battery; enum max170xx_chip_type chip_type; struct max17042_platform_data *pdata; struct work_struct work; @@ -96,8 +93,7 @@ static int max17042_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct max17042_chip *chip = container_of(psy, - struct max17042_chip, battery); + struct max17042_chip *chip = power_supply_get_drvdata(psy); struct regmap *map = chip->regmap; int ret; u32 data; @@ -132,7 +128,7 @@ static int max17042_get_property(struct power_supply *psy, val->intval *= 20000; /* Units of LSB = 20mV */ break; case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: - if (chip->chip_type == MAX17042) + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) ret = regmap_read(map, MAX17042_V_empty, &data); else ret = regmap_read(map, MAX17047_V_empty, &data); @@ -272,6 +268,7 @@ static inline void max17042_override_por(struct regmap *map, static inline void max10742_unlock_model(struct max17042_chip *chip) { struct regmap *map = chip->regmap; + regmap_write(map, MAX17042_MLOCKReg1, MODEL_UNLOCK1); regmap_write(map, MAX17042_MLOCKReg2, MODEL_UNLOCK2); } @@ -289,6 +286,7 @@ static inline void max17042_write_model_data(struct max17042_chip *chip, { struct regmap *map = chip->regmap; int i; + for (i = 0; i < size; i++) regmap_write(map, addr + i, chip->pdata->config_data->cell_char_tbl[i]); @@ -379,7 +377,8 @@ static void max17042_write_config_regs(struct max17042_chip *chip) regmap_write(map, MAX17042_FilterCFG, config->filter_cfg); regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg); - if (chip->chip_type == MAX17047) + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 || + chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) regmap_write(map, MAX17047_FullSOCThr, config->full_soc_thresh); } @@ -392,7 +391,7 @@ static void max17042_write_custom_regs(struct max17042_chip *chip) max17042_write_verify_reg(map, MAX17042_RCOMP0, config->rcomp0); max17042_write_verify_reg(map, MAX17042_TempCo, config->tcompc0); max17042_write_verify_reg(map, MAX17042_ICHGTerm, config->ichgt_term); - if (chip->chip_type == MAX17042) { + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) { regmap_write(map, MAX17042_EmptyTempCo, config->empty_tempco); max17042_write_verify_reg(map, MAX17042_K_empty0, config->kempty0); @@ -501,14 +500,14 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) max17042_override_por(map, MAX17042_FullCAP, config->fullcap); max17042_override_por(map, MAX17042_FullCAPNom, config->fullcapnom); - if (chip->chip_type == MAX17042) + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) max17042_override_por(map, MAX17042_SOC_empty, config->socempty); max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty); max17042_override_por(map, MAX17042_dQacc, config->dqacc); max17042_override_por(map, MAX17042_dPacc, config->dpacc); - if (chip->chip_type == MAX17042) + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) max17042_override_por(map, MAX17042_V_empty, config->vempty); else max17042_override_por(map, MAX17047_V_empty, config->vempty); @@ -529,7 +528,6 @@ static int max17042_init_chip(struct max17042_chip *chip) { struct regmap *map = chip->regmap; int ret; - int val; max17042_override_por_values(chip); /* After Power up, the MAX17042 requires 500mS in order @@ -572,8 +570,7 @@ static int max17042_init_chip(struct max17042_chip *chip) max17042_load_new_capacity_params(chip); /* Init complete, Clear the POR bit */ - regmap_read(map, MAX17042_STATUS, &val); - regmap_write(map, MAX17042_STATUS, val & (~STATUS_POR_BIT)); + regmap_update_bits(map, MAX17042_STATUS, STATUS_POR_BIT, 0x0); return 0; } @@ -604,7 +601,7 @@ static irqreturn_t max17042_thread_handler(int id, void *dev) max17042_set_soc_threshold(chip, 1); } - power_supply_changed(&chip->battery); + power_supply_changed(chip->battery); return IRQ_HANDLED; } @@ -664,10 +661,28 @@ static const struct regmap_config max17042_regmap_config = { .val_format_endian = REGMAP_ENDIAN_NATIVE, }; +static const struct power_supply_desc max17042_psy_desc = { + .name = "max170xx_battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = max17042_get_property, + .properties = max17042_battery_props, + .num_properties = ARRAY_SIZE(max17042_battery_props), +}; + +static const struct power_supply_desc max17042_no_current_sense_psy_desc = { + .name = "max170xx_battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = max17042_get_property, + .properties = max17042_battery_props, + .num_properties = ARRAY_SIZE(max17042_battery_props) - 2, +}; + static int max17042_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + const struct power_supply_desc *max17042_desc = &max17042_psy_desc; + struct power_supply_config psy_cfg = {}; struct max17042_chip *chip; int ret; int i; @@ -694,29 +709,13 @@ static int max17042_probe(struct i2c_client *client, } i2c_set_clientdata(client, chip); - - regmap_read(chip->regmap, MAX17042_DevName, &val); - if (val == MAX17042_IC_VERSION) { - dev_dbg(&client->dev, "chip type max17042 detected\n"); - chip->chip_type = MAX17042; - } else if (val == MAX17047_IC_VERSION) { - dev_dbg(&client->dev, "chip type max17047/50 detected\n"); - chip->chip_type = MAX17047; - } else { - dev_err(&client->dev, "device version mismatch: %x\n", val); - return -EIO; - } - - chip->battery.name = "max170xx_battery"; - chip->battery.type = POWER_SUPPLY_TYPE_BATTERY; - chip->battery.get_property = max17042_get_property; - chip->battery.properties = max17042_battery_props; - chip->battery.num_properties = ARRAY_SIZE(max17042_battery_props); + chip->chip_type = id->driver_data; + psy_cfg.drv_data = chip; /* When current is not measured, * CURRENT_NOW and CURRENT_AVG properties should be invisible. */ if (!chip->pdata->enable_current_sense) - chip->battery.num_properties -= 2; + max17042_desc = &max17042_no_current_sense_psy_desc; if (chip->pdata->r_sns == 0) chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR; @@ -733,21 +732,22 @@ static int max17042_probe(struct i2c_client *client, regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007); } - ret = power_supply_register(&client->dev, &chip->battery); - if (ret) { + chip->battery = power_supply_register(&client->dev, max17042_desc, + &psy_cfg); + if (IS_ERR(chip->battery)) { dev_err(&client->dev, "failed: power supply register\n"); - return ret; + return PTR_ERR(chip->battery); } if (client->irq) { ret = request_threaded_irq(client->irq, NULL, max17042_thread_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - chip->battery.name, chip); + chip->battery->desc->name, chip); if (!ret) { - regmap_read(chip->regmap, MAX17042_CONFIG, &val); - val |= CONFIG_ALRT_BIT_ENBL; - regmap_write(chip->regmap, MAX17042_CONFIG, val); + regmap_update_bits(chip->regmap, MAX17042_CONFIG, + CONFIG_ALRT_BIT_ENBL, + CONFIG_ALRT_BIT_ENBL); max17042_set_soc_threshold(chip, 1); } else { client->irq = 0; @@ -773,7 +773,7 @@ static int max17042_remove(struct i2c_client *client) if (client->irq) free_irq(client->irq, chip); - power_supply_unregister(&chip->battery); + power_supply_unregister(chip->battery); return 0; } @@ -823,9 +823,9 @@ MODULE_DEVICE_TABLE(of, max17042_dt_match); #endif static const struct i2c_device_id max17042_id[] = { - { "max17042", 0 }, - { "max17047", 1 }, - { "max17050", 2 }, + { "max17042", MAXIM_DEVICE_TYPE_MAX17042 }, + { "max17047", MAXIM_DEVICE_TYPE_MAX17047 }, + { "max17050", MAXIM_DEVICE_TYPE_MAX17050 }, { } }; MODULE_DEVICE_TABLE(i2c, max17042_id); diff --git a/drivers/power/max77693_charger.c b/drivers/power/max77693_charger.c index b042970..754879e 100644 --- a/drivers/power/max77693_charger.c +++ b/drivers/power/max77693_charger.c @@ -22,14 +22,14 @@ #include <linux/mfd/max77693.h> #include <linux/mfd/max77693-private.h> -static const char *max77693_charger_name = "max77693-charger"; +#define MAX77693_CHARGER_NAME "max77693-charger" static const char *max77693_charger_model = "MAX77693"; static const char *max77693_charger_manufacturer = "Maxim Integrated"; struct max77693_charger { struct device *dev; struct max77693_dev *max77693; - struct power_supply charger; + struct power_supply *charger; u32 constant_volt; u32 min_system_volt; @@ -38,13 +38,14 @@ struct max77693_charger { u32 charge_input_threshold_volt; }; -static int max77693_get_charger_state(struct regmap *regmap) +static int max77693_get_charger_state(struct regmap *regmap, int *val) { - int state; + int ret; unsigned int data; - if (regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data) < 0) - return POWER_SUPPLY_STATUS_UNKNOWN; + ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data); + if (ret < 0) + return ret; data &= CHG_DETAILS_01_CHG_MASK; data >>= CHG_DETAILS_01_CHG_SHIFT; @@ -56,35 +57,36 @@ static int max77693_get_charger_state(struct regmap *regmap) case MAX77693_CHARGING_TOP_OFF: /* In high temp the charging current is reduced, but still charging */ case MAX77693_CHARGING_HIGH_TEMP: - state = POWER_SUPPLY_STATUS_CHARGING; + *val = POWER_SUPPLY_STATUS_CHARGING; break; case MAX77693_CHARGING_DONE: - state = POWER_SUPPLY_STATUS_FULL; + *val = POWER_SUPPLY_STATUS_FULL; break; case MAX77693_CHARGING_TIMER_EXPIRED: case MAX77693_CHARGING_THERMISTOR_SUSPEND: - state = POWER_SUPPLY_STATUS_NOT_CHARGING; + *val = POWER_SUPPLY_STATUS_NOT_CHARGING; break; case MAX77693_CHARGING_OFF: case MAX77693_CHARGING_OVER_TEMP: case MAX77693_CHARGING_WATCHDOG_EXPIRED: - state = POWER_SUPPLY_STATUS_DISCHARGING; + *val = POWER_SUPPLY_STATUS_DISCHARGING; break; case MAX77693_CHARGING_RESERVED: default: - state = POWER_SUPPLY_STATUS_UNKNOWN; + *val = POWER_SUPPLY_STATUS_UNKNOWN; } - return state; + return 0; } -static int max77693_get_charge_type(struct regmap *regmap) +static int max77693_get_charge_type(struct regmap *regmap, int *val) { - int state; + int ret; unsigned int data; - if (regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data) < 0) - return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data); + if (ret < 0) + return ret; data &= CHG_DETAILS_01_CHG_MASK; data >>= CHG_DETAILS_01_CHG_SHIFT; @@ -96,13 +98,13 @@ static int max77693_get_charge_type(struct regmap *regmap) * 100 and 250 mA. It is higher than prequalification current. */ case MAX77693_CHARGING_TOP_OFF: - state = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; break; case MAX77693_CHARGING_FAST_CONST_CURRENT: case MAX77693_CHARGING_FAST_CONST_VOLTAGE: /* In high temp the charging current is reduced, but still charging */ case MAX77693_CHARGING_HIGH_TEMP: - state = POWER_SUPPLY_CHARGE_TYPE_FAST; + *val = POWER_SUPPLY_CHARGE_TYPE_FAST; break; case MAX77693_CHARGING_DONE: case MAX77693_CHARGING_TIMER_EXPIRED: @@ -110,14 +112,14 @@ static int max77693_get_charge_type(struct regmap *regmap) case MAX77693_CHARGING_OFF: case MAX77693_CHARGING_OVER_TEMP: case MAX77693_CHARGING_WATCHDOG_EXPIRED: - state = POWER_SUPPLY_CHARGE_TYPE_NONE; + *val = POWER_SUPPLY_CHARGE_TYPE_NONE; break; case MAX77693_CHARGING_RESERVED: default: - state = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; } - return state; + return 0; } /* @@ -129,69 +131,78 @@ static int max77693_get_charge_type(struct regmap *regmap) * - POWER_SUPPLY_HEALTH_UNKNOWN * - POWER_SUPPLY_HEALTH_UNSPEC_FAILURE */ -static int max77693_get_battery_health(struct regmap *regmap) +static int max77693_get_battery_health(struct regmap *regmap, int *val) { - int state; + int ret; unsigned int data; - if (regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data) < 0) - return POWER_SUPPLY_HEALTH_UNKNOWN; + ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data); + if (ret < 0) + return ret; data &= CHG_DETAILS_01_BAT_MASK; data >>= CHG_DETAILS_01_BAT_SHIFT; switch (data) { case MAX77693_BATTERY_NOBAT: - state = POWER_SUPPLY_HEALTH_DEAD; + *val = POWER_SUPPLY_HEALTH_DEAD; break; case MAX77693_BATTERY_PREQUALIFICATION: case MAX77693_BATTERY_GOOD: case MAX77693_BATTERY_LOWVOLTAGE: - state = POWER_SUPPLY_HEALTH_GOOD; + *val = POWER_SUPPLY_HEALTH_GOOD; break; case MAX77693_BATTERY_TIMER_EXPIRED: /* * Took longer to charge than expected, charging suspended. * Damaged battery? */ - state = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; + *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; break; case MAX77693_BATTERY_OVERVOLTAGE: - state = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE; break; case MAX77693_BATTERY_OVERCURRENT: - state = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + *val = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; break; case MAX77693_BATTERY_RESERVED: default: - state = POWER_SUPPLY_HEALTH_UNKNOWN; + *val = POWER_SUPPLY_HEALTH_UNKNOWN; break; } - return state; + return 0; } -static int max77693_get_present(struct regmap *regmap) +static int max77693_get_present(struct regmap *regmap, int *val) { unsigned int data; + int ret; /* * Read CHG_INT_OK register. High DETBAT bit here should be * equal to value 0x0 in CHG_DETAILS_01/BAT field. */ - regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data); - if (data & CHG_INT_OK_DETBAT_MASK) - return 0; - return 1; + ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data); + if (ret < 0) + return ret; + + *val = (data & CHG_INT_OK_DETBAT_MASK) ? 0 : 1; + + return 0; } -static int max77693_get_online(struct regmap *regmap) +static int max77693_get_online(struct regmap *regmap, int *val) { unsigned int data; + int ret; + + ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data); + if (ret < 0) + return ret; + + *val = (data & CHG_INT_OK_CHGIN_MASK) ? 1 : 0; - regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data); - if (data & CHG_INT_OK_CHGIN_MASK) - return 1; return 0; } @@ -209,27 +220,25 @@ static int max77693_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct max77693_charger *chg = container_of(psy, - struct max77693_charger, - charger); + struct max77693_charger *chg = power_supply_get_drvdata(psy); struct regmap *regmap = chg->max77693->regmap; int ret = 0; switch (psp) { case POWER_SUPPLY_PROP_STATUS: - val->intval = max77693_get_charger_state(regmap); + ret = max77693_get_charger_state(regmap, &val->intval); break; case POWER_SUPPLY_PROP_CHARGE_TYPE: - val->intval = max77693_get_charge_type(regmap); + ret = max77693_get_charge_type(regmap, &val->intval); break; case POWER_SUPPLY_PROP_HEALTH: - val->intval = max77693_get_battery_health(regmap); + ret = max77693_get_battery_health(regmap, &val->intval); break; case POWER_SUPPLY_PROP_PRESENT: - val->intval = max77693_get_present(regmap); + ret = max77693_get_present(regmap, &val->intval); break; case POWER_SUPPLY_PROP_ONLINE: - val->intval = max77693_get_online(regmap); + ret = max77693_get_online(regmap, &val->intval); break; case POWER_SUPPLY_PROP_MODEL_NAME: val->strval = max77693_charger_model; @@ -244,6 +253,14 @@ static int max77693_charger_get_property(struct power_supply *psy, return ret; } +static const struct power_supply_desc max77693_charger_desc = { + .name = MAX77693_CHARGER_NAME, + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = max77693_charger_props, + .num_properties = ARRAY_SIZE(max77693_charger_props), + .get_property = max77693_charger_get_property, +}; + static ssize_t device_attr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count, int (*fn)(struct max77693_charger *, unsigned long)) @@ -659,6 +676,7 @@ static int max77693_dt_init(struct device *dev, struct max77693_charger *chg) static int max77693_charger_probe(struct platform_device *pdev) { struct max77693_charger *chg; + struct power_supply_config psy_cfg = {}; struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent); int ret; @@ -678,11 +696,7 @@ static int max77693_charger_probe(struct platform_device *pdev) if (ret) return ret; - chg->charger.name = max77693_charger_name; - chg->charger.type = POWER_SUPPLY_TYPE_BATTERY; - chg->charger.properties = max77693_charger_props; - chg->charger.num_properties = ARRAY_SIZE(max77693_charger_props); - chg->charger.get_property = max77693_charger_get_property; + psy_cfg.drv_data = chg; ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer); if (ret) { @@ -703,9 +717,12 @@ static int max77693_charger_probe(struct platform_device *pdev) goto err; } - ret = power_supply_register(&pdev->dev, &chg->charger); - if (ret) { + chg->charger = power_supply_register(&pdev->dev, + &max77693_charger_desc, + &psy_cfg); + if (IS_ERR(chg->charger)) { dev_err(&pdev->dev, "failed: power supply register\n"); + ret = PTR_ERR(chg->charger); goto err; } @@ -727,7 +744,7 @@ static int max77693_charger_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current); device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); - power_supply_unregister(&chg->charger); + power_supply_unregister(chg->charger); return 0; } diff --git a/drivers/power/max8903_charger.c b/drivers/power/max8903_charger.c index 99e3cdc..bf2b4b3 100644 --- a/drivers/power/max8903_charger.c +++ b/drivers/power/max8903_charger.c @@ -31,7 +31,8 @@ struct max8903_data { struct max8903_pdata pdata; struct device *dev; - struct power_supply psy; + struct power_supply *psy; + struct power_supply_desc psy_desc; bool fault; bool usb_in; bool ta_in; @@ -47,8 +48,7 @@ static int max8903_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct max8903_data *data = container_of(psy, - struct max8903_data, psy); + struct max8903_data *data = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_STATUS: @@ -104,17 +104,17 @@ static irqreturn_t max8903_dcin(int irq, void *_data) dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ? "Connected" : "Disconnected"); - old_type = data->psy.type; + old_type = data->psy_desc.type; if (data->ta_in) - data->psy.type = POWER_SUPPLY_TYPE_MAINS; + data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS; else if (data->usb_in) - data->psy.type = POWER_SUPPLY_TYPE_USB; + data->psy_desc.type = POWER_SUPPLY_TYPE_USB; else - data->psy.type = POWER_SUPPLY_TYPE_BATTERY; + data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY; - if (old_type != data->psy.type) - power_supply_changed(&data->psy); + if (old_type != data->psy_desc.type) + power_supply_changed(data->psy); return IRQ_HANDLED; } @@ -143,17 +143,17 @@ static irqreturn_t max8903_usbin(int irq, void *_data) dev_dbg(data->dev, "USB Charger %s.\n", usb_in ? "Connected" : "Disconnected"); - old_type = data->psy.type; + old_type = data->psy_desc.type; if (data->ta_in) - data->psy.type = POWER_SUPPLY_TYPE_MAINS; + data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS; else if (data->usb_in) - data->psy.type = POWER_SUPPLY_TYPE_USB; + data->psy_desc.type = POWER_SUPPLY_TYPE_USB; else - data->psy.type = POWER_SUPPLY_TYPE_BATTERY; + data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY; - if (old_type != data->psy.type) - power_supply_changed(&data->psy); + if (old_type != data->psy_desc.type) + power_supply_changed(data->psy); return IRQ_HANDLED; } @@ -184,6 +184,7 @@ static int max8903_probe(struct platform_device *pdev) struct max8903_data *data; struct device *dev = &pdev->dev; struct max8903_pdata *pdata = pdev->dev.platform_data; + struct power_supply_config psy_cfg = {}; int ret = 0; int gpio; int ta_in = 0; @@ -280,17 +281,20 @@ static int max8903_probe(struct platform_device *pdev) data->ta_in = ta_in; data->usb_in = usb_in; - data->psy.name = "max8903_charger"; - data->psy.type = (ta_in) ? POWER_SUPPLY_TYPE_MAINS : + data->psy_desc.name = "max8903_charger"; + data->psy_desc.type = (ta_in) ? POWER_SUPPLY_TYPE_MAINS : ((usb_in) ? POWER_SUPPLY_TYPE_USB : POWER_SUPPLY_TYPE_BATTERY); - data->psy.get_property = max8903_get_property; - data->psy.properties = max8903_charger_props; - data->psy.num_properties = ARRAY_SIZE(max8903_charger_props); + data->psy_desc.get_property = max8903_get_property; + data->psy_desc.properties = max8903_charger_props; + data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props); - ret = power_supply_register(dev, &data->psy); - if (ret) { + psy_cfg.drv_data = data; + + data->psy = power_supply_register(dev, &data->psy_desc, &psy_cfg); + if (IS_ERR(data->psy)) { dev_err(dev, "failed: power supply register.\n"); + ret = PTR_ERR(data->psy); goto err; } @@ -339,7 +343,7 @@ err_dc_irq: if (pdata->dc_valid) free_irq(gpio_to_irq(pdata->dok), data); err_psy: - power_supply_unregister(&data->psy); + power_supply_unregister(data->psy); err: return ret; } @@ -357,7 +361,7 @@ static int max8903_remove(struct platform_device *pdev) free_irq(gpio_to_irq(pdata->uok), data); if (pdata->dc_valid) free_irq(gpio_to_irq(pdata->dok), data); - power_supply_unregister(&data->psy); + power_supply_unregister(data->psy); } return 0; diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c index a6d45ee..57eb5c2 100644 --- a/drivers/power/max8925_power.c +++ b/drivers/power/max8925_power.c @@ -68,9 +68,9 @@ struct max8925_power_info { struct i2c_client *gpm; struct i2c_client *adc; - struct power_supply ac; - struct power_supply usb; - struct power_supply battery; + struct power_supply *ac; + struct power_supply *usb; + struct power_supply *battery; int irq_base; unsigned ac_online:1; unsigned usb_online:1; @@ -196,7 +196,7 @@ static int max8925_ac_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct max8925_power_info *info = dev_get_drvdata(psy->dev->parent); + struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent); int ret = 0; switch (psp) { @@ -230,7 +230,7 @@ static int max8925_usb_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct max8925_power_info *info = dev_get_drvdata(psy->dev->parent); + struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent); int ret = 0; switch (psp) { @@ -264,7 +264,7 @@ static int max8925_bat_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct max8925_power_info *info = dev_get_drvdata(psy->dev->parent); + struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent); int ret = 0; switch (psp) { @@ -347,6 +347,30 @@ static enum power_supply_property max8925_battery_props[] = { POWER_SUPPLY_PROP_STATUS, }; +static const struct power_supply_desc ac_desc = { + .name = "max8925-ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = max8925_ac_props, + .num_properties = ARRAY_SIZE(max8925_ac_props), + .get_property = max8925_ac_get_prop, +}; + +static const struct power_supply_desc usb_desc = { + .name = "max8925-usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = max8925_usb_props, + .num_properties = ARRAY_SIZE(max8925_usb_props), + .get_property = max8925_usb_get_prop, +}; + +static const struct power_supply_desc battery_desc = { + .name = "max8925-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = max8925_battery_props, + .num_properties = ARRAY_SIZE(max8925_battery_props), + .get_property = max8925_bat_get_prop, +}; + #define REQUEST_IRQ(_irq, _name) \ do { \ ret = request_threaded_irq(chip->irq_base + _irq, NULL, \ @@ -482,6 +506,7 @@ max8925_power_dt_init(struct platform_device *pdev) static int max8925_power_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct power_supply_config psy_cfg = {}; /* Only for ac and usb */ struct max8925_power_pdata *pdata = NULL; struct max8925_power_info *info; int ret; @@ -502,40 +527,29 @@ static int max8925_power_probe(struct platform_device *pdev) info->adc = chip->adc; platform_set_drvdata(pdev, info); - info->ac.name = "max8925-ac"; - info->ac.type = POWER_SUPPLY_TYPE_MAINS; - info->ac.properties = max8925_ac_props; - info->ac.num_properties = ARRAY_SIZE(max8925_ac_props); - info->ac.get_property = max8925_ac_get_prop; - info->ac.supplied_to = pdata->supplied_to; - info->ac.num_supplicants = pdata->num_supplicants; - ret = power_supply_register(&pdev->dev, &info->ac); - if (ret) + psy_cfg.supplied_to = pdata->supplied_to; + psy_cfg.num_supplicants = pdata->num_supplicants; + + info->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg); + if (IS_ERR(info->ac)) { + ret = PTR_ERR(info->ac); goto out; - info->ac.dev->parent = &pdev->dev; - - info->usb.name = "max8925-usb"; - info->usb.type = POWER_SUPPLY_TYPE_USB; - info->usb.properties = max8925_usb_props; - info->usb.num_properties = ARRAY_SIZE(max8925_usb_props); - info->usb.get_property = max8925_usb_get_prop; - info->usb.supplied_to = pdata->supplied_to; - info->usb.num_supplicants = pdata->num_supplicants; - - ret = power_supply_register(&pdev->dev, &info->usb); - if (ret) + } + info->ac->dev.parent = &pdev->dev; + + info->usb = power_supply_register(&pdev->dev, &usb_desc, &psy_cfg); + if (IS_ERR(info->usb)) { + ret = PTR_ERR(info->usb); goto out_usb; - info->usb.dev->parent = &pdev->dev; - - info->battery.name = "max8925-battery"; - info->battery.type = POWER_SUPPLY_TYPE_BATTERY; - info->battery.properties = max8925_battery_props; - info->battery.num_properties = ARRAY_SIZE(max8925_battery_props); - info->battery.get_property = max8925_bat_get_prop; - ret = power_supply_register(&pdev->dev, &info->battery); - if (ret) + } + info->usb->dev.parent = &pdev->dev; + + info->battery = power_supply_register(&pdev->dev, &battery_desc, NULL); + if (IS_ERR(info->battery)) { + ret = PTR_ERR(info->battery); goto out_battery; - info->battery.dev->parent = &pdev->dev; + } + info->battery->dev.parent = &pdev->dev; info->batt_detect = pdata->batt_detect; info->topoff_threshold = pdata->topoff_threshold; @@ -547,9 +561,9 @@ static int max8925_power_probe(struct platform_device *pdev) max8925_init_charger(chip, info); return 0; out_battery: - power_supply_unregister(&info->battery); + power_supply_unregister(info->battery); out_usb: - power_supply_unregister(&info->ac); + power_supply_unregister(info->ac); out: return ret; } @@ -559,9 +573,9 @@ static int max8925_power_remove(struct platform_device *pdev) struct max8925_power_info *info = platform_get_drvdata(pdev); if (info) { - power_supply_unregister(&info->ac); - power_supply_unregister(&info->usb); - power_supply_unregister(&info->battery); + power_supply_unregister(info->ac); + power_supply_unregister(info->usb); + power_supply_unregister(info->battery); max8925_deinit_charger(info); } return 0; diff --git a/drivers/power/max8997_charger.c b/drivers/power/max8997_charger.c index aefa0c9..0b2eab5 100644 --- a/drivers/power/max8997_charger.c +++ b/drivers/power/max8997_charger.c @@ -30,7 +30,7 @@ struct charger_data { struct device *dev; struct max8997_dev *iodev; - struct power_supply battery; + struct power_supply *battery; }; static enum power_supply_property max8997_battery_props[] = { @@ -44,8 +44,7 @@ static int max8997_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct charger_data *charger = container_of(psy, - struct charger_data, battery); + struct charger_data *charger = power_supply_get_drvdata(psy); struct i2c_client *i2c = charger->iodev->i2c; int ret; u8 reg; @@ -86,12 +85,21 @@ static int max8997_battery_get_property(struct power_supply *psy, return 0; } +static const struct power_supply_desc max8997_battery_desc = { + .name = "max8997_pmic", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = max8997_battery_get_property, + .properties = max8997_battery_props, + .num_properties = ARRAY_SIZE(max8997_battery_props), +}; + static int max8997_battery_probe(struct platform_device *pdev) { int ret = 0; struct charger_data *charger; struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); + struct power_supply_config psy_cfg = {}; if (!pdata) return -EINVAL; @@ -147,19 +155,18 @@ static int max8997_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, charger); - charger->battery.name = "max8997_pmic"; - charger->battery.type = POWER_SUPPLY_TYPE_BATTERY; - charger->battery.get_property = max8997_battery_get_property; - charger->battery.properties = max8997_battery_props; - charger->battery.num_properties = ARRAY_SIZE(max8997_battery_props); charger->dev = &pdev->dev; charger->iodev = iodev; - ret = power_supply_register(&pdev->dev, &charger->battery); - if (ret) { + psy_cfg.drv_data = charger; + + charger->battery = power_supply_register(&pdev->dev, + &max8997_battery_desc, + &psy_cfg); + if (IS_ERR(charger->battery)) { dev_err(&pdev->dev, "failed: power supply register\n"); - return ret; + return PTR_ERR(charger->battery); } return 0; @@ -169,7 +176,7 @@ static int max8997_battery_remove(struct platform_device *pdev) { struct charger_data *charger = platform_get_drvdata(pdev); - power_supply_unregister(&charger->battery); + power_supply_unregister(charger->battery); return 0; } diff --git a/drivers/power/max8998_charger.c b/drivers/power/max8998_charger.c index 08694c7..47448d4 100644 --- a/drivers/power/max8998_charger.c +++ b/drivers/power/max8998_charger.c @@ -30,7 +30,7 @@ struct max8998_battery_data { struct device *dev; struct max8998_dev *iodev; - struct power_supply battery; + struct power_supply *battery; }; static enum power_supply_property max8998_battery_props[] = { @@ -43,8 +43,7 @@ static int max8998_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct max8998_battery_data *max8998 = container_of(psy, - struct max8998_battery_data, battery); + struct max8998_battery_data *max8998 = power_supply_get_drvdata(psy); struct i2c_client *i2c = max8998->iodev->i2c; int ret; u8 reg; @@ -75,10 +74,19 @@ static int max8998_battery_get_property(struct power_supply *psy, return 0; } +static const struct power_supply_desc max8998_battery_desc = { + .name = "max8998_pmic", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = max8998_battery_get_property, + .properties = max8998_battery_props, + .num_properties = ARRAY_SIZE(max8998_battery_props), +}; + static int max8998_battery_probe(struct platform_device *pdev) { struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct max8998_platform_data *pdata = dev_get_platdata(iodev->dev); + struct power_supply_config psy_cfg = {}; struct max8998_battery_data *max8998; struct i2c_client *i2c; int ret = 0; @@ -161,15 +169,15 @@ static int max8998_battery_probe(struct platform_device *pdev) goto err; } - max8998->battery.name = "max8998_pmic"; - max8998->battery.type = POWER_SUPPLY_TYPE_BATTERY; - max8998->battery.get_property = max8998_battery_get_property; - max8998->battery.properties = max8998_battery_props; - max8998->battery.num_properties = ARRAY_SIZE(max8998_battery_props); + psy_cfg.drv_data = max8998; - ret = power_supply_register(max8998->dev, &max8998->battery); - if (ret) { - dev_err(max8998->dev, "failed: power supply register\n"); + max8998->battery = power_supply_register(max8998->dev, + &max8998_battery_desc, + &psy_cfg); + if (IS_ERR(max8998->battery)) { + ret = PTR_ERR(max8998->battery); + dev_err(max8998->dev, "failed: power supply register: %d\n", + ret); goto err; } @@ -182,7 +190,7 @@ static int max8998_battery_remove(struct platform_device *pdev) { struct max8998_battery_data *max8998 = platform_get_drvdata(pdev); - power_supply_unregister(&max8998->battery); + power_supply_unregister(max8998->battery); return 0; } diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index ad9cde7..a944338 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -81,7 +81,7 @@ static enum power_supply_property olpc_ac_props[] = { POWER_SUPPLY_PROP_ONLINE, }; -static struct power_supply olpc_ac = { +static const struct power_supply_desc olpc_ac_desc = { .name = "olpc-ac", .type = POWER_SUPPLY_TYPE_MAINS, .properties = olpc_ac_props, @@ -89,6 +89,8 @@ static struct power_supply olpc_ac = { .get_property = olpc_ac_get_prop, }; +static struct power_supply *olpc_ac; + static char bat_serial[17]; /* Ick */ static int olpc_bat_get_status(union power_supply_propval *val, uint8_t ec_byte) @@ -574,21 +576,23 @@ static struct device_attribute olpc_bat_error = { * Initialisation *********************************************************************/ -static struct power_supply olpc_bat = { +static struct power_supply_desc olpc_bat_desc = { .name = "olpc-battery", .get_property = olpc_bat_get_property, .use_for_apm = 1, }; +static struct power_supply *olpc_bat; + static int olpc_battery_suspend(struct platform_device *pdev, pm_message_t state) { - if (device_may_wakeup(olpc_ac.dev)) + if (device_may_wakeup(&olpc_ac->dev)) olpc_ec_wakeup_set(EC_SCI_SRC_ACPWR); else olpc_ec_wakeup_clear(EC_SCI_SRC_ACPWR); - if (device_may_wakeup(olpc_bat.dev)) + if (device_may_wakeup(&olpc_bat->dev)) olpc_ec_wakeup_set(EC_SCI_SRC_BATTERY | EC_SCI_SRC_BATSOC | EC_SCI_SRC_BATERR); else @@ -619,52 +623,54 @@ static int olpc_battery_probe(struct platform_device *pdev) /* Ignore the status. It doesn't actually matter */ - ret = power_supply_register(&pdev->dev, &olpc_ac); - if (ret) - return ret; + olpc_ac = power_supply_register(&pdev->dev, &olpc_ac_desc, NULL); + if (IS_ERR(olpc_ac)) + return PTR_ERR(olpc_ac); if (olpc_board_at_least(olpc_board_pre(0xd0))) { /* XO-1.5 */ - olpc_bat.properties = olpc_xo15_bat_props; - olpc_bat.num_properties = ARRAY_SIZE(olpc_xo15_bat_props); + olpc_bat_desc.properties = olpc_xo15_bat_props; + olpc_bat_desc.num_properties = ARRAY_SIZE(olpc_xo15_bat_props); } else { /* XO-1 */ - olpc_bat.properties = olpc_xo1_bat_props; - olpc_bat.num_properties = ARRAY_SIZE(olpc_xo1_bat_props); + olpc_bat_desc.properties = olpc_xo1_bat_props; + olpc_bat_desc.num_properties = ARRAY_SIZE(olpc_xo1_bat_props); } - ret = power_supply_register(&pdev->dev, &olpc_bat); - if (ret) + olpc_bat = power_supply_register(&pdev->dev, &olpc_bat_desc, NULL); + if (IS_ERR(olpc_bat)) { + ret = PTR_ERR(olpc_bat); goto battery_failed; + } - ret = device_create_bin_file(olpc_bat.dev, &olpc_bat_eeprom); + ret = device_create_bin_file(&olpc_bat->dev, &olpc_bat_eeprom); if (ret) goto eeprom_failed; - ret = device_create_file(olpc_bat.dev, &olpc_bat_error); + ret = device_create_file(&olpc_bat->dev, &olpc_bat_error); if (ret) goto error_failed; if (olpc_ec_wakeup_available()) { - device_set_wakeup_capable(olpc_ac.dev, true); - device_set_wakeup_capable(olpc_bat.dev, true); + device_set_wakeup_capable(&olpc_ac->dev, true); + device_set_wakeup_capable(&olpc_bat->dev, true); } return 0; error_failed: - device_remove_bin_file(olpc_bat.dev, &olpc_bat_eeprom); + device_remove_bin_file(&olpc_bat->dev, &olpc_bat_eeprom); eeprom_failed: - power_supply_unregister(&olpc_bat); + power_supply_unregister(olpc_bat); battery_failed: - power_supply_unregister(&olpc_ac); + power_supply_unregister(olpc_ac); return ret; } static int olpc_battery_remove(struct platform_device *pdev) { - device_remove_file(olpc_bat.dev, &olpc_bat_error); - device_remove_bin_file(olpc_bat.dev, &olpc_bat_eeprom); - power_supply_unregister(&olpc_bat); - power_supply_unregister(&olpc_ac); + device_remove_file(&olpc_bat->dev, &olpc_bat_error); + device_remove_bin_file(&olpc_bat->dev, &olpc_bat_eeprom); + power_supply_unregister(olpc_bat); + power_supply_unregister(olpc_ac); return 0; } diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c index 771c4f0..d05597b 100644 --- a/drivers/power/pcf50633-charger.c +++ b/drivers/power/pcf50633-charger.c @@ -33,9 +33,9 @@ struct pcf50633_mbc { int adapter_online; int usb_online; - struct power_supply usb; - struct power_supply adapter; - struct power_supply ac; + struct power_supply *usb; + struct power_supply *adapter; + struct power_supply *ac; }; int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) @@ -104,7 +104,7 @@ int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA); } - power_supply_changed(&mbc->usb); + power_supply_changed(mbc->usb); return ret; } @@ -278,9 +278,9 @@ pcf50633_mbc_irq_handler(int irq, void *data) else if (irq == PCF50633_IRQ_ADPREM) mbc->adapter_online = 0; - power_supply_changed(&mbc->ac); - power_supply_changed(&mbc->usb); - power_supply_changed(&mbc->adapter); + power_supply_changed(mbc->ac); + power_supply_changed(mbc->usb); + power_supply_changed(mbc->adapter); if (mbc->pcf->pdata->mbc_event_callback) mbc->pcf->pdata->mbc_event_callback(mbc->pcf, irq); @@ -290,8 +290,7 @@ static int adapter_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct pcf50633_mbc *mbc = container_of(psy, - struct pcf50633_mbc, adapter); + struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy); int ret = 0; switch (psp) { @@ -309,7 +308,7 @@ static int usb_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb); + struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy); int ret = 0; u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) & PCF50633_MBCC7_USB_MASK; @@ -330,7 +329,7 @@ static int ac_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, ac); + struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy); int ret = 0; u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) & PCF50633_MBCC7_USB_MASK; @@ -366,8 +365,33 @@ static const u8 mbc_irq_handlers[] = { PCF50633_IRQ_LOWBAT, }; +static const struct power_supply_desc pcf50633_mbc_adapter_desc = { + .name = "adapter", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = power_props, + .num_properties = ARRAY_SIZE(power_props), + .get_property = &adapter_get_property, +}; + +static const struct power_supply_desc pcf50633_mbc_usb_desc = { + .name = "usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = power_props, + .num_properties = ARRAY_SIZE(power_props), + .get_property = usb_get_property, +}; + +static const struct power_supply_desc pcf50633_mbc_ac_desc = { + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = power_props, + .num_properties = ARRAY_SIZE(power_props), + .get_property = ac_get_property, +}; + static int pcf50633_mbc_probe(struct platform_device *pdev) { + struct power_supply_config psy_cfg = {}; struct pcf50633_mbc *mbc; int ret; int i; @@ -385,49 +409,36 @@ static int pcf50633_mbc_probe(struct platform_device *pdev) pcf50633_register_irq(mbc->pcf, mbc_irq_handlers[i], pcf50633_mbc_irq_handler, mbc); + psy_cfg.supplied_to = mbc->pcf->pdata->batteries; + psy_cfg.num_supplicants = mbc->pcf->pdata->num_batteries; + psy_cfg.drv_data = mbc; + /* Create power supplies */ - mbc->adapter.name = "adapter"; - mbc->adapter.type = POWER_SUPPLY_TYPE_MAINS; - mbc->adapter.properties = power_props; - mbc->adapter.num_properties = ARRAY_SIZE(power_props); - mbc->adapter.get_property = &adapter_get_property; - mbc->adapter.supplied_to = mbc->pcf->pdata->batteries; - mbc->adapter.num_supplicants = mbc->pcf->pdata->num_batteries; - - mbc->usb.name = "usb"; - mbc->usb.type = POWER_SUPPLY_TYPE_USB; - mbc->usb.properties = power_props; - mbc->usb.num_properties = ARRAY_SIZE(power_props); - mbc->usb.get_property = usb_get_property; - mbc->usb.supplied_to = mbc->pcf->pdata->batteries; - mbc->usb.num_supplicants = mbc->pcf->pdata->num_batteries; - - mbc->ac.name = "ac"; - mbc->ac.type = POWER_SUPPLY_TYPE_MAINS; - mbc->ac.properties = power_props; - mbc->ac.num_properties = ARRAY_SIZE(power_props); - mbc->ac.get_property = ac_get_property; - mbc->ac.supplied_to = mbc->pcf->pdata->batteries; - mbc->ac.num_supplicants = mbc->pcf->pdata->num_batteries; - - ret = power_supply_register(&pdev->dev, &mbc->adapter); - if (ret) { + mbc->adapter = power_supply_register(&pdev->dev, + &pcf50633_mbc_adapter_desc, + &psy_cfg); + if (IS_ERR(mbc->adapter)) { dev_err(mbc->pcf->dev, "failed to register adapter\n"); + ret = PTR_ERR(mbc->adapter); return ret; } - ret = power_supply_register(&pdev->dev, &mbc->usb); - if (ret) { + mbc->usb = power_supply_register(&pdev->dev, &pcf50633_mbc_usb_desc, + &psy_cfg); + if (IS_ERR(mbc->usb)) { dev_err(mbc->pcf->dev, "failed to register usb\n"); - power_supply_unregister(&mbc->adapter); + power_supply_unregister(mbc->adapter); + ret = PTR_ERR(mbc->usb); return ret; } - ret = power_supply_register(&pdev->dev, &mbc->ac); - if (ret) { + mbc->ac = power_supply_register(&pdev->dev, &pcf50633_mbc_ac_desc, + &psy_cfg); + if (IS_ERR(mbc->ac)) { dev_err(mbc->pcf->dev, "failed to register ac\n"); - power_supply_unregister(&mbc->adapter); - power_supply_unregister(&mbc->usb); + power_supply_unregister(mbc->adapter); + power_supply_unregister(mbc->usb); + ret = PTR_ERR(mbc->ac); return ret; } @@ -454,9 +465,9 @@ static int pcf50633_mbc_remove(struct platform_device *pdev) pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]); sysfs_remove_group(&pdev->dev.kobj, &mbc_attr_group); - power_supply_unregister(&mbc->usb); - power_supply_unregister(&mbc->adapter); - power_supply_unregister(&mbc->ac); + power_supply_unregister(mbc->usb); + power_supply_unregister(mbc->adapter); + power_supply_unregister(mbc->ac); return 0; } diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 0c52e2a..dfe1ee8 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -34,6 +34,7 @@ static struct timer_list charger_timer; static struct timer_list supply_timer; static struct timer_list polling_timer; static int polling; +static struct power_supply *pda_psy_ac, *pda_psy_usb; #if IS_ENABLED(CONFIG_USB_PHY) static struct usb_phy *transceiver; @@ -58,7 +59,7 @@ static int pda_power_get_property(struct power_supply *psy, { switch (psp) { case POWER_SUPPLY_PROP_ONLINE: - if (psy->type == POWER_SUPPLY_TYPE_MAINS) + if (psy->desc->type == POWER_SUPPLY_TYPE_MAINS) val->intval = pdata->is_ac_online ? pdata->is_ac_online() : 0; else @@ -80,21 +81,17 @@ static char *pda_power_supplied_to[] = { "backup-battery", }; -static struct power_supply pda_psy_ac = { +static const struct power_supply_desc pda_psy_ac_desc = { .name = "ac", .type = POWER_SUPPLY_TYPE_MAINS, - .supplied_to = pda_power_supplied_to, - .num_supplicants = ARRAY_SIZE(pda_power_supplied_to), .properties = pda_power_props, .num_properties = ARRAY_SIZE(pda_power_props), .get_property = pda_power_get_property, }; -static struct power_supply pda_psy_usb = { +static const struct power_supply_desc pda_psy_usb_desc = { .name = "usb", .type = POWER_SUPPLY_TYPE_USB, - .supplied_to = pda_power_supplied_to, - .num_supplicants = ARRAY_SIZE(pda_power_supplied_to), .properties = pda_power_props, .num_properties = ARRAY_SIZE(pda_power_props), .get_property = pda_power_get_property, @@ -147,12 +144,12 @@ static void supply_timer_func(unsigned long unused) { if (ac_status == PDA_PSY_TO_CHANGE) { ac_status = new_ac_status; - power_supply_changed(&pda_psy_ac); + power_supply_changed(pda_psy_ac); } if (usb_status == PDA_PSY_TO_CHANGE) { usb_status = new_usb_status; - power_supply_changed(&pda_psy_usb); + power_supply_changed(pda_psy_usb); } } @@ -176,9 +173,9 @@ static void charger_timer_func(unsigned long unused) static irqreturn_t power_changed_isr(int irq, void *power_supply) { - if (power_supply == &pda_psy_ac) + if (power_supply == pda_psy_ac) ac_status = PDA_PSY_TO_CHANGE; - else if (power_supply == &pda_psy_usb) + else if (power_supply == pda_psy_usb) usb_status = PDA_PSY_TO_CHANGE; else return IRQ_NONE; @@ -262,6 +259,7 @@ static int otg_handle_notification(struct notifier_block *nb, static int pda_power_probe(struct platform_device *pdev) { + struct power_supply_config psy_cfg = {}; int ret = 0; dev = &pdev->dev; @@ -309,10 +307,11 @@ static int pda_power_probe(struct platform_device *pdev) usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb"); if (pdata->supplied_to) { - pda_psy_ac.supplied_to = pdata->supplied_to; - pda_psy_ac.num_supplicants = pdata->num_supplicants; - pda_psy_usb.supplied_to = pdata->supplied_to; - pda_psy_usb.num_supplicants = pdata->num_supplicants; + psy_cfg.supplied_to = pdata->supplied_to; + psy_cfg.num_supplicants = pdata->num_supplicants; + } else { + psy_cfg.supplied_to = pda_power_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(pda_power_supplied_to); } #if IS_ENABLED(CONFIG_USB_PHY) @@ -326,17 +325,19 @@ static int pda_power_probe(struct platform_device *pdev) #endif if (pdata->is_ac_online) { - ret = power_supply_register(&pdev->dev, &pda_psy_ac); - if (ret) { + pda_psy_ac = power_supply_register(&pdev->dev, + &pda_psy_ac_desc, &psy_cfg); + if (IS_ERR(pda_psy_ac)) { dev_err(dev, "failed to register %s power supply\n", - pda_psy_ac.name); + pda_psy_ac_desc.name); + ret = PTR_ERR(pda_psy_ac); goto ac_supply_failed; } if (ac_irq) { ret = request_irq(ac_irq->start, power_changed_isr, get_irq_flags(ac_irq), ac_irq->name, - &pda_psy_ac); + pda_psy_ac); if (ret) { dev_err(dev, "request ac irq failed\n"); goto ac_irq_failed; @@ -347,17 +348,20 @@ static int pda_power_probe(struct platform_device *pdev) } if (pdata->is_usb_online) { - ret = power_supply_register(&pdev->dev, &pda_psy_usb); - if (ret) { + pda_psy_usb = power_supply_register(&pdev->dev, + &pda_psy_usb_desc, + &psy_cfg); + if (IS_ERR(pda_psy_usb)) { dev_err(dev, "failed to register %s power supply\n", - pda_psy_usb.name); + pda_psy_usb_desc.name); + ret = PTR_ERR(pda_psy_usb); goto usb_supply_failed; } if (usb_irq) { ret = request_irq(usb_irq->start, power_changed_isr, get_irq_flags(usb_irq), - usb_irq->name, &pda_psy_usb); + usb_irq->name, pda_psy_usb); if (ret) { dev_err(dev, "request usb irq failed\n"); goto usb_irq_failed; @@ -394,21 +398,21 @@ static int pda_power_probe(struct platform_device *pdev) #if IS_ENABLED(CONFIG_USB_PHY) otg_reg_notifier_failed: if (pdata->is_usb_online && usb_irq) - free_irq(usb_irq->start, &pda_psy_usb); + free_irq(usb_irq->start, pda_psy_usb); #endif usb_irq_failed: if (pdata->is_usb_online) - power_supply_unregister(&pda_psy_usb); + power_supply_unregister(pda_psy_usb); usb_supply_failed: if (pdata->is_ac_online && ac_irq) - free_irq(ac_irq->start, &pda_psy_ac); + free_irq(ac_irq->start, pda_psy_ac); #if IS_ENABLED(CONFIG_USB_PHY) if (!IS_ERR_OR_NULL(transceiver)) usb_put_phy(transceiver); #endif ac_irq_failed: if (pdata->is_ac_online) - power_supply_unregister(&pda_psy_ac); + power_supply_unregister(pda_psy_ac); ac_supply_failed: if (ac_draw) { regulator_put(ac_draw); @@ -424,9 +428,9 @@ wrongid: static int pda_power_remove(struct platform_device *pdev) { if (pdata->is_usb_online && usb_irq) - free_irq(usb_irq->start, &pda_psy_usb); + free_irq(usb_irq->start, pda_psy_usb); if (pdata->is_ac_online && ac_irq) - free_irq(ac_irq->start, &pda_psy_ac); + free_irq(ac_irq->start, pda_psy_ac); if (polling) del_timer_sync(&polling_timer); @@ -434,9 +438,9 @@ static int pda_power_remove(struct platform_device *pdev) del_timer_sync(&supply_timer); if (pdata->is_usb_online) - power_supply_unregister(&pda_psy_usb); + power_supply_unregister(pda_psy_usb); if (pdata->is_ac_online) - power_supply_unregister(&pda_psy_ac); + power_supply_unregister(pda_psy_ac); #if IS_ENABLED(CONFIG_USB_PHY) if (!IS_ERR_OR_NULL(transceiver)) usb_put_phy(transceiver); diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c index 7773249..cc0893f 100644 --- a/drivers/power/pm2301_charger.c +++ b/drivers/power/pm2301_charger.c @@ -216,7 +216,7 @@ static int pm2xxx_charger_ovv_mngt(struct pm2xxx_charger *pm2, int val) { dev_err(pm2->dev, "Overvoltage detected\n"); pm2->flags.ovv = true; - power_supply_changed(&pm2->ac_chg.psy); + power_supply_changed(pm2->ac_chg.psy); /* Schedule a new HW failure check */ queue_delayed_work(pm2->charger_wq, &pm2->check_hw_failure_work, 0); @@ -229,7 +229,7 @@ static int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val) dev_dbg(pm2->dev , "20 minutes watchdog expired\n"); pm2->ac.wd_expired = true; - power_supply_changed(&pm2->ac_chg.psy); + power_supply_changed(pm2->ac_chg.psy); return 0; } @@ -573,7 +573,7 @@ static int pm2xxx_charger_update_charger_current(struct ux500_charger *charger, struct pm2xxx_charger *pm2; u8 val; - if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS) + if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS) pm2 = to_pm2xxx_charger_ac_device_info(charger); else return -ENXIO; @@ -816,7 +816,7 @@ static int pm2xxx_charger_ac_en(struct ux500_charger *charger, dev_dbg(pm2->dev, "PM2301: " "Disabled AC charging\n"); } - power_supply_changed(&pm2->ac_chg.psy); + power_supply_changed(pm2->ac_chg.psy); error_occured: return ret; @@ -827,7 +827,7 @@ static int pm2xxx_charger_watchdog_kick(struct ux500_charger *charger) int ret; struct pm2xxx_charger *pm2; - if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS) + if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS) pm2 = to_pm2xxx_charger_ac_device_info(charger); else return -ENXIO; @@ -845,8 +845,8 @@ static void pm2xxx_charger_ac_work(struct work_struct *work) struct pm2xxx_charger, ac_work); - power_supply_changed(&pm2->ac_chg.psy); - sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present"); + power_supply_changed(pm2->ac_chg.psy); + sysfs_notify(&pm2->ac_chg.psy->dev.kobj, NULL, "present"); }; static void pm2xxx_charger_check_hw_failure_work(struct work_struct *work) @@ -862,7 +862,7 @@ static void pm2xxx_charger_check_hw_failure_work(struct work_struct *work) if (!(reg_value & (PM2XXX_INT4_S_ITVPWR1OVV | PM2XXX_INT4_S_ITVPWR2OVV))) { pm2->flags.ovv = false; - power_supply_changed(&pm2->ac_chg.psy); + power_supply_changed(pm2->ac_chg.psy); } } @@ -895,7 +895,7 @@ static void pm2xxx_charger_check_main_thermal_prot_work( | PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL)) pm2->flags.main_thermal_prot = false; - power_supply_changed(&pm2->ac_chg.psy); + power_supply_changed(pm2->ac_chg.psy); } static struct pm2xxx_interrupts pm2xxx_int = { @@ -989,6 +989,7 @@ static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) { struct pm2xxx_platform_data *pl_data = i2c_client->dev.platform_data; + struct power_supply_config psy_cfg = {}; struct pm2xxx_charger *pm2; int ret = 0; u8 val; @@ -1042,13 +1043,14 @@ static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, /* AC supply */ /* power_supply base class */ - pm2->ac_chg.psy.name = pm2->pdata->label; - pm2->ac_chg.psy.type = POWER_SUPPLY_TYPE_MAINS; - pm2->ac_chg.psy.properties = pm2xxx_charger_ac_props; - pm2->ac_chg.psy.num_properties = ARRAY_SIZE(pm2xxx_charger_ac_props); - pm2->ac_chg.psy.get_property = pm2xxx_charger_ac_get_property; - pm2->ac_chg.psy.supplied_to = pm2->pdata->supplied_to; - pm2->ac_chg.psy.num_supplicants = pm2->pdata->num_supplicants; + pm2->ac_chg_desc.name = pm2->pdata->label; + pm2->ac_chg_desc.type = POWER_SUPPLY_TYPE_MAINS; + pm2->ac_chg_desc.properties = pm2xxx_charger_ac_props; + pm2->ac_chg_desc.num_properties = ARRAY_SIZE(pm2xxx_charger_ac_props); + pm2->ac_chg_desc.get_property = pm2xxx_charger_ac_get_property; + + psy_cfg.supplied_to = pm2->pdata->supplied_to; + psy_cfg.num_supplicants = pm2->pdata->num_supplicants; /* pm2xxx_charger sub-class */ pm2->ac_chg.ops.enable = &pm2xxx_charger_ac_en; pm2->ac_chg.ops.kick_wd = &pm2xxx_charger_watchdog_kick; @@ -1093,9 +1095,11 @@ static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, } /* Register AC charger class */ - ret = power_supply_register(pm2->dev, &pm2->ac_chg.psy); - if (ret) { + pm2->ac_chg.psy = power_supply_register(pm2->dev, &pm2->ac_chg_desc, + &psy_cfg); + if (IS_ERR(pm2->ac_chg.psy)) { dev_err(pm2->dev, "failed to register AC charger\n"); + ret = PTR_ERR(pm2->ac_chg.psy); goto free_regulator; } @@ -1167,8 +1171,8 @@ static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, ab8500_override_turn_on_stat(~AB8500_POW_KEY_1_ON, AB8500_MAIN_CH_DET); pm2->ac_conn = true; - power_supply_changed(&pm2->ac_chg.psy); - sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present"); + power_supply_changed(pm2->ac_chg.psy); + sysfs_notify(&pm2->ac_chg.psy->dev.kobj, NULL, "present"); } return 0; @@ -1183,7 +1187,7 @@ unregister_pm2xxx_interrupt: free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2); unregister_pm2xxx_charger: /* unregister power supply */ - power_supply_unregister(&pm2->ac_chg.psy); + power_supply_unregister(pm2->ac_chg.psy); free_regulator: /* disable the regulator */ regulator_put(pm2->regu); @@ -1218,7 +1222,7 @@ static int pm2xxx_wall_charger_remove(struct i2c_client *i2c_client) /* disable the regulator */ regulator_put(pm2->regu); - power_supply_unregister(&pm2->ac_chg.psy); + power_supply_unregister(pm2->ac_chg.psy); if (gpio_is_valid(pm2->lpn_pin)) gpio_free(pm2->lpn_pin); diff --git a/drivers/power/pm2301_charger.h b/drivers/power/pm2301_charger.h index 8ce3cc0..24181cf 100644 --- a/drivers/power/pm2301_charger.h +++ b/drivers/power/pm2301_charger.h @@ -486,6 +486,7 @@ struct pm2xxx_charger { struct work_struct check_main_thermal_prot_work; struct delayed_work check_hw_failure_work; struct ux500_charger ac_chg; + struct power_supply_desc ac_chg_desc; struct pm2xxx_charger_event_flags flags; }; diff --git a/drivers/power/pmu_battery.c b/drivers/power/pmu_battery.c index 023d2499..9c8d525 100644 --- a/drivers/power/pmu_battery.c +++ b/drivers/power/pmu_battery.c @@ -17,13 +17,14 @@ #include <linux/slab.h> static struct pmu_battery_dev { - struct power_supply bat; + struct power_supply *bat; + struct power_supply_desc bat_desc; struct pmu_battery_info *pbi; char name[16]; int propval; } *pbats[PMU_MAX_BATTERIES]; -#define to_pmu_battery_dev(x) container_of(x, struct pmu_battery_dev, bat) +#define to_pmu_battery_dev(x) power_supply_get_drvdata(x) /********************************************************************* * Power @@ -49,7 +50,7 @@ static enum power_supply_property pmu_ac_props[] = { POWER_SUPPLY_PROP_ONLINE, }; -static struct power_supply pmu_ac = { +static const struct power_supply_desc pmu_ac_desc = { .name = "pmu-ac", .type = POWER_SUPPLY_TYPE_MAINS, .properties = pmu_ac_props, @@ -57,6 +58,8 @@ static struct power_supply pmu_ac = { .get_property = pmu_get_ac_prop, }; +static struct power_supply *pmu_ac; + /********************************************************************* * Battery properties *********************************************************************/ @@ -142,7 +145,7 @@ static struct platform_device *bat_pdev; static int __init pmu_bat_init(void) { - int ret; + int ret = 0; int i; bat_pdev = platform_device_register_simple("pmu-battery", @@ -152,25 +155,32 @@ static int __init pmu_bat_init(void) goto pdev_register_failed; } - ret = power_supply_register(&bat_pdev->dev, &pmu_ac); - if (ret) + pmu_ac = power_supply_register(&bat_pdev->dev, &pmu_ac_desc, NULL); + if (IS_ERR(pmu_ac)) { + ret = PTR_ERR(pmu_ac); goto ac_register_failed; + } for (i = 0; i < pmu_battery_count; i++) { + struct power_supply_config psy_cfg = {}; struct pmu_battery_dev *pbat = kzalloc(sizeof(*pbat), GFP_KERNEL); if (!pbat) break; sprintf(pbat->name, "PMU_battery_%d", i); - pbat->bat.name = pbat->name; - pbat->bat.properties = pmu_bat_props; - pbat->bat.num_properties = ARRAY_SIZE(pmu_bat_props); - pbat->bat.get_property = pmu_bat_get_property; + pbat->bat_desc.name = pbat->name; + pbat->bat_desc.properties = pmu_bat_props; + pbat->bat_desc.num_properties = ARRAY_SIZE(pmu_bat_props); + pbat->bat_desc.get_property = pmu_bat_get_property; pbat->pbi = &pmu_batteries[i]; + psy_cfg.drv_data = pbat; - ret = power_supply_register(&bat_pdev->dev, &pbat->bat); - if (ret) { + pbat->bat = power_supply_register(&bat_pdev->dev, + &pbat->bat_desc, + &psy_cfg); + if (IS_ERR(pbat->bat)) { + ret = PTR_ERR(pbat->bat); kfree(pbat); goto battery_register_failed; } @@ -183,10 +193,10 @@ battery_register_failed: while (i--) { if (!pbats[i]) continue; - power_supply_unregister(&pbats[i]->bat); + power_supply_unregister(pbats[i]->bat); kfree(pbats[i]); } - power_supply_unregister(&pmu_ac); + power_supply_unregister(pmu_ac); ac_register_failed: platform_device_unregister(bat_pdev); pdev_register_failed: @@ -201,10 +211,10 @@ static void __exit pmu_bat_exit(void) for (i = 0; i < PMU_MAX_BATTERIES; i++) { if (!pbats[i]) continue; - power_supply_unregister(&pbats[i]->bat); + power_supply_unregister(pbats[i]->bat); kfree(pbats[i]); } - power_supply_unregister(&pmu_ac); + power_supply_unregister(pmu_ac); platform_device_unregister(bat_pdev); } diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 694e8cd..2ed4a4a 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -40,16 +40,16 @@ static bool __power_supply_is_supplied_by(struct power_supply *supplier, /* Support both supplied_to and supplied_from modes */ if (supply->supplied_from) { - if (!supplier->name) + if (!supplier->desc->name) return false; for (i = 0; i < supply->num_supplies; i++) - if (!strcmp(supplier->name, supply->supplied_from[i])) + if (!strcmp(supplier->desc->name, supply->supplied_from[i])) return true; } else { - if (!supply->name) + if (!supply->desc->name) return false; for (i = 0; i < supplier->num_supplicants; i++) - if (!strcmp(supplier->supplied_to[i], supply->name)) + if (!strcmp(supplier->supplied_to[i], supply->desc->name)) return true; } @@ -62,8 +62,8 @@ static int __power_supply_changed_work(struct device *dev, void *data) struct power_supply *pst = dev_get_drvdata(dev); if (__power_supply_is_supplied_by(psy, pst)) { - if (pst->external_power_changed) - pst->external_power_changed(pst); + if (pst->desc->external_power_changed) + pst->desc->external_power_changed(pst); } return 0; @@ -75,7 +75,7 @@ static void power_supply_changed_work(struct work_struct *work) struct power_supply *psy = container_of(work, struct power_supply, changed_work); - dev_dbg(psy->dev, "%s\n", __func__); + dev_dbg(&psy->dev, "%s\n", __func__); spin_lock_irqsave(&psy->changed_lock, flags); /* @@ -93,7 +93,7 @@ static void power_supply_changed_work(struct work_struct *work) power_supply_update_leds(psy); atomic_notifier_call_chain(&power_supply_notifier, PSY_EVENT_PROP_CHANGED, psy); - kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); + kobject_uevent(&psy->dev.kobj, KOBJ_CHANGE); spin_lock_irqsave(&psy->changed_lock, flags); } @@ -103,7 +103,7 @@ static void power_supply_changed_work(struct work_struct *work) * to true. */ if (likely(!psy->changed)) - pm_relax(psy->dev); + pm_relax(&psy->dev); spin_unlock_irqrestore(&psy->changed_lock, flags); } @@ -111,11 +111,11 @@ void power_supply_changed(struct power_supply *psy) { unsigned long flags; - dev_dbg(psy->dev, "%s\n", __func__); + dev_dbg(&psy->dev, "%s\n", __func__); spin_lock_irqsave(&psy->changed_lock, flags); psy->changed = true; - pm_stay_awake(psy->dev); + pm_stay_awake(&psy->dev); spin_unlock_irqrestore(&psy->changed_lock, flags); schedule_work(&psy->changed_work); } @@ -138,9 +138,9 @@ static int __power_supply_populate_supplied_from(struct device *dev, break; if (np == epsy->of_node) { - dev_info(psy->dev, "%s: Found supply : %s\n", - psy->name, epsy->name); - psy->supplied_from[i-1] = (char *)epsy->name; + dev_info(&psy->dev, "%s: Found supply : %s\n", + psy->desc->name, epsy->desc->name); + psy->supplied_from[i-1] = (char *)epsy->desc->name; psy->num_supplies++; of_node_put(np); break; @@ -158,7 +158,7 @@ static int power_supply_populate_supplied_from(struct power_supply *psy) error = class_for_each_device(power_supply_class, NULL, psy, __power_supply_populate_supplied_from); - dev_dbg(psy->dev, "%s %d\n", __func__, error); + dev_dbg(&psy->dev, "%s %d\n", __func__, error); return error; } @@ -220,7 +220,7 @@ static int power_supply_check_supplies(struct power_supply *psy) of_node_put(np); if (ret) { - dev_dbg(psy->dev, "Failed to find supply!\n"); + dev_dbg(&psy->dev, "Failed to find supply!\n"); return ret; } } while (np); @@ -230,17 +230,18 @@ static int power_supply_check_supplies(struct power_supply *psy) return 0; /* All supplies found, allocate char ** array for filling */ - psy->supplied_from = devm_kzalloc(psy->dev, sizeof(psy->supplied_from), + psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(psy->supplied_from), GFP_KERNEL); if (!psy->supplied_from) { - dev_err(psy->dev, "Couldn't allocate memory for supply list\n"); + dev_err(&psy->dev, "Couldn't allocate memory for supply list\n"); return -ENOMEM; } - *psy->supplied_from = devm_kzalloc(psy->dev, sizeof(char *) * (cnt - 1), + *psy->supplied_from = devm_kzalloc(&psy->dev, + sizeof(char *) * (cnt - 1), GFP_KERNEL); if (!*psy->supplied_from) { - dev_err(psy->dev, "Couldn't allocate memory for supply list\n"); + dev_err(&psy->dev, "Couldn't allocate memory for supply list\n"); return -ENOMEM; } @@ -260,7 +261,8 @@ static int __power_supply_am_i_supplied(struct device *dev, void *data) struct power_supply *epsy = dev_get_drvdata(dev); if (__power_supply_is_supplied_by(epsy, psy)) - if (!epsy->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, &ret)) + if (!epsy->desc->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, + &ret)) return ret.intval; return 0; @@ -273,7 +275,7 @@ int power_supply_am_i_supplied(struct power_supply *psy) error = class_for_each_device(power_supply_class, NULL, psy, __power_supply_am_i_supplied); - dev_dbg(psy->dev, "%s %d\n", __func__, error); + dev_dbg(&psy->dev, "%s %d\n", __func__, error); return error; } @@ -286,8 +288,9 @@ static int __power_supply_is_system_supplied(struct device *dev, void *data) unsigned int *count = data; (*count)++; - if (psy->type != POWER_SUPPLY_TYPE_BATTERY) - if (!psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret)) + if (psy->desc->type != POWER_SUPPLY_TYPE_BATTERY) + if (!psy->desc->get_property(psy, POWER_SUPPLY_PROP_ONLINE, + &ret)) return ret.intval; return 0; @@ -314,8 +317,10 @@ EXPORT_SYMBOL_GPL(power_supply_is_system_supplied); int power_supply_set_battery_charged(struct power_supply *psy) { - if (psy->type == POWER_SUPPLY_TYPE_BATTERY && psy->set_charged) { - psy->set_charged(psy); + if (atomic_read(&psy->use_cnt) >= 0 && + psy->desc->type == POWER_SUPPLY_TYPE_BATTERY && + psy->desc->set_charged) { + psy->desc->set_charged(psy); return 0; } @@ -328,28 +333,74 @@ static int power_supply_match_device_by_name(struct device *dev, const void *dat const char *name = data; struct power_supply *psy = dev_get_drvdata(dev); - return strcmp(psy->name, name) == 0; + return strcmp(psy->desc->name, name) == 0; } +/** + * power_supply_get_by_name() - Search for a power supply and returns its ref + * @name: Power supply name to fetch + * + * If power supply was found, it increases reference count for the + * internal power supply's device. The user should power_supply_put() + * after usage. + * + * Return: On success returns a reference to a power supply with + * matching name equals to @name, a NULL otherwise. + */ struct power_supply *power_supply_get_by_name(const char *name) { + struct power_supply *psy = NULL; struct device *dev = class_find_device(power_supply_class, NULL, name, power_supply_match_device_by_name); - return dev ? dev_get_drvdata(dev) : NULL; + if (dev) { + psy = dev_get_drvdata(dev); + atomic_inc(&psy->use_cnt); + } + + return psy; } EXPORT_SYMBOL_GPL(power_supply_get_by_name); +/** + * power_supply_put() - Drop reference obtained with power_supply_get_by_name + * @psy: Reference to put + * + * The reference to power supply should be put before unregistering + * the power supply. + */ +void power_supply_put(struct power_supply *psy) +{ + might_sleep(); + + atomic_dec(&psy->use_cnt); + put_device(&psy->dev); +} +EXPORT_SYMBOL_GPL(power_supply_put); + #ifdef CONFIG_OF static int power_supply_match_device_node(struct device *dev, const void *data) { return dev->parent && dev->parent->of_node == data; } +/** + * power_supply_get_by_phandle() - Search for a power supply and returns its ref + * @np: Pointer to device node holding phandle property + * @phandle_name: Name of property holding a power supply name + * + * If power supply was found, it increases reference count for the + * internal power supply's device. The user should power_supply_put() + * after usage. + * + * Return: On success returns a reference to a power supply with + * matching name equals to value under @property, NULL or ERR_PTR otherwise. + */ struct power_supply *power_supply_get_by_phandle(struct device_node *np, const char *property) { struct device_node *power_supply_np; + struct power_supply *psy = NULL; struct device *dev; power_supply_np = of_parse_phandle(np, property, 0); @@ -361,21 +412,70 @@ struct power_supply *power_supply_get_by_phandle(struct device_node *np, of_node_put(power_supply_np); - return dev ? dev_get_drvdata(dev) : NULL; + if (dev) { + psy = dev_get_drvdata(dev); + atomic_inc(&psy->use_cnt); + } + + return psy; } EXPORT_SYMBOL_GPL(power_supply_get_by_phandle); #endif /* CONFIG_OF */ +int power_supply_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + if (atomic_read(&psy->use_cnt) <= 0) + return -ENODEV; + + return psy->desc->get_property(psy, psp, val); +} +EXPORT_SYMBOL_GPL(power_supply_get_property); + +int power_supply_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + if (atomic_read(&psy->use_cnt) <= 0 || !psy->desc->set_property) + return -ENODEV; + + return psy->desc->set_property(psy, psp, val); +} +EXPORT_SYMBOL_GPL(power_supply_set_property); + +int power_supply_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + if (atomic_read(&psy->use_cnt) <= 0 || + !psy->desc->property_is_writeable) + return -ENODEV; + + return psy->desc->property_is_writeable(psy, psp); +} +EXPORT_SYMBOL_GPL(power_supply_property_is_writeable); + +void power_supply_external_power_changed(struct power_supply *psy) +{ + if (atomic_read(&psy->use_cnt) <= 0 || + !psy->desc->external_power_changed) + return; + + psy->desc->external_power_changed(psy); +} +EXPORT_SYMBOL_GPL(power_supply_external_power_changed); + int power_supply_powers(struct power_supply *psy, struct device *dev) { - return sysfs_create_link(&psy->dev->kobj, &dev->kobj, "powers"); + return sysfs_create_link(&psy->dev.kobj, &dev->kobj, "powers"); } EXPORT_SYMBOL_GPL(power_supply_powers); static void power_supply_dev_release(struct device *dev) { + struct power_supply *psy = container_of(dev, struct power_supply, dev); pr_debug("device: '%s': %s\n", dev_name(dev), __func__); - kfree(dev); + kfree(psy); } int power_supply_reg_notifier(struct notifier_block *nb) @@ -400,7 +500,7 @@ static int power_supply_read_temp(struct thermal_zone_device *tzd, WARN_ON(tzd == NULL); psy = tzd->devdata; - ret = psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &val); + ret = psy->desc->get_property(psy, POWER_SUPPLY_PROP_TEMP, &val); /* Convert tenths of degree Celsius to milli degree Celsius. */ if (!ret) @@ -417,14 +517,14 @@ static int psy_register_thermal(struct power_supply *psy) { int i; - if (psy->no_thermal) + if (psy->desc->no_thermal) return 0; /* Register battery zone device psy reports temperature */ - for (i = 0; i < psy->num_properties; i++) { - if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) { - psy->tzd = thermal_zone_device_register(psy->name, 0, 0, - psy, &psy_tzd_ops, NULL, 0, 0); + for (i = 0; i < psy->desc->num_properties; i++) { + if (psy->desc->properties[i] == POWER_SUPPLY_PROP_TEMP) { + psy->tzd = thermal_zone_device_register(psy->desc->name, + 0, 0, psy, &psy_tzd_ops, NULL, 0, 0); return PTR_ERR_OR_ZERO(psy->tzd); } } @@ -447,7 +547,7 @@ static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device *tcd, int ret; psy = tcd->devdata; - ret = psy->get_property(psy, + ret = psy->desc->get_property(psy, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val); if (!ret) *state = val.intval; @@ -463,7 +563,7 @@ static int ps_get_cur_chrage_cntl_limit(struct thermal_cooling_device *tcd, int ret; psy = tcd->devdata; - ret = psy->get_property(psy, + ret = psy->desc->get_property(psy, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val); if (!ret) *state = val.intval; @@ -480,7 +580,7 @@ static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device *tcd, psy = tcd->devdata; val.intval = state; - ret = psy->set_property(psy, + ret = psy->desc->set_property(psy, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val); return ret; @@ -497,11 +597,11 @@ static int psy_register_cooler(struct power_supply *psy) int i; /* Register for cooling device if psy can control charging */ - for (i = 0; i < psy->num_properties; i++) { - if (psy->properties[i] == + for (i = 0; i < psy->desc->num_properties; i++) { + if (psy->desc->properties[i] == POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT) { psy->tcd = thermal_cooling_device_register( - (char *)psy->name, + (char *)psy->desc->name, psy, &psy_tcd_ops); return PTR_ERR_OR_ZERO(psy->tcd); } @@ -535,15 +635,21 @@ static void psy_unregister_cooler(struct power_supply *psy) } #endif -static int __power_supply_register(struct device *parent, - struct power_supply *psy, bool ws) +static struct power_supply *__must_check +__power_supply_register(struct device *parent, + const struct power_supply_desc *desc, + const struct power_supply_config *cfg, + bool ws) { struct device *dev; + struct power_supply *psy; int rc; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; + psy = kzalloc(sizeof(*psy), GFP_KERNEL); + if (!psy) + return ERR_PTR(-ENOMEM); + + dev = &psy->dev; device_initialize(dev); @@ -552,9 +658,16 @@ static int __power_supply_register(struct device *parent, dev->parent = parent; dev->release = power_supply_dev_release; dev_set_drvdata(dev, psy); - psy->dev = dev; + psy->desc = desc; + atomic_inc(&psy->use_cnt); + if (cfg) { + psy->drv_data = cfg->drv_data; + psy->of_node = cfg->of_node; + psy->supplied_to = cfg->supplied_to; + psy->num_supplicants = cfg->num_supplicants; + } - rc = dev_set_name(dev, "%s", psy->name); + rc = dev_set_name(dev, "%s", desc->name); if (rc) goto dev_set_name_failed; @@ -589,7 +702,7 @@ static int __power_supply_register(struct device *parent, power_supply_changed(psy); - return 0; + return psy; create_triggers_failed: psy_unregister_cooler(psy); @@ -602,33 +715,155 @@ wakeup_init_failed: check_supplies_failed: dev_set_name_failed: put_device(dev); - return rc; + return ERR_PTR(rc); } -int power_supply_register(struct device *parent, struct power_supply *psy) +/** + * power_supply_register() - Register new power supply + * @parent: Device to be a parent of power supply's device + * @desc: Description of power supply, must be valid through whole + * lifetime of this power supply + * @cfg: Run-time specific configuration accessed during registering, + * may be NULL + * + * Return: A pointer to newly allocated power_supply on success + * or ERR_PTR otherwise. + * Use power_supply_unregister() on returned power_supply pointer to release + * resources. + */ +struct power_supply *__must_check power_supply_register(struct device *parent, + const struct power_supply_desc *desc, + const struct power_supply_config *cfg) { - return __power_supply_register(parent, psy, true); + return __power_supply_register(parent, desc, cfg, true); } EXPORT_SYMBOL_GPL(power_supply_register); -int power_supply_register_no_ws(struct device *parent, struct power_supply *psy) +/** + * power_supply_register() - Register new non-waking-source power supply + * @parent: Device to be a parent of power supply's device + * @desc: Description of power supply, must be valid through whole + * lifetime of this power supply + * @cfg: Run-time specific configuration accessed during registering, + * may be NULL + * + * Return: A pointer to newly allocated power_supply on success + * or ERR_PTR otherwise. + * Use power_supply_unregister() on returned power_supply pointer to release + * resources. + */ +struct power_supply *__must_check +power_supply_register_no_ws(struct device *parent, + const struct power_supply_desc *desc, + const struct power_supply_config *cfg) { - return __power_supply_register(parent, psy, false); + return __power_supply_register(parent, desc, cfg, false); } EXPORT_SYMBOL_GPL(power_supply_register_no_ws); +static void devm_power_supply_release(struct device *dev, void *res) +{ + struct power_supply **psy = res; + + power_supply_unregister(*psy); +} + +/** + * power_supply_register() - Register managed power supply + * @parent: Device to be a parent of power supply's device + * @desc: Description of power supply, must be valid through whole + * lifetime of this power supply + * @cfg: Run-time specific configuration accessed during registering, + * may be NULL + * + * Return: A pointer to newly allocated power_supply on success + * or ERR_PTR otherwise. + * The returned power_supply pointer will be automatically unregistered + * on driver detach. + */ +struct power_supply *__must_check +devm_power_supply_register(struct device *parent, + const struct power_supply_desc *desc, + const struct power_supply_config *cfg) +{ + struct power_supply **ptr, *psy; + + ptr = devres_alloc(devm_power_supply_release, sizeof(*ptr), GFP_KERNEL); + + if (!ptr) + return ERR_PTR(-ENOMEM); + psy = __power_supply_register(parent, desc, cfg, true); + if (IS_ERR(psy)) { + devres_free(ptr); + } else { + *ptr = psy; + devres_add(parent, ptr); + } + return psy; +} +EXPORT_SYMBOL_GPL(devm_power_supply_register); + +/** + * power_supply_register() - Register managed non-waking-source power supply + * @parent: Device to be a parent of power supply's device + * @desc: Description of power supply, must be valid through whole + * lifetime of this power supply + * @cfg: Run-time specific configuration accessed during registering, + * may be NULL + * + * Return: A pointer to newly allocated power_supply on success + * or ERR_PTR otherwise. + * The returned power_supply pointer will be automatically unregistered + * on driver detach. + */ +struct power_supply *__must_check +devm_power_supply_register_no_ws(struct device *parent, + const struct power_supply_desc *desc, + const struct power_supply_config *cfg) +{ + struct power_supply **ptr, *psy; + + ptr = devres_alloc(devm_power_supply_release, sizeof(*ptr), GFP_KERNEL); + + if (!ptr) + return ERR_PTR(-ENOMEM); + psy = __power_supply_register(parent, desc, cfg, false); + if (IS_ERR(psy)) { + devres_free(ptr); + } else { + *ptr = psy; + devres_add(parent, ptr); + } + return psy; +} +EXPORT_SYMBOL_GPL(devm_power_supply_register_no_ws); + +/** + * power_supply_unregister() - Remove this power supply from system + * @psy: Pointer to power supply to unregister + * + * Remove this power supply from the system. The resources of power supply + * will be freed here or on last power_supply_put() call. + */ void power_supply_unregister(struct power_supply *psy) { + WARN_ON(atomic_dec_return(&psy->use_cnt)); cancel_work_sync(&psy->changed_work); - sysfs_remove_link(&psy->dev->kobj, "powers"); + sysfs_remove_link(&psy->dev.kobj, "powers"); power_supply_remove_triggers(psy); psy_unregister_cooler(psy); psy_unregister_thermal(psy); - device_init_wakeup(psy->dev, false); - device_unregister(psy->dev); + device_init_wakeup(&psy->dev, false); + device_unregister(&psy->dev); } EXPORT_SYMBOL_GPL(power_supply_unregister); +void *power_supply_get_drvdata(struct power_supply *psy) +{ + return psy->drv_data; +} +EXPORT_SYMBOL_GPL(power_supply_get_drvdata); + static int __init power_supply_class_init(void) { power_supply_class = class_create(THIS_MODULE, "power_supply"); diff --git a/drivers/power/power_supply_leds.c b/drivers/power/power_supply_leds.c index effa093..2d41a43 100644 --- a/drivers/power/power_supply_leds.c +++ b/drivers/power/power_supply_leds.c @@ -25,10 +25,10 @@ static void power_supply_update_bat_leds(struct power_supply *psy) unsigned long delay_on = 0; unsigned long delay_off = 0; - if (psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status)) + if (psy->desc->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status)) return; - dev_dbg(psy->dev, "%s %d\n", __func__, status.intval); + dev_dbg(&psy->dev, "%s %d\n", __func__, status.intval); switch (status.intval) { case POWER_SUPPLY_STATUS_FULL: @@ -58,21 +58,21 @@ static void power_supply_update_bat_leds(struct power_supply *psy) static int power_supply_create_bat_triggers(struct power_supply *psy) { psy->charging_full_trig_name = kasprintf(GFP_KERNEL, - "%s-charging-or-full", psy->name); + "%s-charging-or-full", psy->desc->name); if (!psy->charging_full_trig_name) goto charging_full_failed; psy->charging_trig_name = kasprintf(GFP_KERNEL, - "%s-charging", psy->name); + "%s-charging", psy->desc->name); if (!psy->charging_trig_name) goto charging_failed; - psy->full_trig_name = kasprintf(GFP_KERNEL, "%s-full", psy->name); + psy->full_trig_name = kasprintf(GFP_KERNEL, "%s-full", psy->desc->name); if (!psy->full_trig_name) goto full_failed; psy->charging_blink_full_solid_trig_name = kasprintf(GFP_KERNEL, - "%s-charging-blink-full-solid", psy->name); + "%s-charging-blink-full-solid", psy->desc->name); if (!psy->charging_blink_full_solid_trig_name) goto charging_blink_full_solid_failed; @@ -115,10 +115,10 @@ static void power_supply_update_gen_leds(struct power_supply *psy) { union power_supply_propval online; - if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online)) + if (psy->desc->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online)) return; - dev_dbg(psy->dev, "%s %d\n", __func__, online.intval); + dev_dbg(&psy->dev, "%s %d\n", __func__, online.intval); if (online.intval) led_trigger_event(psy->online_trig, LED_FULL); @@ -128,7 +128,8 @@ static void power_supply_update_gen_leds(struct power_supply *psy) static int power_supply_create_gen_triggers(struct power_supply *psy) { - psy->online_trig_name = kasprintf(GFP_KERNEL, "%s-online", psy->name); + psy->online_trig_name = kasprintf(GFP_KERNEL, "%s-online", + psy->desc->name); if (!psy->online_trig_name) return -ENOMEM; @@ -147,7 +148,7 @@ static void power_supply_remove_gen_triggers(struct power_supply *psy) void power_supply_update_leds(struct power_supply *psy) { - if (psy->type == POWER_SUPPLY_TYPE_BATTERY) + if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY) power_supply_update_bat_leds(psy); else power_supply_update_gen_leds(psy); @@ -155,14 +156,14 @@ void power_supply_update_leds(struct power_supply *psy) int power_supply_create_triggers(struct power_supply *psy) { - if (psy->type == POWER_SUPPLY_TYPE_BATTERY) + if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY) return power_supply_create_bat_triggers(psy); return power_supply_create_gen_triggers(psy); } void power_supply_remove_triggers(struct power_supply *psy) { - if (psy->type == POWER_SUPPLY_TYPE_BATTERY) + if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY) power_supply_remove_bat_triggers(psy); else power_supply_remove_gen_triggers(psy); diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 62653f5..9134e3d 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -74,9 +74,9 @@ static ssize_t power_supply_show_property(struct device *dev, union power_supply_propval value; if (off == POWER_SUPPLY_PROP_TYPE) { - value.intval = psy->type; + value.intval = psy->desc->type; } else { - ret = psy->get_property(psy, off, &value); + ret = power_supply_get_property(psy, off, &value); if (ret < 0) { if (ret == -ENODATA) @@ -125,7 +125,7 @@ static ssize_t power_supply_store_property(struct device *dev, value.intval = long_val; - ret = psy->set_property(psy, off, &value); + ret = psy->desc->set_property(psy, off, &value); if (ret < 0) return ret; @@ -218,12 +218,12 @@ static umode_t power_supply_attr_is_visible(struct kobject *kobj, if (attrno == POWER_SUPPLY_PROP_TYPE) return mode; - for (i = 0; i < psy->num_properties; i++) { - int property = psy->properties[i]; + for (i = 0; i < psy->desc->num_properties; i++) { + int property = psy->desc->properties[i]; if (property == attrno) { - if (psy->property_is_writeable && - psy->property_is_writeable(psy, property) > 0) + if (psy->desc->property_is_writeable && + power_supply_property_is_writeable(psy, property) > 0) mode |= S_IWUSR; return mode; @@ -279,14 +279,14 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env) dev_dbg(dev, "uevent\n"); - if (!psy || !psy->dev) { + if (!psy || !psy->desc) { dev_dbg(dev, "No power supply yet\n"); return ret; } - dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name); + dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->desc->name); - ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->name); + ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->desc->name); if (ret) return ret; @@ -294,11 +294,11 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env) if (!prop_buf) return -ENOMEM; - for (j = 0; j < psy->num_properties; j++) { + for (j = 0; j < psy->desc->num_properties; j++) { struct device_attribute *attr; char *line; - attr = &power_supply_attrs[psy->properties[j]]; + attr = &power_supply_attrs[psy->desc->properties[j]]; ret = power_supply_show_property(dev, attr, prop_buf); if (ret == -ENODEV || ret == -ENODATA) { diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 27f6646..aad9c33 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -151,9 +151,17 @@ config POWER_RESET_SYSCON help Reboot support for generic SYSCON mapped register reset. +config POWER_RESET_SYSCON_POWEROFF + bool "Generic SYSCON regmap poweroff driver" + depends on OF + select MFD_SYSCON + help + Poweroff support for generic SYSCON mapped register poweroff. + config POWER_RESET_RMOBILE tristate "Renesas R-Mobile reset driver" depends on ARCH_RMOBILE || COMPILE_TEST + depends on HAS_IOMEM help Reboot support for Renesas R-Mobile and SH-Mobile SoCs. diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 11de15b..dbe06c3 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -17,4 +17,5 @@ obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o +obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c index 4b72ea5..9847cfb 100644 --- a/drivers/power/reset/at91-poweroff.c +++ b/drivers/power/reset/at91-poweroff.c @@ -140,7 +140,7 @@ static int at91_poweroff_probe(struct platform_device *pdev) return 0; } -static struct of_device_id at91_poweroff_of_match[] = { +static const struct of_device_id at91_poweroff_of_match[] = { { .compatible = "atmel,at91sam9260-shdwc", }, { .compatible = "atmel,at91sam9rl-shdwc", }, { .compatible = "atmel,at91sam9x5-shdwc", }, diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 13584e2..01c7055 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -73,8 +73,8 @@ static int at91sam9260_restart(struct notifier_block *this, unsigned long mode, : "r" (at91_ramc_base[0]), "r" (at91_rstc_base), "r" (1), - "r" (AT91_SDRAMC_LPCB_POWER_DOWN), - "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)); + "r" cpu_to_le32(AT91_SDRAMC_LPCB_POWER_DOWN), + "r" cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)); return NOTIFY_DONE; } @@ -116,8 +116,8 @@ static int at91sam9g45_restart(struct notifier_block *this, unsigned long mode, "r" (at91_ramc_base[1]), "r" (at91_rstc_base), "r" (1), - "r" (AT91_DDRSDRC_LPCB_POWER_DOWN), - "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST) + "r" cpu_to_le32(AT91_DDRSDRC_LPCB_POWER_DOWN), + "r" cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST) : "r0"); return NOTIFY_DONE; @@ -152,14 +152,14 @@ static void __init at91_reset_status(struct platform_device *pdev) pr_info("AT91: Starting after %s\n", reason); } -static struct of_device_id at91_ramc_of_match[] = { +static const struct of_device_id at91_ramc_of_match[] = { { .compatible = "atmel,at91sam9260-sdramc", }, { .compatible = "atmel,at91sam9g45-ddramc", }, { .compatible = "atmel,sama5d3-ddramc", }, { /* sentinel */ } }; -static struct of_device_id at91_reset_of_match[] = { +static const struct of_device_id at91_reset_of_match[] = { { .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart }, { .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart }, { /* sentinel */ } diff --git a/drivers/power/reset/hisi-reboot.c b/drivers/power/reset/hisi-reboot.c index 5385460..9ab7f56 100644 --- a/drivers/power/reset/hisi-reboot.c +++ b/drivers/power/reset/hisi-reboot.c @@ -64,7 +64,7 @@ static int hisi_reboot_probe(struct platform_device *pdev) return err; } -static struct of_device_id hisi_reboot_of_match[] = { +static const struct of_device_id hisi_reboot_of_match[] = { { .compatible = "hisilicon,sysctrl" }, {} }; diff --git a/drivers/power/reset/keystone-reset.c b/drivers/power/reset/keystone-reset.c index faedf16..c70f1bf 100644 --- a/drivers/power/reset/keystone-reset.c +++ b/drivers/power/reset/keystone-reset.c @@ -70,7 +70,7 @@ static struct notifier_block rsctrl_restart_nb = { .priority = 128, }; -static struct of_device_id rsctrl_of_match[] = { +static const struct of_device_id rsctrl_of_match[] = { {.compatible = "ti,keystone-reset", }, {}, }; diff --git a/drivers/power/reset/st-poweroff.c b/drivers/power/reset/st-poweroff.c index 27383de..a488877 100644 --- a/drivers/power/reset/st-poweroff.c +++ b/drivers/power/reset/st-poweroff.c @@ -97,7 +97,7 @@ static struct notifier_block st_restart_nb = { .priority = 192, }; -static struct of_device_id st_reset_of_match[] = { +static const struct of_device_id st_reset_of_match[] = { { .compatible = "st,stih415-restart", .data = (void *)&stih415_reset, diff --git a/drivers/power/reset/syscon-poweroff.c b/drivers/power/reset/syscon-poweroff.c new file mode 100644 index 0000000..5560b0d --- /dev/null +++ b/drivers/power/reset/syscon-poweroff.c @@ -0,0 +1,102 @@ +/* + * Generic Syscon Poweroff Driver + * + * Copyright (c) 2015, National Instruments Corp. + * Author: Moritz Fischer <moritz.fischer@ettus.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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/kallsyms.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/notifier.h> +#include <linux/mfd/syscon.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/regmap.h> + +static struct regmap *map; +static u32 offset; +static u32 mask; + +void syscon_poweroff(void) +{ + /* Issue the poweroff */ + regmap_write(map, offset, mask); + + mdelay(1000); + + pr_emerg("Unable to poweroff system\n"); +} + +static int syscon_poweroff_probe(struct platform_device *pdev) +{ + char symname[KSYM_NAME_LEN]; + + map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap"); + if (IS_ERR(map)) { + dev_err(&pdev->dev, "unable to get syscon"); + return PTR_ERR(map); + } + + if (of_property_read_u32(pdev->dev.of_node, "offset", &offset)) { + dev_err(&pdev->dev, "unable to read 'offset'"); + return -EINVAL; + } + + if (of_property_read_u32(pdev->dev.of_node, "mask", &mask)) { + dev_err(&pdev->dev, "unable to read 'mask'"); + return -EINVAL; + } + + if (pm_power_off) { + lookup_symbol_name((ulong)pm_power_off, symname); + dev_err(&pdev->dev, + "pm_power_off already claimed %p %s", + pm_power_off, symname); + return -EBUSY; + } + + pm_power_off = syscon_poweroff; + + return 0; +} + +static int syscon_poweroff_remove(struct platform_device *pdev) +{ + if (pm_power_off == syscon_poweroff) + pm_power_off = NULL; + + return 0; +} + +static const struct of_device_id syscon_poweroff_of_match[] = { + { .compatible = "syscon-poweroff" }, + {} +}; + +static struct platform_driver syscon_poweroff_driver = { + .probe = syscon_poweroff_probe, + .remove = syscon_poweroff_remove, + .driver = { + .name = "syscon-poweroff", + .of_match_table = syscon_poweroff_of_match, + }, +}; + +static int __init syscon_poweroff_register(void) +{ + return platform_driver_register(&syscon_poweroff_driver); +} +device_initcall(syscon_poweroff_register); diff --git a/drivers/power/reset/syscon-reboot.c b/drivers/power/reset/syscon-reboot.c index c4049f4..d3c7d24 100644 --- a/drivers/power/reset/syscon-reboot.c +++ b/drivers/power/reset/syscon-reboot.c @@ -76,7 +76,7 @@ static int syscon_reboot_probe(struct platform_device *pdev) return err; } -static struct of_device_id syscon_reboot_of_match[] = { +static const struct of_device_id syscon_reboot_of_match[] = { { .compatible = "syscon-reboot" }, {} }; diff --git a/drivers/power/reset/vexpress-poweroff.c b/drivers/power/reset/vexpress-poweroff.c index be12d9b..6a9bf70 100644 --- a/drivers/power/reset/vexpress-poweroff.c +++ b/drivers/power/reset/vexpress-poweroff.c @@ -80,7 +80,7 @@ DEVICE_ATTR(active, S_IRUGO | S_IWUSR, vexpress_reset_active_show, enum vexpress_reset_func { FUNC_RESET, FUNC_SHUTDOWN, FUNC_REBOOT }; -static struct of_device_id vexpress_reset_of_match[] = { +static const struct of_device_id vexpress_reset_of_match[] = { { .compatible = "arm,vexpress-reset", .data = (void *)FUNC_RESET, diff --git a/drivers/power/reset/xgene-reboot.c b/drivers/power/reset/xgene-reboot.c index b0e5002..f07e93c 100644 --- a/drivers/power/reset/xgene-reboot.c +++ b/drivers/power/reset/xgene-reboot.c @@ -87,7 +87,7 @@ static int xgene_reboot_probe(struct platform_device *pdev) return err; } -static struct of_device_id xgene_reboot_of_match[] = { +static const struct of_device_id xgene_reboot_of_match[] = { { .compatible = "apm,xgene-reboot" }, {} }; diff --git a/drivers/power/rt5033_battery.c b/drivers/power/rt5033_battery.c index 7b898f4..a7a6877 100644 --- a/drivers/power/rt5033_battery.c +++ b/drivers/power/rt5033_battery.c @@ -72,8 +72,7 @@ static int rt5033_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct rt5033_battery *battery = container_of(psy, - struct rt5033_battery, psy); + struct rt5033_battery *battery = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_VOLTAGE_NOW: @@ -102,16 +101,25 @@ static enum power_supply_property rt5033_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY, }; -static struct regmap_config rt5033_battery_regmap_config = { +static const struct regmap_config rt5033_battery_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = RT5033_FUEL_REG_END, }; +static const struct power_supply_desc rt5033_battery_desc = { + .name = "rt5033-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = rt5033_battery_get_property, + .properties = rt5033_battery_props, + .num_properties = ARRAY_SIZE(rt5033_battery_props), +}; + static int rt5033_battery_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct power_supply_config psy_cfg = {}; struct rt5033_battery *battery; u32 ret; @@ -131,16 +139,13 @@ static int rt5033_battery_probe(struct i2c_client *client, } i2c_set_clientdata(client, battery); + psy_cfg.drv_data = battery; - battery->psy.name = "rt5033-battery"; - battery->psy.type = POWER_SUPPLY_TYPE_BATTERY; - battery->psy.get_property = rt5033_battery_get_property; - battery->psy.properties = rt5033_battery_props; - battery->psy.num_properties = ARRAY_SIZE(rt5033_battery_props); - - ret = power_supply_register(&client->dev, &battery->psy); - if (ret) { + battery->psy = power_supply_register(&client->dev, + &rt5033_battery_desc, &psy_cfg); + if (IS_ERR(battery->psy)) { dev_err(&client->dev, "Failed to register power supply\n"); + ret = PTR_ERR(battery->psy); return ret; } @@ -151,7 +156,7 @@ static int rt5033_battery_remove(struct i2c_client *client) { struct rt5033_battery *battery = i2c_get_clientdata(client); - power_supply_unregister(&battery->psy); + power_supply_unregister(battery->psy); return 0; } diff --git a/drivers/power/rx51_battery.c b/drivers/power/rx51_battery.c index a01aacb..ac62069 100644 --- a/drivers/power/rx51_battery.c +++ b/drivers/power/rx51_battery.c @@ -29,7 +29,8 @@ struct rx51_device_info { struct device *dev; - struct power_supply bat; + struct power_supply *bat; + struct power_supply_desc bat_desc; struct iio_channel *channel_temp; struct iio_channel *channel_bsi; struct iio_channel *channel_vbat; @@ -161,8 +162,7 @@ static int rx51_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct rx51_device_info *di = container_of((psy), - struct rx51_device_info, bat); + struct rx51_device_info *di = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_TECHNOLOGY: @@ -204,6 +204,7 @@ static enum power_supply_property rx51_battery_props[] = { static int rx51_battery_probe(struct platform_device *pdev) { + struct power_supply_config psy_cfg = {}; struct rx51_device_info *di; int ret; @@ -214,11 +215,13 @@ static int rx51_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, di); di->dev = &pdev->dev; - di->bat.name = dev_name(&pdev->dev); - di->bat.type = POWER_SUPPLY_TYPE_BATTERY; - di->bat.properties = rx51_battery_props; - di->bat.num_properties = ARRAY_SIZE(rx51_battery_props); - di->bat.get_property = rx51_battery_get_property; + di->bat_desc.name = dev_name(&pdev->dev); + di->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; + di->bat_desc.properties = rx51_battery_props; + di->bat_desc.num_properties = ARRAY_SIZE(rx51_battery_props); + di->bat_desc.get_property = rx51_battery_get_property; + + psy_cfg.drv_data = di; di->channel_temp = iio_channel_get(di->dev, "temp"); if (IS_ERR(di->channel_temp)) { @@ -238,9 +241,11 @@ static int rx51_battery_probe(struct platform_device *pdev) goto error_channel_bsi; } - ret = power_supply_register(di->dev, &di->bat); - if (ret) + di->bat = power_supply_register(di->dev, &di->bat_desc, &psy_cfg); + if (IS_ERR(di->bat)) { + ret = PTR_ERR(di->bat); goto error_channel_vbat; + } return 0; @@ -259,7 +264,7 @@ static int rx51_battery_remove(struct platform_device *pdev) { struct rx51_device_info *di = platform_get_drvdata(pdev); - power_supply_unregister(&di->bat); + power_supply_unregister(di->bat); iio_channel_release(di->channel_vbat); iio_channel_release(di->channel_bsi); diff --git a/drivers/power/s3c_adc_battery.c b/drivers/power/s3c_adc_battery.c index 5948ce0..0ffe5cd 100644 --- a/drivers/power/s3c_adc_battery.c +++ b/drivers/power/s3c_adc_battery.c @@ -28,7 +28,7 @@ #define JITTER_DELAY 500 /* ms */ struct s3c_adc_bat { - struct power_supply psy; + struct power_supply *psy; struct s3c_adc_client *client; struct s3c_adc_bat_pdata *pdata; int volt_value; @@ -73,10 +73,10 @@ static int s3c_adc_backup_bat_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy); + struct s3c_adc_bat *bat = power_supply_get_drvdata(psy); if (!bat) { - dev_err(psy->dev, "%s: no battery infos ?!\n", __func__); + dev_err(&psy->dev, "%s: no battery infos ?!\n", __func__); return -EINVAL; } @@ -105,17 +105,17 @@ static int s3c_adc_backup_bat_get_property(struct power_supply *psy, } } -static struct s3c_adc_bat backup_bat = { - .psy = { - .name = "backup-battery", - .type = POWER_SUPPLY_TYPE_BATTERY, - .properties = s3c_adc_backup_bat_props, - .num_properties = ARRAY_SIZE(s3c_adc_backup_bat_props), - .get_property = s3c_adc_backup_bat_get_property, - .use_for_apm = 1, - }, +static const struct power_supply_desc backup_bat_desc = { + .name = "backup-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = s3c_adc_backup_bat_props, + .num_properties = ARRAY_SIZE(s3c_adc_backup_bat_props), + .get_property = s3c_adc_backup_bat_get_property, + .use_for_apm = 1, }; +static struct s3c_adc_bat backup_bat; + static enum power_supply_property s3c_adc_main_bat_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, @@ -141,7 +141,7 @@ static int s3c_adc_bat_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy); + struct s3c_adc_bat *bat = power_supply_get_drvdata(psy); int new_level; int full_volt; @@ -149,7 +149,7 @@ static int s3c_adc_bat_get_property(struct power_supply *psy, unsigned int lut_size; if (!bat) { - dev_err(psy->dev, "no battery infos ?!\n"); + dev_err(&psy->dev, "no battery infos ?!\n"); return -EINVAL; } @@ -232,18 +232,18 @@ static int s3c_adc_bat_get_property(struct power_supply *psy, } } -static struct s3c_adc_bat main_bat = { - .psy = { - .name = "main-battery", - .type = POWER_SUPPLY_TYPE_BATTERY, - .properties = s3c_adc_main_bat_props, - .num_properties = ARRAY_SIZE(s3c_adc_main_bat_props), - .get_property = s3c_adc_bat_get_property, - .external_power_changed = s3c_adc_bat_ext_power_changed, - .use_for_apm = 1, - }, +static const struct power_supply_desc main_bat_desc = { + .name = "main-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = s3c_adc_main_bat_props, + .num_properties = ARRAY_SIZE(s3c_adc_main_bat_props), + .get_property = s3c_adc_bat_get_property, + .external_power_changed = s3c_adc_bat_ext_power_changed, + .use_for_apm = 1, }; +static struct s3c_adc_bat main_bat; + static void s3c_adc_bat_work(struct work_struct *work) { struct s3c_adc_bat *bat = &main_bat; @@ -251,7 +251,7 @@ static void s3c_adc_bat_work(struct work_struct *work) int is_plugged; static int was_plugged; - is_plugged = power_supply_am_i_supplied(&bat->psy); + is_plugged = power_supply_am_i_supplied(bat->psy); bat->cable_plugged = is_plugged; if (is_plugged != was_plugged) { was_plugged = is_plugged; @@ -279,7 +279,7 @@ static void s3c_adc_bat_work(struct work_struct *work) } } - power_supply_changed(&bat->psy); + power_supply_changed(bat->psy); } static irqreturn_t s3c_adc_bat_charged(int irq, void *dev_id) @@ -310,16 +310,25 @@ static int s3c_adc_bat_probe(struct platform_device *pdev) main_bat.cable_plugged = 0; main_bat.status = POWER_SUPPLY_STATUS_DISCHARGING; - ret = power_supply_register(&pdev->dev, &main_bat.psy); - if (ret) + main_bat.psy = power_supply_register(&pdev->dev, &main_bat_desc, NULL); + if (IS_ERR(main_bat.psy)) { + ret = PTR_ERR(main_bat.psy); goto err_reg_main; + } if (pdata->backup_volt_mult) { + const struct power_supply_config psy_cfg + = { .drv_data = &backup_bat, }; + backup_bat.client = client; backup_bat.pdata = pdev->dev.platform_data; backup_bat.volt_value = -1; - ret = power_supply_register(&pdev->dev, &backup_bat.psy); - if (ret) + backup_bat.psy = power_supply_register(&pdev->dev, + &backup_bat_desc, + &psy_cfg); + if (IS_ERR(backup_bat.psy)) { + ret = PTR_ERR(backup_bat.psy); goto err_reg_backup; + } } INIT_DELAYED_WORK(&bat_work, s3c_adc_bat_work); @@ -360,9 +369,9 @@ err_irq: gpio_free(pdata->gpio_charge_finished); err_gpio: if (pdata->backup_volt_mult) - power_supply_unregister(&backup_bat.psy); + power_supply_unregister(backup_bat.psy); err_reg_backup: - power_supply_unregister(&main_bat.psy); + power_supply_unregister(main_bat.psy); err_reg_main: return ret; } @@ -372,9 +381,9 @@ static int s3c_adc_bat_remove(struct platform_device *pdev) struct s3c_adc_client *client = platform_get_drvdata(pdev); struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data; - power_supply_unregister(&main_bat.psy); + power_supply_unregister(main_bat.psy); if (pdata->backup_volt_mult) - power_supply_unregister(&backup_bat.psy); + power_supply_unregister(backup_bat.psy); s3c_adc_release(client); diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c index c7b7b40..de11786 100644 --- a/drivers/power/sbs-battery.c +++ b/drivers/power/sbs-battery.c @@ -156,7 +156,7 @@ static enum power_supply_property sbs_properties[] = { struct sbs_info { struct i2c_client *client; - struct power_supply power_supply; + struct power_supply *power_supply; struct sbs_platform_data *pdata; bool is_present; bool gpio_detect; @@ -391,7 +391,7 @@ static int sbs_get_battery_property(struct i2c_client *client, chip->last_state = val->intval; else if (chip->last_state != val->intval) { cancel_delayed_work_sync(&chip->work); - power_supply_changed(&chip->power_supply); + power_supply_changed(chip->power_supply); chip->poll_time = 0; } } else { @@ -556,8 +556,7 @@ static int sbs_get_property(struct power_supply *psy, union power_supply_propval *val) { int ret = 0; - struct sbs_info *chip = container_of(psy, - struct sbs_info, power_supply); + struct sbs_info *chip = power_supply_get_drvdata(psy); struct i2c_client *client = chip->client; switch (psp) { @@ -638,7 +637,7 @@ static int sbs_get_property(struct power_supply *psy, if (!chip->gpio_detect && chip->is_present != (ret >= 0)) { chip->is_present = (ret >= 0); - power_supply_changed(&chip->power_supply); + power_supply_changed(chip->power_supply); } done: @@ -671,9 +670,7 @@ static irqreturn_t sbs_irq(int irq, void *devid) static void sbs_external_power_changed(struct power_supply *psy) { - struct sbs_info *chip; - - chip = container_of(psy, struct sbs_info, power_supply); + struct sbs_info *chip = power_supply_get_drvdata(psy); if (chip->ignore_changes > 0) { chip->ignore_changes--; @@ -712,7 +709,7 @@ static void sbs_delayed_work(struct work_struct *work) if (chip->last_state != ret) { chip->poll_time = 0; - power_supply_changed(&chip->power_supply); + power_supply_changed(chip->power_supply); return; } if (chip->poll_time > 0) { @@ -796,42 +793,48 @@ static struct sbs_platform_data *sbs_of_populate_pdata( } #endif +static const struct power_supply_desc sbs_default_desc = { + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = sbs_properties, + .num_properties = ARRAY_SIZE(sbs_properties), + .get_property = sbs_get_property, + .external_power_changed = sbs_external_power_changed, +}; + static int sbs_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct sbs_info *chip; + struct power_supply_desc *sbs_desc; struct sbs_platform_data *pdata = client->dev.platform_data; + struct power_supply_config psy_cfg = {}; int rc; int irq; - char *name; - name = kasprintf(GFP_KERNEL, "sbs-%s", dev_name(&client->dev)); - if (!name) { - dev_err(&client->dev, "Failed to allocate device name\n"); + sbs_desc = devm_kmemdup(&client->dev, &sbs_default_desc, + sizeof(*sbs_desc), GFP_KERNEL); + if (!sbs_desc) + return -ENOMEM; + + sbs_desc->name = devm_kasprintf(&client->dev, GFP_KERNEL, "sbs-%s", + dev_name(&client->dev)); + if (!sbs_desc->name) return -ENOMEM; - } chip = kzalloc(sizeof(struct sbs_info), GFP_KERNEL); - if (!chip) { - rc = -ENOMEM; - goto exit_free_name; - } + if (!chip) + return -ENOMEM; chip->client = client; chip->enable_detection = false; chip->gpio_detect = false; - chip->power_supply.name = name; - chip->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; - chip->power_supply.properties = sbs_properties; - chip->power_supply.num_properties = ARRAY_SIZE(sbs_properties); - chip->power_supply.get_property = sbs_get_property; - chip->power_supply.of_node = client->dev.of_node; + psy_cfg.of_node = client->dev.of_node; + psy_cfg.drv_data = chip; /* ignore first notification of external change, it is generated * from the power_supply_register call back */ chip->ignore_changes = 1; chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN; - chip->power_supply.external_power_changed = sbs_external_power_changed; pdata = sbs_of_populate_pdata(client); @@ -870,7 +873,7 @@ static int sbs_probe(struct i2c_client *client, rc = request_irq(irq, sbs_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - dev_name(&client->dev), &chip->power_supply); + dev_name(&client->dev), chip->power_supply); if (rc) { dev_warn(&client->dev, "Failed to request irq: %d\n", rc); gpio_free(pdata->battery_detect); @@ -892,10 +895,12 @@ skip_gpio: goto exit_psupply; } - rc = power_supply_register(&client->dev, &chip->power_supply); - if (rc) { + chip->power_supply = power_supply_register(&client->dev, sbs_desc, + &psy_cfg); + if (IS_ERR(chip->power_supply)) { dev_err(&client->dev, "%s: Failed to register power supply\n", __func__); + rc = PTR_ERR(chip->power_supply); goto exit_psupply; } @@ -910,15 +915,12 @@ skip_gpio: exit_psupply: if (chip->irq) - free_irq(chip->irq, &chip->power_supply); + free_irq(chip->irq, chip->power_supply); if (chip->gpio_detect) gpio_free(pdata->battery_detect); kfree(chip); -exit_free_name: - kfree(name); - return rc; } @@ -927,15 +929,14 @@ static int sbs_remove(struct i2c_client *client) struct sbs_info *chip = i2c_get_clientdata(client); if (chip->irq) - free_irq(chip->irq, &chip->power_supply); + free_irq(chip->irq, chip->power_supply); if (chip->gpio_detect) gpio_free(chip->pdata->battery_detect); - power_supply_unregister(&chip->power_supply); + power_supply_unregister(chip->power_supply); cancel_delayed_work_sync(&chip->work); - kfree(chip->power_supply.name); kfree(chip); chip = NULL; diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c index acf84e8..0b60a0b 100644 --- a/drivers/power/smb347-charger.c +++ b/drivers/power/smb347-charger.c @@ -139,9 +139,9 @@ struct smb347_charger { struct mutex lock; struct device *dev; struct regmap *regmap; - struct power_supply mains; - struct power_supply usb; - struct power_supply battery; + struct power_supply *mains; + struct power_supply *usb; + struct power_supply *battery; bool mains_online; bool usb_online; bool charging_enabled; @@ -741,7 +741,7 @@ static irqreturn_t smb347_interrupt(int irq, void *data) */ if (stat_c & STAT_C_CHARGER_ERROR) { dev_err(smb->dev, "charging stopped due to charger error\n"); - power_supply_changed(&smb->battery); + power_supply_changed(smb->battery); handled = true; } @@ -752,7 +752,7 @@ static irqreturn_t smb347_interrupt(int irq, void *data) */ if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) - power_supply_changed(&smb->battery); + power_supply_changed(smb->battery); dev_dbg(smb->dev, "going to HW maintenance mode\n"); handled = true; } @@ -766,7 +766,7 @@ static irqreturn_t smb347_interrupt(int irq, void *data) if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_STAT) dev_warn(smb->dev, "charging stopped due to timeout\n"); - power_supply_changed(&smb->battery); + power_supply_changed(smb->battery); handled = true; } @@ -778,9 +778,9 @@ static irqreturn_t smb347_interrupt(int irq, void *data) if (smb347_update_ps_status(smb) > 0) { smb347_start_stop_charging(smb); if (smb->pdata->use_mains) - power_supply_changed(&smb->mains); + power_supply_changed(smb->mains); if (smb->pdata->use_usb) - power_supply_changed(&smb->usb); + power_supply_changed(smb->usb); } handled = true; } @@ -842,7 +842,8 @@ static int smb347_irq_init(struct smb347_charger *smb, goto fail; ret = request_threaded_irq(irq, NULL, smb347_interrupt, - IRQF_TRIGGER_FALLING, client->name, smb); + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->name, smb); if (ret < 0) goto fail_gpio; @@ -934,8 +935,7 @@ static int smb347_mains_get_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) { - struct smb347_charger *smb = - container_of(psy, struct smb347_charger, mains); + struct smb347_charger *smb = power_supply_get_drvdata(psy); int ret; switch (prop) { @@ -976,8 +976,7 @@ static int smb347_usb_get_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) { - struct smb347_charger *smb = - container_of(psy, struct smb347_charger, usb); + struct smb347_charger *smb = power_supply_get_drvdata(psy); int ret; switch (prop) { @@ -1063,8 +1062,7 @@ static int smb347_battery_get_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) { - struct smb347_charger *smb = - container_of(psy, struct smb347_charger, battery); + struct smb347_charger *smb = power_supply_get_drvdata(psy); const struct smb347_charger_platform_data *pdata = smb->pdata; int ret; @@ -1188,11 +1186,36 @@ static const struct regmap_config smb347_regmap = { .readable_reg = smb347_readable_reg, }; +static const struct power_supply_desc smb347_mains_desc = { + .name = "smb347-mains", + .type = POWER_SUPPLY_TYPE_MAINS, + .get_property = smb347_mains_get_property, + .properties = smb347_mains_properties, + .num_properties = ARRAY_SIZE(smb347_mains_properties), +}; + +static const struct power_supply_desc smb347_usb_desc = { + .name = "smb347-usb", + .type = POWER_SUPPLY_TYPE_USB, + .get_property = smb347_usb_get_property, + .properties = smb347_usb_properties, + .num_properties = ARRAY_SIZE(smb347_usb_properties), +}; + +static const struct power_supply_desc smb347_battery_desc = { + .name = "smb347-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = smb347_battery_get_property, + .properties = smb347_battery_properties, + .num_properties = ARRAY_SIZE(smb347_battery_properties), +}; + static int smb347_probe(struct i2c_client *client, const struct i2c_device_id *id) { static char *battery[] = { "smb347-battery" }; const struct smb347_charger_platform_data *pdata; + struct power_supply_config mains_usb_cfg = {}, battery_cfg = {}; struct device *dev = &client->dev; struct smb347_charger *smb; int ret; @@ -1222,49 +1245,35 @@ static int smb347_probe(struct i2c_client *client, if (ret < 0) return ret; + mains_usb_cfg.supplied_to = battery; + mains_usb_cfg.num_supplicants = ARRAY_SIZE(battery); + mains_usb_cfg.drv_data = smb; if (smb->pdata->use_mains) { - smb->mains.name = "smb347-mains"; - smb->mains.type = POWER_SUPPLY_TYPE_MAINS; - smb->mains.get_property = smb347_mains_get_property; - smb->mains.properties = smb347_mains_properties; - smb->mains.num_properties = ARRAY_SIZE(smb347_mains_properties); - smb->mains.supplied_to = battery; - smb->mains.num_supplicants = ARRAY_SIZE(battery); - ret = power_supply_register(dev, &smb->mains); - if (ret < 0) - return ret; + smb->mains = power_supply_register(dev, &smb347_mains_desc, + &mains_usb_cfg); + if (IS_ERR(smb->mains)) + return PTR_ERR(smb->mains); } if (smb->pdata->use_usb) { - smb->usb.name = "smb347-usb"; - smb->usb.type = POWER_SUPPLY_TYPE_USB; - smb->usb.get_property = smb347_usb_get_property; - smb->usb.properties = smb347_usb_properties; - smb->usb.num_properties = ARRAY_SIZE(smb347_usb_properties); - smb->usb.supplied_to = battery; - smb->usb.num_supplicants = ARRAY_SIZE(battery); - ret = power_supply_register(dev, &smb->usb); - if (ret < 0) { + smb->usb = power_supply_register(dev, &smb347_usb_desc, + &mains_usb_cfg); + if (IS_ERR(smb->usb)) { if (smb->pdata->use_mains) - power_supply_unregister(&smb->mains); - return ret; + power_supply_unregister(smb->mains); + return PTR_ERR(smb->usb); } } - smb->battery.name = "smb347-battery"; - smb->battery.type = POWER_SUPPLY_TYPE_BATTERY; - smb->battery.get_property = smb347_battery_get_property; - smb->battery.properties = smb347_battery_properties; - smb->battery.num_properties = ARRAY_SIZE(smb347_battery_properties); - - - ret = power_supply_register(dev, &smb->battery); - if (ret < 0) { + battery_cfg.drv_data = smb; + smb->battery = power_supply_register(dev, &smb347_battery_desc, + &battery_cfg); + if (IS_ERR(smb->battery)) { if (smb->pdata->use_usb) - power_supply_unregister(&smb->usb); + power_supply_unregister(smb->usb); if (smb->pdata->use_mains) - power_supply_unregister(&smb->mains); - return ret; + power_supply_unregister(smb->mains); + return PTR_ERR(smb->battery); } /* @@ -1294,11 +1303,11 @@ static int smb347_remove(struct i2c_client *client) gpio_free(smb->pdata->irq_gpio); } - power_supply_unregister(&smb->battery); + power_supply_unregister(smb->battery); if (smb->pdata->use_usb) - power_supply_unregister(&smb->usb); + power_supply_unregister(smb->usb); if (smb->pdata->use_mains) - power_supply_unregister(&smb->mains); + power_supply_unregister(smb->mains); return 0; } diff --git a/drivers/power/test_power.c b/drivers/power/test_power.c index f26b1fa..f986e0c 100644 --- a/drivers/power/test_power.c +++ b/drivers/power/test_power.c @@ -153,12 +153,12 @@ static char *test_power_ac_supplied_to[] = { "test_battery", }; -static struct power_supply test_power_supplies[] = { +static struct power_supply *test_power_supplies[TEST_POWER_NUM]; + +static const struct power_supply_desc test_power_desc[] = { [TEST_AC] = { .name = "test_ac", .type = POWER_SUPPLY_TYPE_MAINS, - .supplied_to = test_power_ac_supplied_to, - .num_supplicants = ARRAY_SIZE(test_power_ac_supplied_to), .properties = test_power_ac_props, .num_properties = ARRAY_SIZE(test_power_ac_props), .get_property = test_power_get_ac_property, @@ -173,14 +173,25 @@ static struct power_supply test_power_supplies[] = { [TEST_USB] = { .name = "test_usb", .type = POWER_SUPPLY_TYPE_USB, - .supplied_to = test_power_ac_supplied_to, - .num_supplicants = ARRAY_SIZE(test_power_ac_supplied_to), .properties = test_power_ac_props, .num_properties = ARRAY_SIZE(test_power_ac_props), .get_property = test_power_get_usb_property, }, }; +static const struct power_supply_config test_power_configs[] = { + { + /* test_ac */ + .supplied_to = test_power_ac_supplied_to, + .num_supplicants = ARRAY_SIZE(test_power_ac_supplied_to), + }, { + /* test_battery */ + }, { + /* test_usb */ + .supplied_to = test_power_ac_supplied_to, + .num_supplicants = ARRAY_SIZE(test_power_ac_supplied_to), + }, +}; static int __init test_power_init(void) { @@ -188,12 +199,16 @@ static int __init test_power_init(void) int ret; BUILD_BUG_ON(TEST_POWER_NUM != ARRAY_SIZE(test_power_supplies)); + BUILD_BUG_ON(TEST_POWER_NUM != ARRAY_SIZE(test_power_configs)); for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) { - ret = power_supply_register(NULL, &test_power_supplies[i]); - if (ret) { + test_power_supplies[i] = power_supply_register(NULL, + &test_power_desc[i], + &test_power_configs[i]); + if (IS_ERR(test_power_supplies[i])) { pr_err("%s: failed to register %s\n", __func__, - test_power_supplies[i].name); + test_power_desc[i].name); + ret = PTR_ERR(test_power_supplies[i]); goto failed; } } @@ -202,7 +217,7 @@ static int __init test_power_init(void) return 0; failed: while (--i >= 0) - power_supply_unregister(&test_power_supplies[i]); + power_supply_unregister(test_power_supplies[i]); return ret; } module_init(test_power_init); @@ -216,13 +231,13 @@ static void __exit test_power_exit(void) usb_online = 0; battery_status = POWER_SUPPLY_STATUS_DISCHARGING; for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) - power_supply_changed(&test_power_supplies[i]); + power_supply_changed(test_power_supplies[i]); pr_info("%s: 'changed' event sent, sleeping for 10 seconds...\n", __func__); ssleep(10); for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) - power_supply_unregister(&test_power_supplies[i]); + power_supply_unregister(test_power_supplies[i]); module_initialized = false; } @@ -320,7 +335,7 @@ static inline void signal_power_supply_changed(struct power_supply *psy) static int param_set_ac_online(const char *key, const struct kernel_param *kp) { ac_online = map_get_value(map_ac_online, key, ac_online); - signal_power_supply_changed(&test_power_supplies[TEST_AC]); + signal_power_supply_changed(test_power_supplies[TEST_AC]); return 0; } @@ -333,7 +348,7 @@ static int param_get_ac_online(char *buffer, const struct kernel_param *kp) static int param_set_usb_online(const char *key, const struct kernel_param *kp) { usb_online = map_get_value(map_ac_online, key, usb_online); - signal_power_supply_changed(&test_power_supplies[TEST_USB]); + signal_power_supply_changed(test_power_supplies[TEST_USB]); return 0; } @@ -347,7 +362,7 @@ static int param_set_battery_status(const char *key, const struct kernel_param *kp) { battery_status = map_get_value(map_status, key, battery_status); - signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]); + signal_power_supply_changed(test_power_supplies[TEST_BATTERY]); return 0; } @@ -361,7 +376,7 @@ static int param_set_battery_health(const char *key, const struct kernel_param *kp) { battery_health = map_get_value(map_health, key, battery_health); - signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]); + signal_power_supply_changed(test_power_supplies[TEST_BATTERY]); return 0; } @@ -375,7 +390,7 @@ static int param_set_battery_present(const char *key, const struct kernel_param *kp) { battery_present = map_get_value(map_present, key, battery_present); - signal_power_supply_changed(&test_power_supplies[TEST_AC]); + signal_power_supply_changed(test_power_supplies[TEST_AC]); return 0; } @@ -391,7 +406,7 @@ static int param_set_battery_technology(const char *key, { battery_technology = map_get_value(map_technology, key, battery_technology); - signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]); + signal_power_supply_changed(test_power_supplies[TEST_BATTERY]); return 0; } @@ -412,7 +427,7 @@ static int param_set_battery_capacity(const char *key, return -EINVAL; battery_capacity = tmp; - signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]); + signal_power_supply_changed(test_power_supplies[TEST_BATTERY]); return 0; } @@ -427,7 +442,7 @@ static int param_set_battery_voltage(const char *key, return -EINVAL; battery_voltage = tmp; - signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]); + signal_power_supply_changed(test_power_supplies[TEST_BATTERY]); return 0; } diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c index f4d80df..6e88c1b 100644 --- a/drivers/power/tosa_battery.c +++ b/drivers/power/tosa_battery.c @@ -26,7 +26,7 @@ static struct work_struct bat_work; struct tosa_bat { int status; - struct power_supply psy; + struct power_supply *psy; int full_chrg; struct mutex work_lock; /* protects data */ @@ -61,7 +61,7 @@ static unsigned long tosa_read_bat(struct tosa_bat *bat) mutex_lock(&bat_lock); gpio_set_value(bat->gpio_bat, 1); msleep(5); - value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy.dev->parent), + value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy->dev.parent), bat->adc_bat); gpio_set_value(bat->gpio_bat, 0); mutex_unlock(&bat_lock); @@ -81,7 +81,7 @@ static unsigned long tosa_read_temp(struct tosa_bat *bat) mutex_lock(&bat_lock); gpio_set_value(bat->gpio_temp, 1); msleep(5); - value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy.dev->parent), + value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy->dev.parent), bat->adc_temp); gpio_set_value(bat->gpio_temp, 0); mutex_unlock(&bat_lock); @@ -96,7 +96,7 @@ static int tosa_bat_get_property(struct power_supply *psy, union power_supply_propval *val) { int ret = 0; - struct tosa_bat *bat = container_of(psy, struct tosa_bat, psy); + struct tosa_bat *bat = power_supply_get_drvdata(psy); if (bat->is_present && !bat->is_present(bat) && psp != POWER_SUPPLY_PROP_PRESENT) { @@ -158,14 +158,14 @@ static irqreturn_t tosa_bat_gpio_isr(int irq, void *data) static void tosa_bat_update(struct tosa_bat *bat) { int old; - struct power_supply *psy = &bat->psy; + struct power_supply *psy = bat->psy; mutex_lock(&bat->work_lock); old = bat->status; if (bat->is_present && !bat->is_present(bat)) { - printk(KERN_NOTICE "%s not present\n", psy->name); + printk(KERN_NOTICE "%s not present\n", psy->desc->name); bat->status = POWER_SUPPLY_STATUS_UNKNOWN; bat->full_chrg = -1; } else if (power_supply_am_i_supplied(psy)) { @@ -222,18 +222,38 @@ static enum power_supply_property tosa_bat_bu_props[] = { POWER_SUPPLY_PROP_PRESENT, }; +static const struct power_supply_desc tosa_bat_main_desc = { + .name = "main-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = tosa_bat_main_props, + .num_properties = ARRAY_SIZE(tosa_bat_main_props), + .get_property = tosa_bat_get_property, + .external_power_changed = tosa_bat_external_power_changed, + .use_for_apm = 1, +}; + +static const struct power_supply_desc tosa_bat_jacket_desc = { + .name = "jacket-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = tosa_bat_main_props, + .num_properties = ARRAY_SIZE(tosa_bat_main_props), + .get_property = tosa_bat_get_property, + .external_power_changed = tosa_bat_external_power_changed, +}; + +static const struct power_supply_desc tosa_bat_bu_desc = { + .name = "backup-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = tosa_bat_bu_props, + .num_properties = ARRAY_SIZE(tosa_bat_bu_props), + .get_property = tosa_bat_get_property, + .external_power_changed = tosa_bat_external_power_changed, +}; + static struct tosa_bat tosa_bat_main = { .status = POWER_SUPPLY_STATUS_DISCHARGING, .full_chrg = -1, - .psy = { - .name = "main-battery", - .type = POWER_SUPPLY_TYPE_BATTERY, - .properties = tosa_bat_main_props, - .num_properties = ARRAY_SIZE(tosa_bat_main_props), - .get_property = tosa_bat_get_property, - .external_power_changed = tosa_bat_external_power_changed, - .use_for_apm = 1, - }, + .psy = NULL, .gpio_full = TOSA_GPIO_BAT0_CRG, .gpio_charge_off = TOSA_GPIO_CHARGE_OFF, @@ -254,14 +274,7 @@ static struct tosa_bat tosa_bat_main = { static struct tosa_bat tosa_bat_jacket = { .status = POWER_SUPPLY_STATUS_DISCHARGING, .full_chrg = -1, - .psy = { - .name = "jacket-battery", - .type = POWER_SUPPLY_TYPE_BATTERY, - .properties = tosa_bat_main_props, - .num_properties = ARRAY_SIZE(tosa_bat_main_props), - .get_property = tosa_bat_get_property, - .external_power_changed = tosa_bat_external_power_changed, - }, + .psy = NULL, .is_present = tosa_jacket_bat_is_present, .gpio_full = TOSA_GPIO_BAT1_CRG, @@ -283,15 +296,7 @@ static struct tosa_bat tosa_bat_jacket = { static struct tosa_bat tosa_bat_bu = { .status = POWER_SUPPLY_STATUS_UNKNOWN, .full_chrg = -1, - - .psy = { - .name = "backup-battery", - .type = POWER_SUPPLY_TYPE_BATTERY, - .properties = tosa_bat_bu_props, - .num_properties = ARRAY_SIZE(tosa_bat_bu_props), - .get_property = tosa_bat_get_property, - .external_power_changed = tosa_bat_external_power_changed, - }, + .psy = NULL, .gpio_full = -1, .gpio_charge_off = -1, @@ -345,6 +350,9 @@ static int tosa_bat_resume(struct platform_device *dev) static int tosa_bat_probe(struct platform_device *dev) { int ret; + struct power_supply_config main_psy_cfg = {}, + jacket_psy_cfg = {}, + bu_psy_cfg = {}; if (!machine_is_tosa()) return -ENODEV; @@ -358,15 +366,31 @@ static int tosa_bat_probe(struct platform_device *dev) INIT_WORK(&bat_work, tosa_bat_work); - ret = power_supply_register(&dev->dev, &tosa_bat_main.psy); - if (ret) + main_psy_cfg.drv_data = &tosa_bat_main; + tosa_bat_main.psy = power_supply_register(&dev->dev, + &tosa_bat_main_desc, + &main_psy_cfg); + if (IS_ERR(tosa_bat_main.psy)) { + ret = PTR_ERR(tosa_bat_main.psy); goto err_psy_reg_main; - ret = power_supply_register(&dev->dev, &tosa_bat_jacket.psy); - if (ret) + } + + jacket_psy_cfg.drv_data = &tosa_bat_jacket; + tosa_bat_jacket.psy = power_supply_register(&dev->dev, + &tosa_bat_jacket_desc, + &jacket_psy_cfg); + if (IS_ERR(tosa_bat_jacket.psy)) { + ret = PTR_ERR(tosa_bat_jacket.psy); goto err_psy_reg_jacket; - ret = power_supply_register(&dev->dev, &tosa_bat_bu.psy); - if (ret) + } + + bu_psy_cfg.drv_data = &tosa_bat_bu; + tosa_bat_bu.psy = power_supply_register(&dev->dev, &tosa_bat_bu_desc, + &bu_psy_cfg); + if (IS_ERR(tosa_bat_bu.psy)) { + ret = PTR_ERR(tosa_bat_bu.psy); goto err_psy_reg_bu; + } ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), tosa_bat_gpio_isr, @@ -395,11 +419,11 @@ static int tosa_bat_probe(struct platform_device *dev) err_req_jacket: free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main); err_req_main: - power_supply_unregister(&tosa_bat_bu.psy); + power_supply_unregister(tosa_bat_bu.psy); err_psy_reg_bu: - power_supply_unregister(&tosa_bat_jacket.psy); + power_supply_unregister(tosa_bat_jacket.psy); err_psy_reg_jacket: - power_supply_unregister(&tosa_bat_main.psy); + power_supply_unregister(tosa_bat_main.psy); err_psy_reg_main: /* see comment in tosa_bat_remove */ @@ -415,9 +439,9 @@ static int tosa_bat_remove(struct platform_device *dev) free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket); free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main); - power_supply_unregister(&tosa_bat_bu.psy); - power_supply_unregister(&tosa_bat_jacket.psy); - power_supply_unregister(&tosa_bat_main.psy); + power_supply_unregister(tosa_bat_bu.psy); + power_supply_unregister(tosa_bat_jacket.psy); + power_supply_unregister(tosa_bat_main.psy); /* * Now cancel the bat_work. We won't get any more schedules, diff --git a/drivers/power/tps65090-charger.c b/drivers/power/tps65090-charger.c index 0f4e597..7e8fbd2 100644 --- a/drivers/power/tps65090-charger.c +++ b/drivers/power/tps65090-charger.c @@ -43,7 +43,7 @@ struct tps65090_charger { int irq; struct task_struct *poll_task; bool passive_mode; - struct power_supply ac; + struct power_supply *ac; struct tps65090_platform_data *pdata; }; @@ -135,8 +135,7 @@ static int tps65090_ac_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct tps65090_charger *charger = container_of(psy, - struct tps65090_charger, ac); + struct tps65090_charger *charger = power_supply_get_drvdata(psy); if (psp == POWER_SUPPLY_PROP_ONLINE) { val->intval = charger->ac_online; @@ -190,7 +189,7 @@ static irqreturn_t tps65090_charger_isr(int irq, void *dev_id) } if (charger->prev_ac_online != charger->ac_online) - power_supply_changed(&charger->ac); + power_supply_changed(charger->ac); return IRQ_HANDLED; } @@ -229,10 +228,19 @@ static int tps65090_charger_poll_task(void *data) return 0; } +static const struct power_supply_desc tps65090_charger_desc = { + .name = "tps65090-ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .get_property = tps65090_ac_get_property, + .properties = tps65090_ac_props, + .num_properties = ARRAY_SIZE(tps65090_ac_props), +}; + static int tps65090_charger_probe(struct platform_device *pdev) { struct tps65090_charger *cdata; struct tps65090_platform_data *pdata; + struct power_supply_config psy_cfg = {}; uint8_t status1 = 0; int ret; int irq; @@ -259,19 +267,16 @@ static int tps65090_charger_probe(struct platform_device *pdev) cdata->dev = &pdev->dev; cdata->pdata = pdata; - cdata->ac.name = "tps65090-ac"; - cdata->ac.type = POWER_SUPPLY_TYPE_MAINS; - cdata->ac.get_property = tps65090_ac_get_property; - cdata->ac.properties = tps65090_ac_props; - cdata->ac.num_properties = ARRAY_SIZE(tps65090_ac_props); - cdata->ac.supplied_to = pdata->supplied_to; - cdata->ac.num_supplicants = pdata->num_supplicants; - cdata->ac.of_node = pdev->dev.of_node; - - ret = power_supply_register(&pdev->dev, &cdata->ac); - if (ret) { + psy_cfg.supplied_to = pdata->supplied_to; + psy_cfg.num_supplicants = pdata->num_supplicants; + psy_cfg.of_node = pdev->dev.of_node; + psy_cfg.drv_data = cdata; + + cdata->ac = power_supply_register(&pdev->dev, &tps65090_charger_desc, + &psy_cfg); + if (IS_ERR(cdata->ac)) { dev_err(&pdev->dev, "failed: power supply register\n"); - return ret; + return PTR_ERR(cdata->ac); } irq = platform_get_irq(pdev, 0); @@ -301,7 +306,7 @@ static int tps65090_charger_probe(struct platform_device *pdev) goto fail_unregister_supply; } cdata->ac_online = 1; - power_supply_changed(&cdata->ac); + power_supply_changed(cdata->ac); } if (irq != -ENXIO) { @@ -328,7 +333,7 @@ static int tps65090_charger_probe(struct platform_device *pdev) return 0; fail_unregister_supply: - power_supply_unregister(&cdata->ac); + power_supply_unregister(cdata->ac); return ret; } @@ -339,12 +344,12 @@ static int tps65090_charger_remove(struct platform_device *pdev) if (cdata->irq == -ENXIO) kthread_stop(cdata->poll_task); - power_supply_unregister(&cdata->ac); + power_supply_unregister(cdata->ac); return 0; } -static struct of_device_id of_tps65090_charger_match[] = { +static const struct of_device_id of_tps65090_charger_match[] = { { .compatible = "ti,tps65090-charger", }, { /* end */ } }; diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index d35b83e..02a522c 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -87,8 +87,8 @@ MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current"); struct twl4030_bci { struct device *dev; - struct power_supply ac; - struct power_supply usb; + struct power_supply *ac; + struct power_supply *usb; struct usb_phy *transceiver; struct notifier_block usb_nb; struct work_struct work; @@ -318,8 +318,8 @@ static irqreturn_t twl4030_charger_interrupt(int irq, void *arg) struct twl4030_bci *bci = arg; dev_dbg(bci->dev, "CHG_PRES irq\n"); - power_supply_changed(&bci->ac); - power_supply_changed(&bci->usb); + power_supply_changed(bci->ac); + power_supply_changed(bci->usb); return IRQ_HANDLED; } @@ -347,8 +347,8 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg) if (irqs1 & (TWL4030_ICHGLOW | TWL4030_ICHGEOC)) { /* charger state change, inform the core */ - power_supply_changed(&bci->ac); - power_supply_changed(&bci->usb); + power_supply_changed(bci->ac); + power_supply_changed(bci->usb); } /* various monitoring events, for now we just log them here */ @@ -463,7 +463,7 @@ static int twl4030_bci_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct twl4030_bci *bci = dev_get_drvdata(psy->dev->parent); + struct twl4030_bci *bci = dev_get_drvdata(psy->dev.parent); int is_charging; int state; int ret; @@ -472,7 +472,7 @@ static int twl4030_bci_get_property(struct power_supply *psy, if (state < 0) return state; - if (psy->type == POWER_SUPPLY_TYPE_USB) + if (psy->desc->type == POWER_SUPPLY_TYPE_USB) is_charging = state & TWL4030_MSTATEC_USB; else is_charging = state & TWL4030_MSTATEC_AC; @@ -488,7 +488,7 @@ static int twl4030_bci_get_property(struct power_supply *psy, /* charging must be active for meaningful result */ if (!is_charging) return -ENODATA; - if (psy->type == POWER_SUPPLY_TYPE_USB) { + if (psy->desc->type == POWER_SUPPLY_TYPE_USB) { ret = twl4030bci_read_adc_val(TWL4030_BCIVBUS); if (ret < 0) return ret; @@ -558,6 +558,22 @@ twl4030_bci_parse_dt(struct device *dev) } #endif +static const struct power_supply_desc twl4030_bci_ac_desc = { + .name = "twl4030_ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = twl4030_charger_props, + .num_properties = ARRAY_SIZE(twl4030_charger_props), + .get_property = twl4030_bci_get_property, +}; + +static const struct power_supply_desc twl4030_bci_usb_desc = { + .name = "twl4030_usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = twl4030_charger_props, + .num_properties = ARRAY_SIZE(twl4030_charger_props), + .get_property = twl4030_bci_get_property, +}; + static int __init twl4030_bci_probe(struct platform_device *pdev) { struct twl4030_bci *bci; @@ -584,28 +600,21 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, bci); - bci->ac.name = "twl4030_ac"; - bci->ac.type = POWER_SUPPLY_TYPE_MAINS; - bci->ac.properties = twl4030_charger_props; - bci->ac.num_properties = ARRAY_SIZE(twl4030_charger_props); - bci->ac.get_property = twl4030_bci_get_property; - ret = power_supply_register(&pdev->dev, &bci->ac); - if (ret) { + bci->ac = power_supply_register(&pdev->dev, &twl4030_bci_ac_desc, + NULL); + if (IS_ERR(bci->ac)) { + ret = PTR_ERR(bci->ac); dev_err(&pdev->dev, "failed to register ac: %d\n", ret); goto fail_register_ac; } - bci->usb.name = "twl4030_usb"; - bci->usb.type = POWER_SUPPLY_TYPE_USB; - bci->usb.properties = twl4030_charger_props; - bci->usb.num_properties = ARRAY_SIZE(twl4030_charger_props); - bci->usb.get_property = twl4030_bci_get_property; - bci->usb_reg = regulator_get(bci->dev, "bci3v1"); - ret = power_supply_register(&pdev->dev, &bci->usb); - if (ret) { + bci->usb = power_supply_register(&pdev->dev, &twl4030_bci_usb_desc, + NULL); + if (IS_ERR(bci->usb)) { + ret = PTR_ERR(bci->usb); dev_err(&pdev->dev, "failed to register usb: %d\n", ret); goto fail_register_usb; } @@ -670,9 +679,9 @@ fail_unmask_interrupts: fail_bci_irq: free_irq(bci->irq_chg, bci); fail_chg_irq: - power_supply_unregister(&bci->usb); + power_supply_unregister(bci->usb); fail_register_usb: - power_supply_unregister(&bci->ac); + power_supply_unregister(bci->ac); fail_register_ac: fail_no_battery: kfree(bci); @@ -700,8 +709,8 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) } free_irq(bci->irq_bci, bci); free_irq(bci->irq_chg, bci); - power_supply_unregister(&bci->usb); - power_supply_unregister(&bci->ac); + power_supply_unregister(bci->usb); + power_supply_unregister(bci->ac); kfree(bci); return 0; diff --git a/drivers/power/twl4030_madc_battery.c b/drivers/power/twl4030_madc_battery.c index 7ef445a..f5817e4 100644 --- a/drivers/power/twl4030_madc_battery.c +++ b/drivers/power/twl4030_madc_battery.c @@ -19,10 +19,14 @@ #include <linux/sort.h> #include <linux/i2c/twl4030-madc.h> #include <linux/power/twl4030_madc_battery.h> +#include <linux/iio/consumer.h> struct twl4030_madc_battery { - struct power_supply psy; + struct power_supply *psy; struct twl4030_madc_bat_platform_data *pdata; + struct iio_channel *channel_temp; + struct iio_channel *channel_ichg; + struct iio_channel *channel_vbat; }; static enum power_supply_property twl4030_madc_bat_props[] = { @@ -38,43 +42,34 @@ static enum power_supply_property twl4030_madc_bat_props[] = { POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, }; -static int madc_read(int index) +static int madc_read(struct iio_channel *channel) { - struct twl4030_madc_request req; - int val; + int val, err; + err = iio_read_channel_processed(channel, &val); + if (err < 0) + return err; - req.channels = index; - req.method = TWL4030_MADC_SW2; - req.type = TWL4030_MADC_WAIT; - req.do_avg = 0; - req.raw = false; - req.func_cb = NULL; - - val = twl4030_madc_conversion(&req); - if (val < 0) - return val; - - return req.rbuf[ffs(index) - 1]; + return val; } -static int twl4030_madc_bat_get_charging_status(void) +static int twl4030_madc_bat_get_charging_status(struct twl4030_madc_battery *bt) { - return (madc_read(TWL4030_MADC_ICHG) > 0) ? 1 : 0; + return (madc_read(bt->channel_ichg) > 0) ? 1 : 0; } -static int twl4030_madc_bat_get_voltage(void) +static int twl4030_madc_bat_get_voltage(struct twl4030_madc_battery *bt) { - return madc_read(TWL4030_MADC_VBAT); + return madc_read(bt->channel_vbat); } -static int twl4030_madc_bat_get_current(void) +static int twl4030_madc_bat_get_current(struct twl4030_madc_battery *bt) { - return madc_read(TWL4030_MADC_ICHG) * 1000; + return madc_read(bt->channel_ichg) * 1000; } -static int twl4030_madc_bat_get_temp(void) +static int twl4030_madc_bat_get_temp(struct twl4030_madc_battery *bt) { - return madc_read(TWL4030_MADC_BTEMP) * 10; + return madc_read(bt->channel_temp) * 10; } static int twl4030_madc_bat_voltscale(struct twl4030_madc_battery *bat, @@ -84,7 +79,7 @@ static int twl4030_madc_bat_voltscale(struct twl4030_madc_battery *bat, int i, res = 0; /* choose charging curve */ - if (twl4030_madc_bat_get_charging_status()) + if (twl4030_madc_bat_get_charging_status(bat)) calibration = bat->pdata->charging; else calibration = bat->pdata->discharging; @@ -113,29 +108,28 @@ static int twl4030_madc_bat_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct twl4030_madc_battery *bat = container_of(psy, - struct twl4030_madc_battery, psy); + struct twl4030_madc_battery *bat = power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_STATUS: if (twl4030_madc_bat_voltscale(bat, - twl4030_madc_bat_get_voltage()) > 95) + twl4030_madc_bat_get_voltage(bat)) > 95) val->intval = POWER_SUPPLY_STATUS_FULL; else { - if (twl4030_madc_bat_get_charging_status()) + if (twl4030_madc_bat_get_charging_status(bat)) val->intval = POWER_SUPPLY_STATUS_CHARGING; else val->intval = POWER_SUPPLY_STATUS_DISCHARGING; } break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = twl4030_madc_bat_get_voltage() * 1000; + val->intval = twl4030_madc_bat_get_voltage(bat) * 1000; break; case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = POWER_SUPPLY_TECHNOLOGY_LION; break; case POWER_SUPPLY_PROP_CURRENT_NOW: - val->intval = twl4030_madc_bat_get_current(); + val->intval = twl4030_madc_bat_get_current(bat); break; case POWER_SUPPLY_PROP_PRESENT: /* assume battery is always present */ @@ -143,23 +137,23 @@ static int twl4030_madc_bat_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CHARGE_NOW: { int percent = twl4030_madc_bat_voltscale(bat, - twl4030_madc_bat_get_voltage()); + twl4030_madc_bat_get_voltage(bat)); val->intval = (percent * bat->pdata->capacity) / 100; break; } case POWER_SUPPLY_PROP_CAPACITY: val->intval = twl4030_madc_bat_voltscale(bat, - twl4030_madc_bat_get_voltage()); + twl4030_madc_bat_get_voltage(bat)); break; case POWER_SUPPLY_PROP_CHARGE_FULL: val->intval = bat->pdata->capacity; break; case POWER_SUPPLY_PROP_TEMP: - val->intval = twl4030_madc_bat_get_temp(); + val->intval = twl4030_madc_bat_get_temp(bat); break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: { int percent = twl4030_madc_bat_voltscale(bat, - twl4030_madc_bat_get_voltage()); + twl4030_madc_bat_get_voltage(bat)); /* in mAh */ int chg = (percent * (bat->pdata->capacity/1000))/100; @@ -176,12 +170,19 @@ static int twl4030_madc_bat_get_property(struct power_supply *psy, static void twl4030_madc_bat_ext_changed(struct power_supply *psy) { - struct twl4030_madc_battery *bat = container_of(psy, - struct twl4030_madc_battery, psy); - - power_supply_changed(&bat->psy); + power_supply_changed(psy); } +static const struct power_supply_desc twl4030_madc_bat_desc = { + .name = "twl4030_battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = twl4030_madc_bat_props, + .num_properties = ARRAY_SIZE(twl4030_madc_bat_props), + .get_property = twl4030_madc_bat_get_property, + .external_power_changed = twl4030_madc_bat_ext_changed, + +}; + static int twl4030_cmp(const void *a, const void *b) { return ((struct twl4030_madc_bat_calibration *)b)->voltage - @@ -192,19 +193,31 @@ static int twl4030_madc_battery_probe(struct platform_device *pdev) { struct twl4030_madc_battery *twl4030_madc_bat; struct twl4030_madc_bat_platform_data *pdata = pdev->dev.platform_data; + struct power_supply_config psy_cfg = {}; + int ret = 0; - twl4030_madc_bat = kzalloc(sizeof(*twl4030_madc_bat), GFP_KERNEL); + twl4030_madc_bat = devm_kzalloc(&pdev->dev, sizeof(*twl4030_madc_bat), + GFP_KERNEL); if (!twl4030_madc_bat) return -ENOMEM; - twl4030_madc_bat->psy.name = "twl4030_battery"; - twl4030_madc_bat->psy.type = POWER_SUPPLY_TYPE_BATTERY; - twl4030_madc_bat->psy.properties = twl4030_madc_bat_props; - twl4030_madc_bat->psy.num_properties = - ARRAY_SIZE(twl4030_madc_bat_props); - twl4030_madc_bat->psy.get_property = twl4030_madc_bat_get_property; - twl4030_madc_bat->psy.external_power_changed = - twl4030_madc_bat_ext_changed; + twl4030_madc_bat->channel_temp = iio_channel_get(&pdev->dev, "temp"); + if (IS_ERR(twl4030_madc_bat->channel_temp)) { + ret = PTR_ERR(twl4030_madc_bat->channel_temp); + goto err; + } + + twl4030_madc_bat->channel_ichg = iio_channel_get(&pdev->dev, "ichg"); + if (IS_ERR(twl4030_madc_bat->channel_ichg)) { + ret = PTR_ERR(twl4030_madc_bat->channel_ichg); + goto err_temp; + } + + twl4030_madc_bat->channel_vbat = iio_channel_get(&pdev->dev, "vbat"); + if (IS_ERR(twl4030_madc_bat->channel_vbat)) { + ret = PTR_ERR(twl4030_madc_bat->channel_vbat); + goto err_ichg; + } /* sort charging and discharging calibration data */ sort(pdata->charging, pdata->charging_size, @@ -216,17 +229,36 @@ static int twl4030_madc_battery_probe(struct platform_device *pdev) twl4030_madc_bat->pdata = pdata; platform_set_drvdata(pdev, twl4030_madc_bat); - power_supply_register(&pdev->dev, &twl4030_madc_bat->psy); + psy_cfg.drv_data = twl4030_madc_bat; + twl4030_madc_bat->psy = power_supply_register(&pdev->dev, + &twl4030_madc_bat_desc, + &psy_cfg); + if (IS_ERR(twl4030_madc_bat->psy)) { + ret = PTR_ERR(twl4030_madc_bat->psy); + goto err_vbat; + } return 0; + +err_vbat: + iio_channel_release(twl4030_madc_bat->channel_vbat); +err_ichg: + iio_channel_release(twl4030_madc_bat->channel_ichg); +err_temp: + iio_channel_release(twl4030_madc_bat->channel_temp); +err: + return ret; } static int twl4030_madc_battery_remove(struct platform_device *pdev) { struct twl4030_madc_battery *bat = platform_get_drvdata(pdev); - power_supply_unregister(&bat->psy); - kfree(bat); + power_supply_unregister(bat->psy); + + iio_channel_release(bat->channel_vbat); + iio_channel_release(bat->channel_ichg); + iio_channel_release(bat->channel_temp); return 0; } @@ -243,3 +275,4 @@ module_platform_driver(twl4030_madc_battery_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Lukas Märdian <lukas@goldelico.com>"); MODULE_DESCRIPTION("twl4030_madc battery driver"); +MODULE_ALIAS("platform:twl4030_madc_battery"); diff --git a/drivers/power/wm831x_backup.c b/drivers/power/wm831x_backup.c index 56fb509..2e33109 100644 --- a/drivers/power/wm831x_backup.c +++ b/drivers/power/wm831x_backup.c @@ -21,7 +21,8 @@ struct wm831x_backup { struct wm831x *wm831x; - struct power_supply backup; + struct power_supply *backup; + struct power_supply_desc backup_desc; char name[20]; }; @@ -115,7 +116,7 @@ static int wm831x_backup_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct wm831x_backup *devdata = dev_get_drvdata(psy->dev->parent); + struct wm831x_backup *devdata = dev_get_drvdata(psy->dev.parent); struct wm831x *wm831x = devdata->wm831x; int ret = 0; @@ -166,8 +167,6 @@ static int wm831x_backup_probe(struct platform_device *pdev) struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data; struct wm831x_backup *devdata; - struct power_supply *backup; - int ret; devdata = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_backup), GFP_KERNEL); @@ -177,8 +176,6 @@ static int wm831x_backup_probe(struct platform_device *pdev) devdata->wm831x = wm831x; platform_set_drvdata(pdev, devdata); - backup = &devdata->backup; - /* We ignore configuration failures since we can still read * back the status without enabling the charger (which may * already be enabled anyway). @@ -192,21 +189,22 @@ static int wm831x_backup_probe(struct platform_device *pdev) snprintf(devdata->name, sizeof(devdata->name), "wm831x-backup"); - backup->name = devdata->name; - backup->type = POWER_SUPPLY_TYPE_BATTERY; - backup->properties = wm831x_backup_props; - backup->num_properties = ARRAY_SIZE(wm831x_backup_props); - backup->get_property = wm831x_backup_get_prop; - ret = power_supply_register(&pdev->dev, backup); + devdata->backup_desc.name = devdata->name; + devdata->backup_desc.type = POWER_SUPPLY_TYPE_BATTERY; + devdata->backup_desc.properties = wm831x_backup_props; + devdata->backup_desc.num_properties = ARRAY_SIZE(wm831x_backup_props); + devdata->backup_desc.get_property = wm831x_backup_get_prop; + devdata->backup = power_supply_register(&pdev->dev, + &devdata->backup_desc, NULL); - return ret; + return PTR_ERR_OR_ZERO(devdata->backup); } static int wm831x_backup_remove(struct platform_device *pdev) { struct wm831x_backup *devdata = platform_get_drvdata(pdev); - power_supply_unregister(&devdata->backup); + power_supply_unregister(devdata->backup); return 0; } diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c index 3bed2f5..0161bda 100644 --- a/drivers/power/wm831x_power.c +++ b/drivers/power/wm831x_power.c @@ -21,9 +21,12 @@ struct wm831x_power { struct wm831x *wm831x; - struct power_supply wall; - struct power_supply usb; - struct power_supply battery; + struct power_supply *wall; + struct power_supply *usb; + struct power_supply *battery; + struct power_supply_desc wall_desc; + struct power_supply_desc usb_desc; + struct power_supply_desc battery_desc; char wall_name[20]; char usb_name[20]; char battery_name[20]; @@ -67,7 +70,7 @@ static int wm831x_wall_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent); + struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent); struct wm831x *wm831x = wm831x_power->wm831x; int ret = 0; @@ -98,7 +101,7 @@ static int wm831x_usb_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent); + struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent); struct wm831x *wm831x = wm831x_power->wm831x; int ret = 0; @@ -393,7 +396,7 @@ static int wm831x_bat_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent); + struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent); struct wm831x *wm831x = wm831x_power->wm831x; int ret = 0; @@ -451,7 +454,7 @@ static irqreturn_t wm831x_bat_irq(int irq, void *data) /* The battery charger is autonomous so we don't need to do * anything except kick user space */ if (wm831x_power->have_battery) - power_supply_changed(&wm831x_power->battery); + power_supply_changed(wm831x_power->battery); return IRQ_HANDLED; } @@ -482,9 +485,9 @@ static irqreturn_t wm831x_pwr_src_irq(int irq, void *data) /* Just notify for everything - little harm in overnotifying. */ if (wm831x_power->have_battery) - power_supply_changed(&wm831x_power->battery); - power_supply_changed(&wm831x_power->usb); - power_supply_changed(&wm831x_power->wall); + power_supply_changed(wm831x_power->battery); + power_supply_changed(wm831x_power->usb); + power_supply_changed(wm831x_power->wall); return IRQ_HANDLED; } @@ -494,9 +497,6 @@ static int wm831x_power_probe(struct platform_device *pdev) struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data; struct wm831x_power *power; - struct power_supply *usb; - struct power_supply *battery; - struct power_supply *wall; int ret, irq, i; power = kzalloc(sizeof(struct wm831x_power), GFP_KERNEL); @@ -506,10 +506,6 @@ static int wm831x_power_probe(struct platform_device *pdev) power->wm831x = wm831x; platform_set_drvdata(pdev, power); - usb = &power->usb; - battery = &power->battery; - wall = &power->wall; - if (wm831x_pdata && wm831x_pdata->wm831x_num) { snprintf(power->wall_name, sizeof(power->wall_name), "wm831x-wall.%d", wm831x_pdata->wm831x_num); @@ -531,23 +527,28 @@ static int wm831x_power_probe(struct platform_device *pdev) */ wm831x_config_battery(wm831x); - wall->name = power->wall_name; - wall->type = POWER_SUPPLY_TYPE_MAINS; - wall->properties = wm831x_wall_props; - wall->num_properties = ARRAY_SIZE(wm831x_wall_props); - wall->get_property = wm831x_wall_get_prop; - ret = power_supply_register(&pdev->dev, wall); - if (ret) + power->wall_desc.name = power->wall_name; + power->wall_desc.type = POWER_SUPPLY_TYPE_MAINS; + power->wall_desc.properties = wm831x_wall_props; + power->wall_desc.num_properties = ARRAY_SIZE(wm831x_wall_props); + power->wall_desc.get_property = wm831x_wall_get_prop; + power->wall = power_supply_register(&pdev->dev, &power->wall_desc, + NULL); + if (IS_ERR(power->wall)) { + ret = PTR_ERR(power->wall); goto err_kmalloc; + } - usb->name = power->usb_name, - usb->type = POWER_SUPPLY_TYPE_USB; - usb->properties = wm831x_usb_props; - usb->num_properties = ARRAY_SIZE(wm831x_usb_props); - usb->get_property = wm831x_usb_get_prop; - ret = power_supply_register(&pdev->dev, usb); - if (ret) + power->usb_desc.name = power->usb_name, + power->usb_desc.type = POWER_SUPPLY_TYPE_USB; + power->usb_desc.properties = wm831x_usb_props; + power->usb_desc.num_properties = ARRAY_SIZE(wm831x_usb_props); + power->usb_desc.get_property = wm831x_usb_get_prop; + power->usb = power_supply_register(&pdev->dev, &power->usb_desc, NULL); + if (IS_ERR(power->usb)) { + ret = PTR_ERR(power->usb); goto err_wall; + } ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_1); if (ret < 0) @@ -555,14 +556,18 @@ static int wm831x_power_probe(struct platform_device *pdev) power->have_battery = ret & WM831X_CHG_ENA; if (power->have_battery) { - battery->name = power->battery_name; - battery->properties = wm831x_bat_props; - battery->num_properties = ARRAY_SIZE(wm831x_bat_props); - battery->get_property = wm831x_bat_get_prop; - battery->use_for_apm = 1; - ret = power_supply_register(&pdev->dev, battery); - if (ret) - goto err_usb; + power->battery_desc.name = power->battery_name; + power->battery_desc.properties = wm831x_bat_props; + power->battery_desc.num_properties = ARRAY_SIZE(wm831x_bat_props); + power->battery_desc.get_property = wm831x_bat_get_prop; + power->battery_desc.use_for_apm = 1; + power->battery = power_supply_register(&pdev->dev, + &power->battery_desc, + NULL); + if (IS_ERR(power->battery)) { + ret = PTR_ERR(power->battery); + goto err_usb; + } } irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO")); @@ -615,11 +620,11 @@ err_syslo: free_irq(irq, power); err_battery: if (power->have_battery) - power_supply_unregister(battery); + power_supply_unregister(power->battery); err_usb: - power_supply_unregister(usb); + power_supply_unregister(power->usb); err_wall: - power_supply_unregister(wall); + power_supply_unregister(power->wall); err_kmalloc: kfree(power); return ret; @@ -645,9 +650,9 @@ static int wm831x_power_remove(struct platform_device *pdev) free_irq(irq, wm831x_power); if (wm831x_power->have_battery) - power_supply_unregister(&wm831x_power->battery); - power_supply_unregister(&wm831x_power->wall); - power_supply_unregister(&wm831x_power->usb); + power_supply_unregister(wm831x_power->battery); + power_supply_unregister(wm831x_power->wall); + power_supply_unregister(wm831x_power->usb); kfree(wm831x_power); return 0; } diff --git a/drivers/power/wm8350_power.c b/drivers/power/wm8350_power.c index b3607e2..5c58806 100644 --- a/drivers/power/wm8350_power.c +++ b/drivers/power/wm8350_power.c @@ -196,14 +196,14 @@ static irqreturn_t wm8350_charger_handler(int irq, void *data) break; case WM8350_IRQ_CHG_TO: dev_err(wm8350->dev, "charger timeout\n"); - power_supply_changed(&power->battery); + power_supply_changed(power->battery); break; case WM8350_IRQ_CHG_BAT_HOT: case WM8350_IRQ_CHG_BAT_COLD: case WM8350_IRQ_CHG_START: case WM8350_IRQ_CHG_END: - power_supply_changed(&power->battery); + power_supply_changed(power->battery); break; case WM8350_IRQ_CHG_FAST_RDY: @@ -231,9 +231,9 @@ static irqreturn_t wm8350_charger_handler(int irq, void *data) case WM8350_IRQ_EXT_WALL_FB: wm8350_charger_config(wm8350, policy); case WM8350_IRQ_EXT_BAT_FB: /* Fall through */ - power_supply_changed(&power->battery); - power_supply_changed(&power->usb); - power_supply_changed(&power->ac); + power_supply_changed(power->battery); + power_supply_changed(power->usb); + power_supply_changed(power->ac); break; default: @@ -250,7 +250,7 @@ static int wm8350_ac_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct wm8350 *wm8350 = dev_get_drvdata(psy->dev->parent); + struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent); int ret = 0; switch (psp) { @@ -280,7 +280,7 @@ static int wm8350_usb_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct wm8350 *wm8350 = dev_get_drvdata(psy->dev->parent); + struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent); int ret = 0; switch (psp) { @@ -346,7 +346,7 @@ static int wm8350_bat_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct wm8350 *wm8350 = dev_get_drvdata(psy->dev->parent); + struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent); int ret = 0; switch (psp) { @@ -382,6 +382,30 @@ static enum power_supply_property wm8350_bat_props[] = { POWER_SUPPLY_PROP_CHARGE_TYPE, }; +static const struct power_supply_desc wm8350_ac_desc = { + .name = "wm8350-ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = wm8350_ac_props, + .num_properties = ARRAY_SIZE(wm8350_ac_props), + .get_property = wm8350_ac_get_prop, +}; + +static const struct power_supply_desc wm8350_battery_desc = { + .name = "wm8350-battery", + .properties = wm8350_bat_props, + .num_properties = ARRAY_SIZE(wm8350_bat_props), + .get_property = wm8350_bat_get_property, + .use_for_apm = 1, +}; + +static const struct power_supply_desc wm8350_usb_desc = { + .name = "wm8350-usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = wm8350_usb_props, + .num_properties = ARRAY_SIZE(wm8350_usb_props), + .get_property = wm8350_usb_get_prop, +}; + /********************************************************************* * Initialisation *********************************************************************/ @@ -447,37 +471,24 @@ static int wm8350_power_probe(struct platform_device *pdev) struct wm8350 *wm8350 = platform_get_drvdata(pdev); struct wm8350_power *power = &wm8350->power; struct wm8350_charger_policy *policy = power->policy; - struct power_supply *usb = &power->usb; - struct power_supply *battery = &power->battery; - struct power_supply *ac = &power->ac; int ret; - ac->name = "wm8350-ac"; - ac->type = POWER_SUPPLY_TYPE_MAINS; - ac->properties = wm8350_ac_props; - ac->num_properties = ARRAY_SIZE(wm8350_ac_props); - ac->get_property = wm8350_ac_get_prop; - ret = power_supply_register(&pdev->dev, ac); - if (ret) - return ret; - - battery->name = "wm8350-battery"; - battery->properties = wm8350_bat_props; - battery->num_properties = ARRAY_SIZE(wm8350_bat_props); - battery->get_property = wm8350_bat_get_property; - battery->use_for_apm = 1; - ret = power_supply_register(&pdev->dev, battery); - if (ret) + power->ac = power_supply_register(&pdev->dev, &wm8350_ac_desc, NULL); + if (IS_ERR(power->ac)) + return PTR_ERR(power->ac); + + power->battery = power_supply_register(&pdev->dev, &wm8350_battery_desc, + NULL); + if (IS_ERR(power->battery)) { + ret = PTR_ERR(power->battery); goto battery_failed; + } - usb->name = "wm8350-usb", - usb->type = POWER_SUPPLY_TYPE_USB; - usb->properties = wm8350_usb_props; - usb->num_properties = ARRAY_SIZE(wm8350_usb_props); - usb->get_property = wm8350_usb_get_prop; - ret = power_supply_register(&pdev->dev, usb); - if (ret) + power->usb = power_supply_register(&pdev->dev, &wm8350_usb_desc, NULL); + if (IS_ERR(power->usb)) { + ret = PTR_ERR(power->usb); goto usb_failed; + } ret = device_create_file(&pdev->dev, &dev_attr_charger_state); if (ret < 0) @@ -494,9 +505,9 @@ static int wm8350_power_probe(struct platform_device *pdev) return ret; usb_failed: - power_supply_unregister(battery); + power_supply_unregister(power->battery); battery_failed: - power_supply_unregister(ac); + power_supply_unregister(power->ac); return ret; } @@ -508,9 +519,9 @@ static int wm8350_power_remove(struct platform_device *pdev) free_charger_irq(wm8350); device_remove_file(&pdev->dev, &dev_attr_charger_state); - power_supply_unregister(&power->battery); - power_supply_unregister(&power->ac); - power_supply_unregister(&power->usb); + power_supply_unregister(power->battery); + power_supply_unregister(power->ac); + power_supply_unregister(power->usb); return 0; } diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c index a8e6203..c2f09ed 100644 --- a/drivers/power/wm97xx_battery.c +++ b/drivers/power/wm97xx_battery.c @@ -32,20 +32,20 @@ static enum power_supply_property *prop; static unsigned long wm97xx_read_bat(struct power_supply *bat_ps) { - struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data; + struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data; struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; - return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev->parent), + return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev.parent), pdata->batt_aux) * pdata->batt_mult / pdata->batt_div; } static unsigned long wm97xx_read_temp(struct power_supply *bat_ps) { - struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data; + struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data; struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; - return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev->parent), + return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev.parent), pdata->temp_aux) * pdata->temp_mult / pdata->temp_div; } @@ -54,7 +54,7 @@ static int wm97xx_bat_get_property(struct power_supply *bat_ps, enum power_supply_property psp, union power_supply_propval *val) { - struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data; + struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data; struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; switch (psp) { @@ -105,7 +105,7 @@ static void wm97xx_bat_external_power_changed(struct power_supply *bat_ps) static void wm97xx_bat_update(struct power_supply *bat_ps) { int old_status = bat_status; - struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data; + struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data; struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; mutex_lock(&work_lock); @@ -117,7 +117,7 @@ static void wm97xx_bat_update(struct power_supply *bat_ps) POWER_SUPPLY_STATUS_UNKNOWN; if (old_status != bat_status) { - pr_debug("%s: %i -> %i\n", bat_ps->name, old_status, + pr_debug("%s: %i -> %i\n", bat_ps->desc->name, old_status, bat_status); power_supply_changed(bat_ps); } @@ -125,7 +125,8 @@ static void wm97xx_bat_update(struct power_supply *bat_ps) mutex_unlock(&work_lock); } -static struct power_supply bat_ps = { +static struct power_supply *bat_psy; +static struct power_supply_desc bat_psy_desc = { .type = POWER_SUPPLY_TYPE_BATTERY, .get_property = wm97xx_bat_get_property, .external_power_changed = wm97xx_bat_external_power_changed, @@ -134,7 +135,7 @@ static struct power_supply bat_ps = { static void wm97xx_bat_work(struct work_struct *work) { - wm97xx_bat_update(&bat_ps); + wm97xx_bat_update(bat_psy); } static irqreturn_t wm97xx_chrg_irq(int irq, void *data) @@ -237,18 +238,20 @@ static int wm97xx_bat_probe(struct platform_device *dev) dev_info(&dev->dev, "Please consider setting proper battery " "name in platform definition file, falling " "back to name \"wm97xx-batt\"\n"); - bat_ps.name = "wm97xx-batt"; + bat_psy_desc.name = "wm97xx-batt"; } else - bat_ps.name = pdata->batt_name; + bat_psy_desc.name = pdata->batt_name; - bat_ps.properties = prop; - bat_ps.num_properties = props; + bat_psy_desc.properties = prop; + bat_psy_desc.num_properties = props; - ret = power_supply_register(&dev->dev, &bat_ps); - if (!ret) + bat_psy = power_supply_register(&dev->dev, &bat_psy_desc, NULL); + if (!IS_ERR(bat_psy)) { schedule_work(&bat_work); - else + } else { + ret = PTR_ERR(bat_psy); goto err4; + } return 0; err4: @@ -273,7 +276,7 @@ static int wm97xx_bat_remove(struct platform_device *dev) gpio_free(pdata->charge_gpio); } cancel_work_sync(&bat_work); - power_supply_unregister(&bat_ps); + power_supply_unregister(bat_psy); kfree(prop); return 0; } diff --git a/drivers/power/z2_battery.c b/drivers/power/z2_battery.c index 814d2e3..b201e3f 100644 --- a/drivers/power/z2_battery.c +++ b/drivers/power/z2_battery.c @@ -21,12 +21,13 @@ #define Z2_DEFAULT_NAME "Z2" struct z2_charger { - struct z2_battery_info *info; - int bat_status; - struct i2c_client *client; - struct power_supply batt_ps; - struct mutex work_lock; - struct work_struct bat_work; + struct z2_battery_info *info; + int bat_status; + struct i2c_client *client; + struct power_supply *batt_ps; + struct power_supply_desc batt_ps_desc; + struct mutex work_lock; + struct work_struct bat_work; }; static unsigned long z2_read_bat(struct z2_charger *charger) @@ -44,8 +45,7 @@ static int z2_batt_get_property(struct power_supply *batt_ps, enum power_supply_property psp, union power_supply_propval *val) { - struct z2_charger *charger = container_of(batt_ps, struct z2_charger, - batt_ps); + struct z2_charger *charger = power_supply_get_drvdata(batt_ps); struct z2_battery_info *info = charger->info; switch (psp) { @@ -85,8 +85,8 @@ static int z2_batt_get_property(struct power_supply *batt_ps, static void z2_batt_ext_power_changed(struct power_supply *batt_ps) { - struct z2_charger *charger = container_of(batt_ps, struct z2_charger, - batt_ps); + struct z2_charger *charger = power_supply_get_drvdata(batt_ps); + schedule_work(&charger->bat_work); } @@ -106,9 +106,10 @@ static void z2_batt_update(struct z2_charger *charger) POWER_SUPPLY_STATUS_UNKNOWN; if (old_status != charger->bat_status) { - pr_debug("%s: %i -> %i\n", charger->batt_ps.name, old_status, - charger->bat_status); - power_supply_changed(&charger->batt_ps); + pr_debug("%s: %i -> %i\n", charger->batt_ps->desc->name, + old_status, + charger->bat_status); + power_supply_changed(charger->batt_ps); } mutex_unlock(&charger->work_lock); @@ -166,16 +167,17 @@ static int z2_batt_ps_init(struct z2_charger *charger, int props) "Please consider setting proper battery " "name in platform definition file, falling " "back to name \" Z2_DEFAULT_NAME \"\n"); - charger->batt_ps.name = Z2_DEFAULT_NAME; + charger->batt_ps_desc.name = Z2_DEFAULT_NAME; } else - charger->batt_ps.name = info->batt_name; + charger->batt_ps_desc.name = info->batt_name; - charger->batt_ps.properties = prop; - charger->batt_ps.num_properties = props; - charger->batt_ps.type = POWER_SUPPLY_TYPE_BATTERY; - charger->batt_ps.get_property = z2_batt_get_property; - charger->batt_ps.external_power_changed = z2_batt_ext_power_changed; - charger->batt_ps.use_for_apm = 1; + charger->batt_ps_desc.properties = prop; + charger->batt_ps_desc.num_properties = props; + charger->batt_ps_desc.type = POWER_SUPPLY_TYPE_BATTERY; + charger->batt_ps_desc.get_property = z2_batt_get_property; + charger->batt_ps_desc.external_power_changed = + z2_batt_ext_power_changed; + charger->batt_ps_desc.use_for_apm = 1; return 0; } @@ -187,6 +189,7 @@ static int z2_batt_probe(struct i2c_client *client, int props = 1; /* POWER_SUPPLY_PROP_PRESENT */ struct z2_charger *charger; struct z2_battery_info *info = client->dev.platform_data; + struct power_supply_config psy_cfg = {}; if (info == NULL) { dev_err(&client->dev, @@ -203,6 +206,7 @@ static int z2_batt_probe(struct i2c_client *client, charger->info = info; charger->client = client; i2c_set_clientdata(client, charger); + psy_cfg.drv_data = charger; mutex_init(&charger->work_lock); @@ -230,16 +234,20 @@ static int z2_batt_probe(struct i2c_client *client, INIT_WORK(&charger->bat_work, z2_batt_work); - ret = power_supply_register(&client->dev, &charger->batt_ps); - if (ret) + charger->batt_ps = power_supply_register(&client->dev, + &charger->batt_ps_desc, + &psy_cfg); + if (IS_ERR(charger->batt_ps)) { + ret = PTR_ERR(charger->batt_ps); goto err4; + } schedule_work(&charger->bat_work); return 0; err4: - kfree(charger->batt_ps.properties); + kfree(charger->batt_ps_desc.properties); err3: if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) free_irq(gpio_to_irq(info->charge_gpio), charger); @@ -257,9 +265,9 @@ static int z2_batt_remove(struct i2c_client *client) struct z2_battery_info *info = charger->info; cancel_work_sync(&charger->bat_work); - power_supply_unregister(&charger->batt_ps); + power_supply_unregister(charger->batt_ps); - kfree(charger->batt_ps.properties); + kfree(charger->batt_ps_desc.properties); if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) { free_irq(gpio_to_irq(info->charge_gpio), charger); gpio_free(info->charge_gpio); diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c index 97b5e4e..63d4033 100644 --- a/drivers/powercap/intel_rapl.c +++ b/drivers/powercap/intel_rapl.c @@ -73,7 +73,7 @@ #define TIME_WINDOW_MAX_MSEC 40000 #define TIME_WINDOW_MIN_MSEC 250 - +#define ENERGY_UNIT_SCALE 1000 /* scale from driver unit to powercap unit */ enum unit_type { ARBITRARY_UNIT, /* no translation */ POWER_UNIT, @@ -158,6 +158,7 @@ struct rapl_domain { struct rapl_power_limit rpl[NR_POWER_LIMITS]; u64 attr_map; /* track capabilities */ unsigned int state; + unsigned int domain_energy_unit; int package_id; }; #define power_zone_to_rapl_domain(_zone) \ @@ -190,6 +191,7 @@ struct rapl_defaults { void (*set_floor_freq)(struct rapl_domain *rd, bool mode); u64 (*compute_time_window)(struct rapl_package *rp, u64 val, bool to_raw); + unsigned int dram_domain_energy_unit; }; static struct rapl_defaults *rapl_defaults; @@ -227,7 +229,8 @@ static int rapl_read_data_raw(struct rapl_domain *rd, static int rapl_write_data_raw(struct rapl_domain *rd, enum rapl_primitives prim, unsigned long long value); -static u64 rapl_unit_xlate(int package, enum unit_type type, u64 value, +static u64 rapl_unit_xlate(struct rapl_domain *rd, int package, + enum unit_type type, u64 value, int to_raw); static void package_power_limit_irq_save(int package_id); @@ -305,7 +308,9 @@ static int get_energy_counter(struct powercap_zone *power_zone, u64 *energy_raw) static int get_max_energy_counter(struct powercap_zone *pcd_dev, u64 *energy) { - *energy = rapl_unit_xlate(0, ENERGY_UNIT, ENERGY_STATUS_MASK, 0); + struct rapl_domain *rd = power_zone_to_rapl_domain(pcd_dev); + + *energy = rapl_unit_xlate(rd, 0, ENERGY_UNIT, ENERGY_STATUS_MASK, 0); return 0; } @@ -639,6 +644,11 @@ static void rapl_init_domains(struct rapl_package *rp) rd->msrs[4] = MSR_DRAM_POWER_INFO; rd->rpl[0].prim_id = PL1_ENABLE; rd->rpl[0].name = pl1_name; + rd->domain_energy_unit = + rapl_defaults->dram_domain_energy_unit; + if (rd->domain_energy_unit) + pr_info("DRAM domain energy unit %dpj\n", + rd->domain_energy_unit); break; } if (mask) { @@ -648,11 +658,13 @@ static void rapl_init_domains(struct rapl_package *rp) } } -static u64 rapl_unit_xlate(int package, enum unit_type type, u64 value, +static u64 rapl_unit_xlate(struct rapl_domain *rd, int package, + enum unit_type type, u64 value, int to_raw) { u64 units = 1; struct rapl_package *rp; + u64 scale = 1; rp = find_package_by_id(package); if (!rp) @@ -663,7 +675,12 @@ static u64 rapl_unit_xlate(int package, enum unit_type type, u64 value, units = rp->power_unit; break; case ENERGY_UNIT: - units = rp->energy_unit; + scale = ENERGY_UNIT_SCALE; + /* per domain unit takes precedence */ + if (rd && rd->domain_energy_unit) + units = rd->domain_energy_unit; + else + units = rp->energy_unit; break; case TIME_UNIT: return rapl_defaults->compute_time_window(rp, value, to_raw); @@ -673,11 +690,11 @@ static u64 rapl_unit_xlate(int package, enum unit_type type, u64 value, }; if (to_raw) - return div64_u64(value, units); + return div64_u64(value, units) * scale; value *= units; - return value; + return div64_u64(value, scale); } /* in the order of enum rapl_primitives */ @@ -773,7 +790,7 @@ static int rapl_read_data_raw(struct rapl_domain *rd, final = value & rp->mask; final = final >> rp->shift; if (xlate) - *data = rapl_unit_xlate(rd->package_id, rp->unit, final, 0); + *data = rapl_unit_xlate(rd, rd->package_id, rp->unit, final, 0); else *data = final; @@ -799,7 +816,7 @@ static int rapl_write_data_raw(struct rapl_domain *rd, "failed to read msr 0x%x on cpu %d\n", msr, cpu); return -EIO; } - value = rapl_unit_xlate(rd->package_id, rp->unit, value, 1); + value = rapl_unit_xlate(rd, rd->package_id, rp->unit, value, 1); msr_val &= ~rp->mask; msr_val |= value << rp->shift; if (wrmsrl_safe_on_cpu(cpu, msr, msr_val)) { @@ -818,7 +835,7 @@ static int rapl_write_data_raw(struct rapl_domain *rd, * calculate units differ on different CPUs. * We convert the units to below format based on CPUs. * i.e. - * energy unit: microJoules : Represented in microJoules by default + * energy unit: picoJoules : Represented in picoJoules by default * power unit : microWatts : Represented in milliWatts by default * time unit : microseconds: Represented in seconds by default */ @@ -834,7 +851,7 @@ static int rapl_check_unit_core(struct rapl_package *rp, int cpu) } value = (msr_val & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET; - rp->energy_unit = 1000000 / (1 << value); + rp->energy_unit = ENERGY_UNIT_SCALE * 1000000 / (1 << value); value = (msr_val & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET; rp->power_unit = 1000000 / (1 << value); @@ -842,7 +859,7 @@ static int rapl_check_unit_core(struct rapl_package *rp, int cpu) value = (msr_val & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET; rp->time_unit = 1000000 / (1 << value); - pr_debug("Core CPU package %d energy=%duJ, time=%dus, power=%duW\n", + pr_debug("Core CPU package %d energy=%dpJ, time=%dus, power=%duW\n", rp->id, rp->energy_unit, rp->time_unit, rp->power_unit); return 0; @@ -859,7 +876,7 @@ static int rapl_check_unit_atom(struct rapl_package *rp, int cpu) return -ENODEV; } value = (msr_val & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET; - rp->energy_unit = 1 << value; + rp->energy_unit = ENERGY_UNIT_SCALE * 1 << value; value = (msr_val & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET; rp->power_unit = (1 << value) * 1000; @@ -867,7 +884,7 @@ static int rapl_check_unit_atom(struct rapl_package *rp, int cpu) value = (msr_val & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET; rp->time_unit = 1000000 / (1 << value); - pr_debug("Atom package %d energy=%duJ, time=%dus, power=%duW\n", + pr_debug("Atom package %d energy=%dpJ, time=%dus, power=%duW\n", rp->id, rp->energy_unit, rp->time_unit, rp->power_unit); return 0; @@ -1017,6 +1034,13 @@ static const struct rapl_defaults rapl_defaults_core = { .compute_time_window = rapl_compute_time_window_core, }; +static const struct rapl_defaults rapl_defaults_hsw_server = { + .check_unit = rapl_check_unit_core, + .set_floor_freq = set_floor_freq_default, + .compute_time_window = rapl_compute_time_window_core, + .dram_domain_energy_unit = 15300, +}; + static const struct rapl_defaults rapl_defaults_atom = { .check_unit = rapl_check_unit_atom, .set_floor_freq = set_floor_freq_atom, @@ -1037,7 +1061,7 @@ static const struct x86_cpu_id rapl_ids[] = { RAPL_CPU(0x3a, rapl_defaults_core),/* Ivy Bridge */ RAPL_CPU(0x3c, rapl_defaults_core),/* Haswell */ RAPL_CPU(0x3d, rapl_defaults_core),/* Broadwell */ - RAPL_CPU(0x3f, rapl_defaults_core),/* Haswell */ + RAPL_CPU(0x3f, rapl_defaults_hsw_server),/* Haswell servers */ RAPL_CPU(0x45, rapl_defaults_core),/* Haswell ULT */ RAPL_CPU(0x4C, rapl_defaults_atom),/* Braswell */ RAPL_CPU(0x4A, rapl_defaults_atom),/* Tangier */ diff --git a/drivers/regulator/act8865-regulator.c b/drivers/regulator/act8865-regulator.c index 9eec453..2ff73d7 100644 --- a/drivers/regulator/act8865-regulator.c +++ b/drivers/regulator/act8865-regulator.c @@ -29,6 +29,35 @@ #include <linux/regmap.h> /* + * ACT8600 Global Register Map. + */ +#define ACT8600_SYS_MODE 0x00 +#define ACT8600_SYS_CTRL 0x01 +#define ACT8600_DCDC1_VSET 0x10 +#define ACT8600_DCDC1_CTRL 0x12 +#define ACT8600_DCDC2_VSET 0x20 +#define ACT8600_DCDC2_CTRL 0x22 +#define ACT8600_DCDC3_VSET 0x30 +#define ACT8600_DCDC3_CTRL 0x32 +#define ACT8600_SUDCDC4_VSET 0x40 +#define ACT8600_SUDCDC4_CTRL 0x41 +#define ACT8600_LDO5_VSET 0x50 +#define ACT8600_LDO5_CTRL 0x51 +#define ACT8600_LDO6_VSET 0x60 +#define ACT8600_LDO6_CTRL 0x61 +#define ACT8600_LDO7_VSET 0x70 +#define ACT8600_LDO7_CTRL 0x71 +#define ACT8600_LDO8_VSET 0x80 +#define ACT8600_LDO8_CTRL 0x81 +#define ACT8600_LDO910_CTRL 0x91 +#define ACT8600_APCH0 0xA1 +#define ACT8600_APCH1 0xA8 +#define ACT8600_APCH2 0xA9 +#define ACT8600_APCH_STAT 0xAA +#define ACT8600_OTG0 0xB0 +#define ACT8600_OTG1 0xB2 + +/* * ACT8846 Global Register Map. */ #define ACT8846_SYS0 0x00 @@ -94,10 +123,15 @@ #define ACT8865_ENA 0x80 /* ON - [7] */ #define ACT8865_VSEL_MASK 0x3F /* VSET - [5:0] */ + +#define ACT8600_LDO10_ENA 0x40 /* ON - [6] */ +#define ACT8600_SUDCDC_VSEL_MASK 0xFF /* SUDCDC VSET - [7:0] */ + /* * ACT8865 voltage number */ #define ACT8865_VOLTAGE_NUM 64 +#define ACT8600_SUDCDC_VOLTAGE_NUM 255 struct act8865 { struct regmap *regmap; @@ -116,6 +150,13 @@ static const struct regulator_linear_range act8865_voltage_ranges[] = { REGULATOR_LINEAR_RANGE(2400000, 48, 63, 100000), }; +static const struct regulator_linear_range act8600_sudcdc_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(3000000, 0, 63, 0), + REGULATOR_LINEAR_RANGE(3000000, 64, 159, 100000), + REGULATOR_LINEAR_RANGE(12600000, 160, 191, 200000), + REGULATOR_LINEAR_RANGE(19000000, 191, 255, 400000), +}; + static struct regulator_ops act8865_ops = { .list_voltage = regulator_list_voltage_linear_range, .map_voltage = regulator_map_voltage_linear_range, @@ -126,9 +167,16 @@ static struct regulator_ops act8865_ops = { .is_enabled = regulator_is_enabled_regmap, }; -#define ACT88xx_REG(_name, _family, _id, _vsel_reg) \ +static struct regulator_ops act8865_ldo_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +#define ACT88xx_REG(_name, _family, _id, _vsel_reg, _supply) \ [_family##_ID_##_id] = { \ .name = _name, \ + .supply_name = _supply, \ .id = _family##_ID_##_id, \ .type = REGULATOR_VOLTAGE, \ .ops = &act8865_ops, \ @@ -142,33 +190,80 @@ static struct regulator_ops act8865_ops = { .owner = THIS_MODULE, \ } +static const struct regulator_desc act8600_regulators[] = { + ACT88xx_REG("DCDC1", ACT8600, DCDC1, VSET, "vp1"), + ACT88xx_REG("DCDC2", ACT8600, DCDC2, VSET, "vp2"), + ACT88xx_REG("DCDC3", ACT8600, DCDC3, VSET, "vp3"), + { + .name = "SUDCDC_REG4", + .id = ACT8600_ID_SUDCDC4, + .ops = &act8865_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = ACT8600_SUDCDC_VOLTAGE_NUM, + .linear_ranges = act8600_sudcdc_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(act8600_sudcdc_voltage_ranges), + .vsel_reg = ACT8600_SUDCDC4_VSET, + .vsel_mask = ACT8600_SUDCDC_VSEL_MASK, + .enable_reg = ACT8600_SUDCDC4_CTRL, + .enable_mask = ACT8865_ENA, + .owner = THIS_MODULE, + }, + ACT88xx_REG("LDO5", ACT8600, LDO5, VSET, "inl"), + ACT88xx_REG("LDO6", ACT8600, LDO6, VSET, "inl"), + ACT88xx_REG("LDO7", ACT8600, LDO7, VSET, "inl"), + ACT88xx_REG("LDO8", ACT8600, LDO8, VSET, "inl"), + { + .name = "LDO_REG9", + .id = ACT8600_ID_LDO9, + .ops = &act8865_ldo_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 1, + .fixed_uV = 1800000, + .enable_reg = ACT8600_LDO910_CTRL, + .enable_mask = ACT8865_ENA, + .owner = THIS_MODULE, + }, + { + .name = "LDO_REG10", + .id = ACT8600_ID_LDO10, + .ops = &act8865_ldo_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 1, + .fixed_uV = 1200000, + .enable_reg = ACT8600_LDO910_CTRL, + .enable_mask = ACT8600_LDO10_ENA, + .owner = THIS_MODULE, + }, +}; + static const struct regulator_desc act8846_regulators[] = { - ACT88xx_REG("REG1", ACT8846, REG1, VSET), - ACT88xx_REG("REG2", ACT8846, REG2, VSET0), - ACT88xx_REG("REG3", ACT8846, REG3, VSET0), - ACT88xx_REG("REG4", ACT8846, REG4, VSET0), - ACT88xx_REG("REG5", ACT8846, REG5, VSET), - ACT88xx_REG("REG6", ACT8846, REG6, VSET), - ACT88xx_REG("REG7", ACT8846, REG7, VSET), - ACT88xx_REG("REG8", ACT8846, REG8, VSET), - ACT88xx_REG("REG9", ACT8846, REG9, VSET), - ACT88xx_REG("REG10", ACT8846, REG10, VSET), - ACT88xx_REG("REG11", ACT8846, REG11, VSET), - ACT88xx_REG("REG12", ACT8846, REG12, VSET), + ACT88xx_REG("REG1", ACT8846, REG1, VSET, "vp1"), + ACT88xx_REG("REG2", ACT8846, REG2, VSET0, "vp2"), + ACT88xx_REG("REG3", ACT8846, REG3, VSET0, "vp3"), + ACT88xx_REG("REG4", ACT8846, REG4, VSET0, "vp4"), + ACT88xx_REG("REG5", ACT8846, REG5, VSET, "inl1"), + ACT88xx_REG("REG6", ACT8846, REG6, VSET, "inl1"), + ACT88xx_REG("REG7", ACT8846, REG7, VSET, "inl1"), + ACT88xx_REG("REG8", ACT8846, REG8, VSET, "inl2"), + ACT88xx_REG("REG9", ACT8846, REG9, VSET, "inl2"), + ACT88xx_REG("REG10", ACT8846, REG10, VSET, "inl3"), + ACT88xx_REG("REG11", ACT8846, REG11, VSET, "inl3"), + ACT88xx_REG("REG12", ACT8846, REG12, VSET, "inl3"), }; static const struct regulator_desc act8865_regulators[] = { - ACT88xx_REG("DCDC_REG1", ACT8865, DCDC1, VSET1), - ACT88xx_REG("DCDC_REG2", ACT8865, DCDC2, VSET1), - ACT88xx_REG("DCDC_REG3", ACT8865, DCDC3, VSET1), - ACT88xx_REG("LDO_REG1", ACT8865, LDO1, VSET), - ACT88xx_REG("LDO_REG2", ACT8865, LDO2, VSET), - ACT88xx_REG("LDO_REG3", ACT8865, LDO3, VSET), - ACT88xx_REG("LDO_REG4", ACT8865, LDO4, VSET), + ACT88xx_REG("DCDC_REG1", ACT8865, DCDC1, VSET1, "vp1"), + ACT88xx_REG("DCDC_REG2", ACT8865, DCDC2, VSET1, "vp2"), + ACT88xx_REG("DCDC_REG3", ACT8865, DCDC3, VSET1, "vp3"), + ACT88xx_REG("LDO_REG1", ACT8865, LDO1, VSET, "inl45"), + ACT88xx_REG("LDO_REG2", ACT8865, LDO2, VSET, "inl45"), + ACT88xx_REG("LDO_REG3", ACT8865, LDO3, VSET, "inl67"), + ACT88xx_REG("LDO_REG4", ACT8865, LDO4, VSET, "inl67"), }; #ifdef CONFIG_OF static const struct of_device_id act8865_dt_ids[] = { + { .compatible = "active-semi,act8600", .data = (void *)ACT8600 }, { .compatible = "active-semi,act8846", .data = (void *)ACT8846 }, { .compatible = "active-semi,act8865", .data = (void *)ACT8865 }, { } @@ -200,6 +295,19 @@ static struct of_regulator_match act8865_matches[] = { [ACT8865_ID_LDO4] = { .name = "LDO_REG4"}, }; +static struct of_regulator_match act8600_matches[] = { + [ACT8600_ID_DCDC1] = { .name = "DCDC_REG1"}, + [ACT8600_ID_DCDC2] = { .name = "DCDC_REG2"}, + [ACT8600_ID_DCDC3] = { .name = "DCDC_REG3"}, + [ACT8600_ID_SUDCDC4] = { .name = "SUDCDC_REG4"}, + [ACT8600_ID_LDO5] = { .name = "LDO_REG5"}, + [ACT8600_ID_LDO6] = { .name = "LDO_REG6"}, + [ACT8600_ID_LDO7] = { .name = "LDO_REG7"}, + [ACT8600_ID_LDO8] = { .name = "LDO_REG8"}, + [ACT8600_ID_LDO9] = { .name = "LDO_REG9"}, + [ACT8600_ID_LDO10] = { .name = "LDO_REG10"}, +}; + static int act8865_pdata_from_dt(struct device *dev, struct device_node **of_node, struct act8865_platform_data *pdata, @@ -217,6 +325,10 @@ static int act8865_pdata_from_dt(struct device *dev, } switch (type) { + case ACT8600: + matches = act8600_matches; + num_matches = ARRAY_SIZE(act8600_matches); + break; case ACT8846: matches = act8846_matches; num_matches = ARRAY_SIZE(act8846_matches); @@ -317,6 +429,12 @@ static int act8865_pmic_probe(struct i2c_client *client, } switch (type) { + case ACT8600: + regulators = act8600_regulators; + num_regulators = ARRAY_SIZE(act8600_regulators); + off_reg = -1; + off_mask = -1; + break; case ACT8846: regulators = act8846_regulators; num_regulators = ARRAY_SIZE(act8846_regulators); @@ -366,7 +484,7 @@ static int act8865_pmic_probe(struct i2c_client *client, } if (of_device_is_system_power_controller(dev->of_node)) { - if (!pm_power_off) { + if (!pm_power_off && (off_reg > 0)) { act8865_i2c_client = client; act8865->off_reg = off_reg; act8865->off_mask = off_mask; @@ -402,6 +520,7 @@ static int act8865_pmic_probe(struct i2c_client *client, } static const struct i2c_device_id act8865_ids[] = { + { .name = "act8600", .driver_data = ACT8600 }, { .name = "act8846", .driver_data = ACT8846 }, { .name = "act8865", .driver_data = ACT8865 }, { }, diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index 8169165..a1d07d3 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -282,6 +282,9 @@ static int arizona_ldo1_probe(struct platform_device *pdev) arizona->external_dcvdd = true; ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config); + + of_node_put(config.of_node); + if (IS_ERR(ldo1->regulator)) { ret = PTR_ERR(ldo1->regulator); dev_err(arizona->dev, "Failed to register LDO1 supply: %d\n", @@ -289,8 +292,6 @@ static int arizona_ldo1_probe(struct platform_device *pdev) return ret; } - of_node_put(config.of_node); - platform_set_drvdata(pdev, ldo1); return 0; diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index 2007900..4116e74 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -284,6 +284,9 @@ static int arizona_micsupp_probe(struct platform_device *pdev) micsupp->regulator = devm_regulator_register(&pdev->dev, desc, &config); + + of_node_put(config.of_node); + if (IS_ERR(micsupp->regulator)) { ret = PTR_ERR(micsupp->regulator); dev_err(arizona->dev, "Failed to register mic supply: %d\n", @@ -291,8 +294,6 @@ static int arizona_micsupp_probe(struct platform_device *pdev) return ret; } - of_node_put(config.of_node); - platform_set_drvdata(pdev, micsupp); return 0; diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index b899947..443eaab 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -648,10 +648,12 @@ static int drms_uA_update(struct regulator_dev *rdev) if (err < 0) return 0; - if (!rdev->desc->ops->get_optimum_mode) + if (!rdev->desc->ops->get_optimum_mode && + !rdev->desc->ops->set_load) return 0; - if (!rdev->desc->ops->set_mode) + if (!rdev->desc->ops->set_mode && + !rdev->desc->ops->set_load) return -EINVAL; /* get output voltage */ @@ -676,22 +678,29 @@ static int drms_uA_update(struct regulator_dev *rdev) list_for_each_entry(sibling, &rdev->consumer_list, list) current_uA += sibling->uA_load; - /* now get the optimum mode for our new total regulator load */ - mode = rdev->desc->ops->get_optimum_mode(rdev, input_uV, - output_uV, current_uA); + if (rdev->desc->ops->set_load) { + /* set the optimum mode for our new total regulator load */ + err = rdev->desc->ops->set_load(rdev, current_uA); + if (err < 0) + rdev_err(rdev, "failed to set load %d\n", current_uA); + } else { + /* now get the optimum mode for our new total regulator load */ + mode = rdev->desc->ops->get_optimum_mode(rdev, input_uV, + output_uV, current_uA); + + /* check the new mode is allowed */ + err = regulator_mode_constrain(rdev, &mode); + if (err < 0) { + rdev_err(rdev, "failed to get optimum mode @ %d uA %d -> %d uV\n", + current_uA, input_uV, output_uV); + return err; + } - /* check the new mode is allowed */ - err = regulator_mode_constrain(rdev, &mode); - if (err < 0) { - rdev_err(rdev, "failed to get optimum mode @ %d uA %d -> %d uV\n", - current_uA, input_uV, output_uV); - return err; + err = rdev->desc->ops->set_mode(rdev, mode); + if (err < 0) + rdev_err(rdev, "failed to set optimum mode %x\n", mode); } - err = rdev->desc->ops->set_mode(rdev, mode); - if (err < 0) - rdev_err(rdev, "failed to set optimum mode %x\n", mode); - return err; } @@ -1316,6 +1325,54 @@ static struct regulator_dev *regulator_dev_lookup(struct device *dev, return NULL; } +static int regulator_resolve_supply(struct regulator_dev *rdev) +{ + struct regulator_dev *r; + struct device *dev = rdev->dev.parent; + int ret; + + /* No supply to resovle? */ + if (!rdev->supply_name) + return 0; + + /* Supply already resolved? */ + if (rdev->supply) + return 0; + + r = regulator_dev_lookup(dev, rdev->supply_name, &ret); + if (ret == -ENODEV) { + /* + * No supply was specified for this regulator and + * there will never be one. + */ + return 0; + } + + if (!r) { + dev_err(dev, "Failed to resolve %s-supply for %s\n", + rdev->supply_name, rdev->desc->name); + return -EPROBE_DEFER; + } + + /* Recursively resolve the supply of the supply */ + ret = regulator_resolve_supply(r); + if (ret < 0) + return ret; + + ret = set_supply(rdev, r); + if (ret < 0) + return ret; + + /* Cascade always-on state to supply */ + if (_regulator_is_enabled(rdev)) { + ret = regulator_enable(rdev->supply); + if (ret < 0) + return ret; + } + + return 0; +} + /* Internal regulator request function */ static struct regulator *_regulator_get(struct device *dev, const char *id, bool exclusive, bool allow_dummy) @@ -1385,6 +1442,12 @@ found: goto out; } + ret = regulator_resolve_supply(rdev); + if (ret < 0) { + regulator = ERR_PTR(ret); + goto out; + } + if (!try_module_get(rdev->owner)) goto out; @@ -1839,10 +1902,12 @@ static int _regulator_do_enable(struct regulator_dev *rdev) } if (rdev->ena_pin) { - ret = regulator_ena_gpio_ctrl(rdev, true); - if (ret < 0) - return ret; - rdev->ena_gpio_state = 1; + if (!rdev->ena_gpio_state) { + ret = regulator_ena_gpio_ctrl(rdev, true); + if (ret < 0) + return ret; + rdev->ena_gpio_state = 1; + } } else if (rdev->desc->ops->enable) { ret = rdev->desc->ops->enable(rdev); if (ret < 0) @@ -1939,10 +2004,12 @@ static int _regulator_do_disable(struct regulator_dev *rdev) trace_regulator_disable(rdev_get_name(rdev)); if (rdev->ena_pin) { - ret = regulator_ena_gpio_ctrl(rdev, false); - if (ret < 0) - return ret; - rdev->ena_gpio_state = 0; + if (rdev->ena_gpio_state) { + ret = regulator_ena_gpio_ctrl(rdev, false); + if (ret < 0) + return ret; + rdev->ena_gpio_state = 0; + } } else if (rdev->desc->ops->disable) { ret = rdev->desc->ops->disable(rdev); @@ -2994,7 +3061,7 @@ unsigned int regulator_get_mode(struct regulator *regulator) EXPORT_SYMBOL_GPL(regulator_get_mode); /** - * regulator_set_optimum_mode - set regulator optimum operating mode + * regulator_set_load - set regulator load * @regulator: regulator source * @uA_load: load current * @@ -3017,9 +3084,9 @@ EXPORT_SYMBOL_GPL(regulator_get_mode); * DRMS will sum the total requested load on the regulator and change * to the most efficient operating mode if platform constraints allow. * - * Returns the new regulator mode or error. + * On error a negative errno is returned. */ -int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) +int regulator_set_load(struct regulator *regulator, int uA_load) { struct regulator_dev *rdev = regulator->rdev; int ret; @@ -3031,7 +3098,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) return ret; } -EXPORT_SYMBOL_GPL(regulator_set_optimum_mode); +EXPORT_SYMBOL_GPL(regulator_set_load); /** * regulator_allow_bypass - allow the regulator to go into bypass mode @@ -3444,13 +3511,6 @@ static umode_t regulator_attr_is_visible(struct kobject *kobj, if (attr == &dev_attr_requested_microamps.attr) return rdev->desc->type == REGULATOR_CURRENT ? mode : 0; - /* all the other attributes exist to support constraints; - * don't show them if there are no constraints, or if the - * relevant supporting methods are missing. - */ - if (!rdev->constraints) - return 0; - /* constraints need specific supporting methods */ if (attr == &dev_attr_min_microvolts.attr || attr == &dev_attr_max_microvolts.attr) @@ -3502,7 +3562,18 @@ static struct class regulator_class = { static void rdev_init_debugfs(struct regulator_dev *rdev) { - rdev->debugfs = debugfs_create_dir(rdev_get_name(rdev), debugfs_root); + struct device *parent = rdev->dev.parent; + const char *rname = rdev_get_name(rdev); + char name[NAME_MAX]; + + /* Avoid duplicate debugfs directory names */ + if (parent && rname == rdev->desc->name) { + snprintf(name, sizeof(name), "%s-%s", dev_name(parent), + rname); + rname = name; + } + + rdev->debugfs = debugfs_create_dir(rname, debugfs_root); if (!rdev->debugfs) { rdev_warn(rdev, "Failed to create debugfs directory\n"); return; @@ -3536,7 +3607,6 @@ regulator_register(const struct regulator_desc *regulator_desc, struct regulator_dev *rdev; struct device *dev; int ret, i; - const char *supply = NULL; if (regulator_desc == NULL || cfg == NULL) return ERR_PTR(-EINVAL); @@ -3633,12 +3703,6 @@ regulator_register(const struct regulator_desc *regulator_desc, config->ena_gpio, ret); goto wash; } - - if (config->ena_gpio_flags & GPIOF_OUT_INIT_HIGH) - rdev->ena_gpio_state = 1; - - if (config->ena_gpio_invert) - rdev->ena_gpio_state = !rdev->ena_gpio_state; } /* set regulator constraints */ @@ -3650,41 +3714,10 @@ regulator_register(const struct regulator_desc *regulator_desc, goto scrub; if (init_data && init_data->supply_regulator) - supply = init_data->supply_regulator; + rdev->supply_name = init_data->supply_regulator; else if (regulator_desc->supply_name) - supply = regulator_desc->supply_name; - - if (supply) { - struct regulator_dev *r; - - r = regulator_dev_lookup(dev, supply, &ret); - - if (ret == -ENODEV) { - /* - * No supply was specified for this regulator and - * there will never be one. - */ - ret = 0; - goto add_dev; - } else if (!r) { - dev_err(dev, "Failed to find supply %s\n", supply); - ret = -EPROBE_DEFER; - goto scrub; - } - - ret = set_supply(rdev, r); - if (ret < 0) - goto scrub; - - /* Enable supply if rail is enabled */ - if (_regulator_is_enabled(rdev)) { - ret = regulator_enable(rdev->supply); - if (ret < 0) - goto scrub; - } - } + rdev->supply_name = regulator_desc->supply_name; -add_dev: /* add consumers devices */ if (init_data) { for (i = 0; i < init_data->num_consumer_supplies; i++) { @@ -3711,8 +3744,6 @@ unset_supplies: unset_regulator_supplies(rdev); scrub: - if (rdev->supply) - _regulator_put(rdev->supply); regulator_ena_gpio_free(rdev); kfree(rdev->constraints); wash: @@ -3807,9 +3838,11 @@ int regulator_suspend_finish(void) list_for_each_entry(rdev, ®ulator_list, list) { mutex_lock(&rdev->mutex); if (rdev->use_count > 0 || rdev->constraints->always_on) { - error = _regulator_do_enable(rdev); - if (error) - ret = error; + if (!_regulator_is_enabled(rdev)) { + error = _regulator_do_enable(rdev); + if (error) + ret = error; + } } else { if (!have_full_constraints()) goto unlock; @@ -3943,6 +3976,110 @@ static const struct file_operations supply_map_fops = { #endif }; +#ifdef CONFIG_DEBUG_FS +static void regulator_summary_show_subtree(struct seq_file *s, + struct regulator_dev *rdev, + int level) +{ + struct list_head *list = s->private; + struct regulator_dev *child; + struct regulation_constraints *c; + struct regulator *consumer; + + if (!rdev) + return; + + seq_printf(s, "%*s%-*s %3d %4d %6d ", + level * 3 + 1, "", + 30 - level * 3, rdev_get_name(rdev), + rdev->use_count, rdev->open_count, rdev->bypass_count); + + seq_printf(s, "%5dmV ", _regulator_get_voltage(rdev) / 1000); + seq_printf(s, "%5dmA ", _regulator_get_current_limit(rdev) / 1000); + + c = rdev->constraints; + if (c) { + switch (rdev->desc->type) { + case REGULATOR_VOLTAGE: + seq_printf(s, "%5dmV %5dmV ", + c->min_uV / 1000, c->max_uV / 1000); + break; + case REGULATOR_CURRENT: + seq_printf(s, "%5dmA %5dmA ", + c->min_uA / 1000, c->max_uA / 1000); + break; + } + } + + seq_puts(s, "\n"); + + list_for_each_entry(consumer, &rdev->consumer_list, list) { + if (consumer->dev->class == ®ulator_class) + continue; + + seq_printf(s, "%*s%-*s ", + (level + 1) * 3 + 1, "", + 30 - (level + 1) * 3, dev_name(consumer->dev)); + + switch (rdev->desc->type) { + case REGULATOR_VOLTAGE: + seq_printf(s, "%37dmV %5dmV", + consumer->min_uV / 1000, + consumer->max_uV / 1000); + break; + case REGULATOR_CURRENT: + break; + } + + seq_puts(s, "\n"); + } + + list_for_each_entry(child, list, list) { + /* handle only non-root regulators supplied by current rdev */ + if (!child->supply || child->supply->rdev != rdev) + continue; + + regulator_summary_show_subtree(s, child, level + 1); + } +} + +static int regulator_summary_show(struct seq_file *s, void *data) +{ + struct list_head *list = s->private; + struct regulator_dev *rdev; + + seq_puts(s, " regulator use open bypass voltage current min max\n"); + seq_puts(s, "-------------------------------------------------------------------------------\n"); + + mutex_lock(®ulator_list_mutex); + + list_for_each_entry(rdev, list, list) { + if (rdev->supply) + continue; + + regulator_summary_show_subtree(s, rdev, 0); + } + + mutex_unlock(®ulator_list_mutex); + + return 0; +} + +static int regulator_summary_open(struct inode *inode, struct file *file) +{ + return single_open(file, regulator_summary_show, inode->i_private); +} +#endif + +static const struct file_operations regulator_summary_fops = { +#ifdef CONFIG_DEBUG_FS + .open = regulator_summary_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +#endif +}; + static int __init regulator_init(void) { int ret; @@ -3956,6 +4093,9 @@ static int __init regulator_init(void) debugfs_create_file("supply_map", 0444, debugfs_root, NULL, &supply_map_fops); + debugfs_create_file("regulator_summary", 0444, debugfs_root, + ®ulator_list, ®ulator_summary_fops); + regulator_dummy_init(); return ret; diff --git a/drivers/regulator/da9210-regulator.c b/drivers/regulator/da9210-regulator.c index bc61001..f0489cb 100644 --- a/drivers/regulator/da9210-regulator.c +++ b/drivers/regulator/da9210-regulator.c @@ -152,6 +152,15 @@ static int da9210_i2c_probe(struct i2c_client *i2c, config.regmap = chip->regmap; config.of_node = dev->of_node; + /* Mask all interrupt sources to deassert interrupt line */ + error = regmap_write(chip->regmap, DA9210_REG_MASK_A, ~0); + if (!error) + error = regmap_write(chip->regmap, DA9210_REG_MASK_B, ~0); + if (error) { + dev_err(&i2c->dev, "Failed to write to mask reg: %d\n", error); + return error; + } + rdev = devm_regulator_register(&i2c->dev, &da9210_reg, &config); if (IS_ERR(rdev)) { dev_err(&i2c->dev, "Failed to register DA9210 regulator\n"); diff --git a/drivers/regulator/da9211-regulator.c b/drivers/regulator/da9211-regulator.c index 0134341..df79e4b 100644 --- a/drivers/regulator/da9211-regulator.c +++ b/drivers/regulator/da9211-regulator.c @@ -305,8 +305,7 @@ static irqreturn_t da9211_irq_handler(int irq, void *data) if (reg_val & DA9211_E_OV_CURR_A) { regulator_notifier_call_chain(chip->rdev[0], - REGULATOR_EVENT_OVER_CURRENT, - rdev_get_drvdata(chip->rdev[0])); + REGULATOR_EVENT_OVER_CURRENT, NULL); err = regmap_write(chip->regmap, DA9211_REG_EVENT_B, DA9211_E_OV_CURR_A); @@ -318,8 +317,7 @@ static irqreturn_t da9211_irq_handler(int irq, void *data) if (reg_val & DA9211_E_OV_CURR_B) { regulator_notifier_call_chain(chip->rdev[1], - REGULATOR_EVENT_OVER_CURRENT, - rdev_get_drvdata(chip->rdev[1])); + REGULATOR_EVENT_OVER_CURRENT, NULL); err = regmap_write(chip->regmap, DA9211_REG_EVENT_B, DA9211_E_OV_CURR_B); @@ -344,7 +342,7 @@ static int da9211_regulator_init(struct da9211 *chip) ret = regmap_read(chip->regmap, DA9211_REG_CONFIG_E, &data); if (ret < 0) { - dev_err(chip->dev, "Failed to read CONTROL_E reg: %d\n", ret); + dev_err(chip->dev, "Failed to read CONFIG_E reg: %d\n", ret); return ret; } diff --git a/drivers/regulator/dbx500-prcmu.c b/drivers/regulator/dbx500-prcmu.c index 2d16b9f..3963dfa 100644 --- a/drivers/regulator/dbx500-prcmu.c +++ b/drivers/regulator/dbx500-prcmu.c @@ -95,14 +95,9 @@ void ux500_regulator_resume_debug(void) static int ux500_regulator_power_state_cnt_print(struct seq_file *s, void *p) { - struct device *dev = s->private; - int err; - /* print power state count */ - err = seq_printf(s, "ux500-regulator power state count: %i\n", - power_state_active_get()); - if (err < 0) - dev_err(dev, "seq_printf overflow\n"); + seq_printf(s, "ux500-regulator power state count: %i\n", + power_state_active_get()); return 0; } @@ -124,19 +119,11 @@ static const struct file_operations ux500_regulator_power_state_cnt_fops = { static int ux500_regulator_status_print(struct seq_file *s, void *p) { - struct device *dev = s->private; - int err; int i; /* print dump header */ - err = seq_puts(s, "ux500-regulator status:\n"); - if (err < 0) - dev_err(dev, "seq_puts overflow\n"); - - err = seq_printf(s, "%31s : %8s : %8s\n", "current", - "before", "after"); - if (err < 0) - dev_err(dev, "seq_printf overflow\n"); + seq_puts(s, "ux500-regulator status:\n"); + seq_printf(s, "%31s : %8s : %8s\n", "current", "before", "after"); for (i = 0; i < rdebug.num_regulators; i++) { struct dbx500_regulator_info *info; @@ -144,12 +131,11 @@ static int ux500_regulator_status_print(struct seq_file *s, void *p) info = &rdebug.regulator_array[i]; /* print status */ - err = seq_printf(s, "%20s : %8s : %8s : %8s\n", info->desc.name, - info->is_enabled ? "enabled" : "disabled", - rdebug.state_before_suspend[i] ? "enabled" : "disabled", - rdebug.state_after_suspend[i] ? "enabled" : "disabled"); - if (err < 0) - dev_err(dev, "seq_printf overflow\n"); + seq_printf(s, "%20s : %8s : %8s : %8s\n", + info->desc.name, + info->is_enabled ? "enabled" : "disabled", + rdebug.state_before_suspend[i] ? "enabled" : "disabled", + rdebug.state_after_suspend[i] ? "enabled" : "disabled"); } return 0; diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c index 8f785bc..6ec1d40 100644 --- a/drivers/regulator/devres.c +++ b/drivers/regulator/devres.c @@ -413,3 +413,88 @@ void devm_regulator_bulk_unregister_supply_alias(struct device *dev, devm_regulator_unregister_supply_alias(dev, id[i]); } EXPORT_SYMBOL_GPL(devm_regulator_bulk_unregister_supply_alias); + +struct regulator_notifier_match { + struct regulator *regulator; + struct notifier_block *nb; +}; + +static int devm_regulator_match_notifier(struct device *dev, void *res, + void *data) +{ + struct regulator_notifier_match *match = res; + struct regulator_notifier_match *target = data; + + return match->regulator == target->regulator && match->nb == target->nb; +} + +static void devm_regulator_destroy_notifier(struct device *dev, void *res) +{ + struct regulator_notifier_match *match = res; + + regulator_unregister_notifier(match->regulator, match->nb); +} + +/** + * devm_regulator_register_notifier - Resource managed + * regulator_register_notifier + * + * @regulator: regulator source + * @nb: notifier block + * + * The notifier will be registers under the consumer device and be + * automatically be unregistered when the source device is unbound. + */ +int devm_regulator_register_notifier(struct regulator *regulator, + struct notifier_block *nb) +{ + struct regulator_notifier_match *match; + int ret; + + match = devres_alloc(devm_regulator_destroy_notifier, + sizeof(struct regulator_notifier_match), + GFP_KERNEL); + if (!match) + return -ENOMEM; + + match->regulator = regulator; + match->nb = nb; + + ret = regulator_register_notifier(regulator, nb); + if (ret < 0) { + devres_free(match); + return ret; + } + + devres_add(regulator->dev, match); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_regulator_register_notifier); + +/** + * devm_regulator_unregister_notifier - Resource managed + * regulator_unregister_notifier() + * + * @regulator: regulator source + * @nb: notifier block + * + * Unregister a notifier registered with devm_regulator_register_notifier(). + * Normally this function will not need to be called and the resource + * management code will ensure that the resource is freed. + */ +void devm_regulator_unregister_notifier(struct regulator *regulator, + struct notifier_block *nb) +{ + struct regulator_notifier_match match; + int rc; + + match.regulator = regulator; + match.nb = nb; + + rc = devres_release(regulator->dev, devm_regulator_destroy_notifier, + devm_regulator_match_notifier, &match); + if (rc != 0) + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_regulator_unregister_notifier); diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c index 07b313e..9665a48 100644 --- a/drivers/regulator/max77693.c +++ b/drivers/regulator/max77693.c @@ -128,6 +128,8 @@ static struct regulator_ops max77693_charger_ops = { #define regulator_desc_esafeout(_num) { \ .name = "ESAFEOUT"#_num, \ .id = MAX77693_ESAFEOUT##_num, \ + .of_match = of_match_ptr("ESAFEOUT"#_num), \ + .regulators_node = of_match_ptr("regulators"), \ .n_voltages = 4, \ .ops = &max77693_safeout_ops, \ .type = REGULATOR_VOLTAGE, \ @@ -145,6 +147,8 @@ static const struct regulator_desc regulators[] = { { .name = "CHARGER", .id = MAX77693_CHARGER, + .of_match = of_match_ptr("CHARGER"), + .regulators_node = of_match_ptr("regulators"), .ops = &max77693_charger_ops, .type = REGULATOR_CURRENT, .owner = THIS_MODULE, @@ -154,102 +158,23 @@ static const struct regulator_desc regulators[] = { }, }; -#ifdef CONFIG_OF -static int max77693_pmic_dt_parse_rdata(struct device *dev, - struct max77693_regulator_data **rdata) -{ - struct device_node *np; - struct of_regulator_match *rmatch; - struct max77693_regulator_data *tmp; - int i, matched = 0; - - np = of_get_child_by_name(dev->parent->of_node, "regulators"); - if (!np) - return -EINVAL; - - rmatch = devm_kzalloc(dev, - sizeof(*rmatch) * ARRAY_SIZE(regulators), GFP_KERNEL); - if (!rmatch) { - of_node_put(np); - return -ENOMEM; - } - - for (i = 0; i < ARRAY_SIZE(regulators); i++) - rmatch[i].name = regulators[i].name; - - matched = of_regulator_match(dev, np, rmatch, ARRAY_SIZE(regulators)); - of_node_put(np); - if (matched <= 0) - return matched; - *rdata = devm_kzalloc(dev, sizeof(**rdata) * matched, GFP_KERNEL); - if (!(*rdata)) - return -ENOMEM; - - tmp = *rdata; - - for (i = 0; i < matched; i++) { - tmp->initdata = rmatch[i].init_data; - tmp->of_node = rmatch[i].of_node; - tmp->id = regulators[i].id; - tmp++; - } - - return matched; -} -#else -static int max77693_pmic_dt_parse_rdata(struct device *dev, - struct max77693_regulator_data **rdata) -{ - return 0; -} -#endif /* CONFIG_OF */ - -static int max77693_pmic_init_rdata(struct device *dev, - struct max77693_regulator_data **rdata) -{ - struct max77693_platform_data *pdata; - int num_regulators = 0; - - pdata = dev_get_platdata(dev->parent); - if (pdata) { - *rdata = pdata->regulators; - num_regulators = pdata->num_regulators; - } - - if (!(*rdata) && dev->parent->of_node) - num_regulators = max77693_pmic_dt_parse_rdata(dev, rdata); - - return num_regulators; -} - static int max77693_pmic_probe(struct platform_device *pdev) { struct max77693_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct max77693_regulator_data *rdata = NULL; - int num_rdata, i; + int i; struct regulator_config config = { }; - num_rdata = max77693_pmic_init_rdata(&pdev->dev, &rdata); - if (!rdata || num_rdata <= 0) { - dev_err(&pdev->dev, "No init data supplied.\n"); - return -ENODEV; - } - - config.dev = &pdev->dev; + config.dev = iodev->dev; config.regmap = iodev->regmap; - for (i = 0; i < num_rdata; i++) { - int id = rdata[i].id; + for (i = 0; i < ARRAY_SIZE(regulators); i++) { struct regulator_dev *rdev; - config.init_data = rdata[i].initdata; - config.of_node = rdata[i].of_node; - rdev = devm_regulator_register(&pdev->dev, - ®ulators[id], &config); + ®ulators[i], &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, - "Failed to initialize regulator-%d\n", id); + "Failed to initialize regulator-%d\n", i); return PTR_ERR(rdev); } } diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c index 7eee2ca..4071d74 100644 --- a/drivers/regulator/max8660.c +++ b/drivers/regulator/max8660.c @@ -382,7 +382,7 @@ static int max8660_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { struct device *dev = &client->dev; - struct max8660_platform_data *pdata = dev_get_platdata(dev); + struct max8660_platform_data pdata_of, *pdata = dev_get_platdata(dev); struct regulator_config config = { }; struct max8660 *max8660; int boot_on, i, id, ret = -EINVAL; @@ -391,7 +391,6 @@ static int max8660_probe(struct i2c_client *client, if (dev->of_node && !pdata) { const struct of_device_id *id; - struct max8660_platform_data pdata_of; id = of_match_device(of_match_ptr(max8660_dt_ids), dev); if (!id) @@ -443,9 +442,9 @@ static int max8660_probe(struct i2c_client *client, for (i = 0; i < pdata->num_subdevs; i++) { if (!pdata->subdevs[i].platform_data) - return ret; - - boot_on = pdata->subdevs[i].platform_data->constraints.boot_on; + boot_on = false; + else + boot_on = pdata->subdevs[i].platform_data->constraints.boot_on; switch (pdata->subdevs[i].id) { case MAX8660_V3: diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index 9205f43..8217613 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -916,6 +916,9 @@ static int palmas_ldo_registration(struct palmas_pmic *pmic, (id == PALMAS_REG_LDO6)) desc->enable_time = 2000; } else { + if (!ddata->has_regen3 && id == PALMAS_REG_REGEN3) + continue; + desc->n_voltages = 1; if (reg_init && reg_init->roof_floor) desc->ops = &palmas_ops_ext_control_extreg; @@ -1398,6 +1401,7 @@ static struct palmas_pmic_driver_data palmas_ddata = { .ldo_begin = PALMAS_REG_LDO1, .ldo_end = PALMAS_REG_LDOUSB, .max_reg = PALMAS_NUM_REGS, + .has_regen3 = true, .palmas_regs_info = palmas_generic_regs_info, .palmas_matches = palmas_matches, .sleep_req_info = palma_sleep_req_info, @@ -1411,6 +1415,7 @@ static struct palmas_pmic_driver_data tps65917_ddata = { .ldo_begin = TPS65917_REG_LDO1, .ldo_end = TPS65917_REG_LDO5, .max_reg = TPS65917_NUM_REGS, + .has_regen3 = true, .palmas_regs_info = tps65917_regs_info, .palmas_matches = tps65917_matches, .sleep_req_info = tps65917_sleep_req_info, @@ -1505,7 +1510,7 @@ static void palmas_dt_to_pdata(struct device *dev, pdata->ldo6_vibrator = of_property_read_bool(node, "ti,ldo6-vibrator"); } -static struct of_device_id of_palmas_match_tbl[] = { +static const struct of_device_id of_palmas_match_tbl[] = { { .compatible = "ti,palmas-pmic", .data = &palmas_ddata, @@ -1572,6 +1577,12 @@ static int palmas_regulators_probe(struct platform_device *pdev) if (!pmic) return -ENOMEM; + if (of_device_is_compatible(node, "ti,tps659038-pmic")) { + palmas_generic_regs_info[PALMAS_REG_REGEN2].ctrl_addr = + TPS659038_REGEN2_CTRL; + palmas_ddata.has_regen3 = false; + } + pmic->dev = &pdev->dev; pmic->palmas = palmas; palmas->pmic = pmic; diff --git a/drivers/regulator/qcom_rpm-regulator.c b/drivers/regulator/qcom_rpm-regulator.c index 00c5cc3..e254272 100644 --- a/drivers/regulator/qcom_rpm-regulator.c +++ b/drivers/regulator/qcom_rpm-regulator.c @@ -393,6 +393,28 @@ static int rpm_reg_is_enabled(struct regulator_dev *rdev) return vreg->is_enabled; } +static int rpm_reg_set_load(struct regulator_dev *rdev, int load_uA) +{ + struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); + const struct rpm_reg_parts *parts = vreg->parts; + const struct request_member *req = &parts->ia; + int load_mA = load_uA / 1000; + int max_mA = req->mask >> req->shift; + int ret; + + if (req->mask == 0) + return -EINVAL; + + if (load_mA > max_mA) + load_mA = max_mA; + + mutex_lock(&vreg->lock); + ret = rpm_reg_write(vreg, req, load_mA); + mutex_unlock(&vreg->lock); + + return ret; +} + static struct regulator_ops uV_ops = { .list_voltage = regulator_list_voltage_linear_range, @@ -402,6 +424,8 @@ static struct regulator_ops uV_ops = { .enable = rpm_reg_uV_enable, .disable = rpm_reg_uV_disable, .is_enabled = rpm_reg_is_enabled, + + .set_load = rpm_reg_set_load, }; static struct regulator_ops mV_ops = { @@ -413,6 +437,8 @@ static struct regulator_ops mV_ops = { .enable = rpm_reg_mV_enable, .disable = rpm_reg_mV_disable, .is_enabled = rpm_reg_is_enabled, + + .set_load = rpm_reg_set_load, }; static struct regulator_ops switch_ops = { @@ -581,31 +607,6 @@ static const struct qcom_rpm_reg smb208_smps = { .supports_force_mode_bypass = false, }; -static const struct of_device_id rpm_of_match[] = { - { .compatible = "qcom,rpm-pm8058-pldo", .data = &pm8058_pldo }, - { .compatible = "qcom,rpm-pm8058-nldo", .data = &pm8058_nldo }, - { .compatible = "qcom,rpm-pm8058-smps", .data = &pm8058_smps }, - { .compatible = "qcom,rpm-pm8058-ncp", .data = &pm8058_ncp }, - { .compatible = "qcom,rpm-pm8058-switch", .data = &pm8058_switch }, - - { .compatible = "qcom,rpm-pm8901-pldo", .data = &pm8901_pldo }, - { .compatible = "qcom,rpm-pm8901-nldo", .data = &pm8901_nldo }, - { .compatible = "qcom,rpm-pm8901-ftsmps", .data = &pm8901_ftsmps }, - { .compatible = "qcom,rpm-pm8901-switch", .data = &pm8901_switch }, - - { .compatible = "qcom,rpm-pm8921-pldo", .data = &pm8921_pldo }, - { .compatible = "qcom,rpm-pm8921-nldo", .data = &pm8921_nldo }, - { .compatible = "qcom,rpm-pm8921-nldo1200", .data = &pm8921_nldo1200 }, - { .compatible = "qcom,rpm-pm8921-smps", .data = &pm8921_smps }, - { .compatible = "qcom,rpm-pm8921-ftsmps", .data = &pm8921_ftsmps }, - { .compatible = "qcom,rpm-pm8921-ncp", .data = &pm8921_ncp }, - { .compatible = "qcom,rpm-pm8921-switch", .data = &pm8921_switch }, - - { .compatible = "qcom,rpm-smb208", .data = &smb208_smps }, - { } -}; -MODULE_DEVICE_TABLE(of, rpm_of_match); - static int rpm_reg_set(struct qcom_rpm_reg *vreg, const struct request_member *req, const int value) @@ -619,7 +620,9 @@ static int rpm_reg_set(struct qcom_rpm_reg *vreg, return 0; } -static int rpm_reg_of_parse_freq(struct device *dev, struct qcom_rpm_reg *vreg) +static int rpm_reg_of_parse_freq(struct device *dev, + struct device_node *node, + struct qcom_rpm_reg *vreg) { static const int freq_table[] = { 19200000, 9600000, 6400000, 4800000, 3840000, 3200000, 2740000, @@ -633,7 +636,7 @@ static int rpm_reg_of_parse_freq(struct device *dev, struct qcom_rpm_reg *vreg) int i; key = "qcom,switch-mode-frequency"; - ret = of_property_read_u32(dev->of_node, key, &freq); + ret = of_property_read_u32(node, key, &freq); if (ret) { dev_err(dev, "regulator requires %s property\n", key); return -EINVAL; @@ -650,84 +653,40 @@ static int rpm_reg_of_parse_freq(struct device *dev, struct qcom_rpm_reg *vreg) return -EINVAL; } -static int rpm_reg_probe(struct platform_device *pdev) +static int rpm_reg_of_parse(struct device_node *node, + const struct regulator_desc *desc, + struct regulator_config *config) { - struct regulator_init_data *initdata; - const struct qcom_rpm_reg *template; - const struct of_device_id *match; - struct regulator_config config = { }; - struct regulator_dev *rdev; - struct qcom_rpm_reg *vreg; + struct qcom_rpm_reg *vreg = config->driver_data; + struct device *dev = config->dev; const char *key; u32 force_mode; bool pwm; u32 val; int ret; - match = of_match_device(rpm_of_match, &pdev->dev); - template = match->data; - - vreg = devm_kmalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL); - if (!vreg) { - dev_err(&pdev->dev, "failed to allocate vreg\n"); - return -ENOMEM; - } - memcpy(vreg, template, sizeof(*vreg)); - mutex_init(&vreg->lock); - vreg->dev = &pdev->dev; - vreg->desc.id = -1; - vreg->desc.owner = THIS_MODULE; - vreg->desc.type = REGULATOR_VOLTAGE; - vreg->desc.name = pdev->dev.of_node->name; - vreg->desc.supply_name = "vin"; - - vreg->rpm = dev_get_drvdata(pdev->dev.parent); - if (!vreg->rpm) { - dev_err(&pdev->dev, "unable to retrieve handle to rpm\n"); - return -ENODEV; - } - - initdata = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node, - &vreg->desc); - if (!initdata) - return -EINVAL; - - key = "reg"; - ret = of_property_read_u32(pdev->dev.of_node, key, &val); - if (ret) { - dev_err(&pdev->dev, "failed to read %s\n", key); - return ret; - } - vreg->resource = val; - - if ((vreg->parts->uV.mask || vreg->parts->mV.mask) && - (!initdata->constraints.min_uV || !initdata->constraints.max_uV)) { - dev_err(&pdev->dev, "no voltage specified for regulator\n"); - return -EINVAL; - } - key = "bias-pull-down"; - if (of_property_read_bool(pdev->dev.of_node, key)) { + if (of_property_read_bool(node, key)) { ret = rpm_reg_set(vreg, &vreg->parts->pd, 1); if (ret) { - dev_err(&pdev->dev, "%s is invalid", key); + dev_err(dev, "%s is invalid", key); return ret; } } if (vreg->parts->freq.mask) { - ret = rpm_reg_of_parse_freq(&pdev->dev, vreg); + ret = rpm_reg_of_parse_freq(dev, node, vreg); if (ret < 0) return ret; } if (vreg->parts->pm.mask) { key = "qcom,power-mode-hysteretic"; - pwm = !of_property_read_bool(pdev->dev.of_node, key); + pwm = !of_property_read_bool(node, key); ret = rpm_reg_set(vreg, &vreg->parts->pm, pwm); if (ret) { - dev_err(&pdev->dev, "failed to set power mode\n"); + dev_err(dev, "failed to set power mode\n"); return ret; } } @@ -736,11 +695,11 @@ static int rpm_reg_probe(struct platform_device *pdev) force_mode = -1; key = "qcom,force-mode"; - ret = of_property_read_u32(pdev->dev.of_node, key, &val); + ret = of_property_read_u32(node, key, &val); if (ret == -EINVAL) { val = QCOM_RPM_FORCE_MODE_NONE; } else if (ret < 0) { - dev_err(&pdev->dev, "failed to read %s\n", key); + dev_err(dev, "failed to read %s\n", key); return ret; } @@ -775,25 +734,192 @@ static int rpm_reg_probe(struct platform_device *pdev) } if (force_mode == -1) { - dev_err(&pdev->dev, "invalid force mode\n"); + dev_err(dev, "invalid force mode\n"); return -EINVAL; } ret = rpm_reg_set(vreg, &vreg->parts->fm, force_mode); if (ret) { - dev_err(&pdev->dev, "failed to set force mode\n"); + dev_err(dev, "failed to set force mode\n"); return ret; } } - config.dev = &pdev->dev; - config.init_data = initdata; - config.driver_data = vreg; - config.of_node = pdev->dev.of_node; - rdev = devm_regulator_register(&pdev->dev, &vreg->desc, &config); - if (IS_ERR(rdev)) { - dev_err(&pdev->dev, "can't register regulator\n"); - return PTR_ERR(rdev); + return 0; +} + +struct rpm_regulator_data { + const char *name; + int resource; + const struct qcom_rpm_reg *template; + const char *supply; +}; + +static const struct rpm_regulator_data rpm_pm8058_regulators[] = { + { "l0", QCOM_RPM_PM8058_LDO0, &pm8058_nldo, "vdd_l0_l1_lvs" }, + { "l1", QCOM_RPM_PM8058_LDO1, &pm8058_nldo, "vdd_l0_l1_lvs" }, + { "l2", QCOM_RPM_PM8058_LDO2, &pm8058_pldo, "vdd_l2_l11_l12" }, + { "l3", QCOM_RPM_PM8058_LDO3, &pm8058_pldo, "vdd_l3_l4_l5" }, + { "l4", QCOM_RPM_PM8058_LDO4, &pm8058_pldo, "vdd_l3_l4_l5" }, + { "l5", QCOM_RPM_PM8058_LDO5, &pm8058_pldo, "vdd_l3_l4_l5" }, + { "l6", QCOM_RPM_PM8058_LDO6, &pm8058_pldo, "vdd_l6_l7" }, + { "l7", QCOM_RPM_PM8058_LDO7, &pm8058_pldo, "vdd_l6_l7" }, + { "l8", QCOM_RPM_PM8058_LDO8, &pm8058_pldo, "vdd_l8" }, + { "l9", QCOM_RPM_PM8058_LDO9, &pm8058_pldo, "vdd_l9" }, + { "l10", QCOM_RPM_PM8058_LDO10, &pm8058_pldo, "vdd_l10" }, + { "l11", QCOM_RPM_PM8058_LDO11, &pm8058_pldo, "vdd_l2_l11_l12" }, + { "l12", QCOM_RPM_PM8058_LDO12, &pm8058_pldo, "vdd_l2_l11_l12" }, + { "l13", QCOM_RPM_PM8058_LDO13, &pm8058_pldo, "vdd_l13_l16" }, + { "l14", QCOM_RPM_PM8058_LDO14, &pm8058_pldo, "vdd_l14_l15" }, + { "l15", QCOM_RPM_PM8058_LDO15, &pm8058_pldo, "vdd_l14_l15" }, + { "l16", QCOM_RPM_PM8058_LDO16, &pm8058_pldo, "vdd_l13_l16" }, + { "l17", QCOM_RPM_PM8058_LDO17, &pm8058_pldo, "vdd_l17_l18" }, + { "l18", QCOM_RPM_PM8058_LDO18, &pm8058_pldo, "vdd_l17_l18" }, + { "l19", QCOM_RPM_PM8058_LDO19, &pm8058_pldo, "vdd_l19_l20" }, + { "l20", QCOM_RPM_PM8058_LDO20, &pm8058_pldo, "vdd_l19_l20" }, + { "l21", QCOM_RPM_PM8058_LDO21, &pm8058_nldo, "vdd_l21" }, + { "l22", QCOM_RPM_PM8058_LDO22, &pm8058_nldo, "vdd_l22" }, + { "l23", QCOM_RPM_PM8058_LDO23, &pm8058_nldo, "vdd_l23_l24_l25" }, + { "l24", QCOM_RPM_PM8058_LDO24, &pm8058_nldo, "vdd_l23_l24_l25" }, + { "l25", QCOM_RPM_PM8058_LDO25, &pm8058_nldo, "vdd_l23_l24_l25" }, + + { "s0", QCOM_RPM_PM8058_SMPS0, &pm8058_smps, "vdd_s0" }, + { "s1", QCOM_RPM_PM8058_SMPS1, &pm8058_smps, "vdd_s1" }, + { "s2", QCOM_RPM_PM8058_SMPS2, &pm8058_smps, "vdd_s2" }, + { "s3", QCOM_RPM_PM8058_SMPS3, &pm8058_smps, "vdd_s3" }, + { "s4", QCOM_RPM_PM8058_SMPS4, &pm8058_smps, "vdd_s4" }, + + { "lvs0", QCOM_RPM_PM8058_LVS0, &pm8058_switch, "vdd_l0_l1_lvs" }, + { "lvs1", QCOM_RPM_PM8058_LVS1, &pm8058_switch, "vdd_l0_l1_lvs" }, + + { "ncp", QCOM_RPM_PM8058_NCP, &pm8058_ncp, "vdd_ncp" }, + { } +}; + +static const struct rpm_regulator_data rpm_pm8901_regulators[] = { + { "l0", QCOM_RPM_PM8901_LDO0, &pm8901_nldo, "vdd_l0" }, + { "l1", QCOM_RPM_PM8901_LDO1, &pm8901_pldo, "vdd_l1" }, + { "l2", QCOM_RPM_PM8901_LDO2, &pm8901_pldo, "vdd_l2" }, + { "l3", QCOM_RPM_PM8901_LDO3, &pm8901_pldo, "vdd_l3" }, + { "l4", QCOM_RPM_PM8901_LDO4, &pm8901_pldo, "vdd_l4" }, + { "l5", QCOM_RPM_PM8901_LDO5, &pm8901_pldo, "vdd_l5" }, + { "l6", QCOM_RPM_PM8901_LDO6, &pm8901_pldo, "vdd_l6" }, + + { "s0", QCOM_RPM_PM8901_SMPS0, &pm8901_ftsmps, "vdd_s0" }, + { "s1", QCOM_RPM_PM8901_SMPS1, &pm8901_ftsmps, "vdd_s1" }, + { "s2", QCOM_RPM_PM8901_SMPS2, &pm8901_ftsmps, "vdd_s2" }, + { "s3", QCOM_RPM_PM8901_SMPS3, &pm8901_ftsmps, "vdd_s3" }, + { "s4", QCOM_RPM_PM8901_SMPS4, &pm8901_ftsmps, "vdd_s4" }, + + { "lvs0", QCOM_RPM_PM8901_LVS0, &pm8901_switch, "lvs0_in" }, + { "lvs1", QCOM_RPM_PM8901_LVS1, &pm8901_switch, "lvs1_in" }, + { "lvs2", QCOM_RPM_PM8901_LVS2, &pm8901_switch, "lvs2_in" }, + { "lvs3", QCOM_RPM_PM8901_LVS3, &pm8901_switch, "lvs3_in" }, + + { "mvs", QCOM_RPM_PM8901_MVS, &pm8901_switch, "mvs_in" }, + { } +}; + +static const struct rpm_regulator_data rpm_pm8921_regulators[] = { + { "s1", QCOM_RPM_PM8921_SMPS1, &pm8921_smps, "vdd_s1" }, + { "s2", QCOM_RPM_PM8921_SMPS2, &pm8921_smps, "vdd_s2" }, + { "s3", QCOM_RPM_PM8921_SMPS3, &pm8921_smps }, + { "s4", QCOM_RPM_PM8921_SMPS4, &pm8921_smps, "vdd_s4" }, + { "s7", QCOM_RPM_PM8921_SMPS7, &pm8921_smps, "vdd_s7" }, + { "s8", QCOM_RPM_PM8921_SMPS8, &pm8921_smps, "vdd_s8" }, + + { "l1", QCOM_RPM_PM8921_LDO1, &pm8921_nldo, "vdd_l1_l2_l12_l18" }, + { "l2", QCOM_RPM_PM8921_LDO2, &pm8921_nldo, "vdd_l1_l2_l12_l18" }, + { "l3", QCOM_RPM_PM8921_LDO3, &pm8921_pldo, "vdd_l3_l15_l17" }, + { "l4", QCOM_RPM_PM8921_LDO4, &pm8921_pldo, "vdd_l4_l14" }, + { "l5", QCOM_RPM_PM8921_LDO5, &pm8921_pldo, "vdd_l5_l8_l16" }, + { "l6", QCOM_RPM_PM8921_LDO6, &pm8921_pldo, "vdd_l6_l7" }, + { "l7", QCOM_RPM_PM8921_LDO7, &pm8921_pldo, "vdd_l6_l7" }, + { "l8", QCOM_RPM_PM8921_LDO8, &pm8921_pldo, "vdd_l5_l8_l16" }, + { "l9", QCOM_RPM_PM8921_LDO9, &pm8921_pldo, "vdd_l9_l11" }, + { "l10", QCOM_RPM_PM8921_LDO10, &pm8921_pldo, "vdd_l10_l22" }, + { "l11", QCOM_RPM_PM8921_LDO11, &pm8921_pldo, "vdd_l9_l11" }, + { "l12", QCOM_RPM_PM8921_LDO12, &pm8921_nldo, "vdd_l1_l2_l12_l18" }, + { "l14", QCOM_RPM_PM8921_LDO14, &pm8921_pldo, "vdd_l4_l14" }, + { "l15", QCOM_RPM_PM8921_LDO15, &pm8921_pldo, "vdd_l3_l15_l17" }, + { "l16", QCOM_RPM_PM8921_LDO16, &pm8921_pldo, "vdd_l5_l8_l16" }, + { "l17", QCOM_RPM_PM8921_LDO17, &pm8921_pldo, "vdd_l3_l15_l17" }, + { "l18", QCOM_RPM_PM8921_LDO18, &pm8921_nldo, "vdd_l1_l2_l12_l18" }, + { "l21", QCOM_RPM_PM8921_LDO21, &pm8921_pldo, "vdd_l21_l23_l29" }, + { "l22", QCOM_RPM_PM8921_LDO22, &pm8921_pldo, "vdd_l10_l22" }, + { "l23", QCOM_RPM_PM8921_LDO23, &pm8921_pldo, "vdd_l21_l23_l29" }, + { "l24", QCOM_RPM_PM8921_LDO24, &pm8921_nldo1200, "vdd_l24" }, + { "l25", QCOM_RPM_PM8921_LDO25, &pm8921_nldo1200, "vdd_l25" }, + { "l26", QCOM_RPM_PM8921_LDO26, &pm8921_nldo1200, "vdd_l26" }, + { "l27", QCOM_RPM_PM8921_LDO27, &pm8921_nldo1200, "vdd_l27" }, + { "l28", QCOM_RPM_PM8921_LDO28, &pm8921_nldo1200, "vdd_l28" }, + { "l29", QCOM_RPM_PM8921_LDO29, &pm8921_pldo, "vdd_l21_l23_l29" }, + + { "lvs1", QCOM_RPM_PM8921_LVS1, &pm8921_switch, "vin_lvs1_3_6" }, + { "lvs2", QCOM_RPM_PM8921_LVS2, &pm8921_switch, "vin_lvs2" }, + { "lvs3", QCOM_RPM_PM8921_LVS3, &pm8921_switch, "vin_lvs1_3_6" }, + { "lvs4", QCOM_RPM_PM8921_LVS4, &pm8921_switch, "vin_lvs4_5_7" }, + { "lvs5", QCOM_RPM_PM8921_LVS5, &pm8921_switch, "vin_lvs4_5_7" }, + { "lvs6", QCOM_RPM_PM8921_LVS6, &pm8921_switch, "vin_lvs1_3_6" }, + { "lvs7", QCOM_RPM_PM8921_LVS7, &pm8921_switch, "vin_lvs4_5_7" }, + + { "usb-switch", QCOM_RPM_USB_OTG_SWITCH, &pm8921_switch, "vin_5vs" }, + { "hdmi-switch", QCOM_RPM_HDMI_SWITCH, &pm8921_switch, "vin_5vs" }, + { "ncp", QCOM_RPM_PM8921_NCP, &pm8921_ncp, "vdd_ncp" }, + { } +}; + +static const struct of_device_id rpm_of_match[] = { + { .compatible = "qcom,rpm-pm8058-regulators", .data = &rpm_pm8058_regulators }, + { .compatible = "qcom,rpm-pm8901-regulators", .data = &rpm_pm8901_regulators }, + { .compatible = "qcom,rpm-pm8921-regulators", .data = &rpm_pm8921_regulators }, + { } +}; +MODULE_DEVICE_TABLE(of, rpm_of_match); + +static int rpm_reg_probe(struct platform_device *pdev) +{ + const struct rpm_regulator_data *reg; + const struct of_device_id *match; + struct regulator_config config = { }; + struct regulator_dev *rdev; + struct qcom_rpm_reg *vreg; + struct qcom_rpm *rpm; + + rpm = dev_get_drvdata(pdev->dev.parent); + if (!rpm) { + dev_err(&pdev->dev, "unable to retrieve handle to rpm\n"); + return -ENODEV; + } + + match = of_match_device(rpm_of_match, &pdev->dev); + for (reg = match->data; reg->name; reg++) { + vreg = devm_kmalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL); + if (!vreg) + return -ENOMEM; + + memcpy(vreg, reg->template, sizeof(*vreg)); + mutex_init(&vreg->lock); + + vreg->dev = &pdev->dev; + vreg->resource = reg->resource; + vreg->rpm = rpm; + + vreg->desc.id = -1; + vreg->desc.owner = THIS_MODULE; + vreg->desc.type = REGULATOR_VOLTAGE; + vreg->desc.name = reg->name; + vreg->desc.supply_name = reg->supply; + vreg->desc.of_match = reg->name; + vreg->desc.of_parse_cb = rpm_reg_of_parse; + + config.dev = &pdev->dev; + config.driver_data = vreg; + rdev = devm_regulator_register(&pdev->dev, &vreg->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register %s\n", reg->name); + return PTR_ERR(rdev); + } } return 0; diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c index 1f93b75..3fd4435 100644 --- a/drivers/regulator/rk808-regulator.c +++ b/drivers/regulator/rk808-regulator.c @@ -235,6 +235,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_LDO_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(0), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "LDO_REG2", @@ -249,6 +250,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_LDO_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(1), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "LDO_REG3", @@ -263,6 +265,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_BUCK4_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(2), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "LDO_REG4", @@ -277,6 +280,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_LDO_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(3), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "LDO_REG5", @@ -291,6 +295,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_LDO_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(4), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "LDO_REG6", @@ -305,6 +310,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_LDO_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(5), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "LDO_REG7", @@ -319,6 +325,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_LDO_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(6), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "LDO_REG8", @@ -333,6 +340,7 @@ static const struct regulator_desc rk808_reg[] = { .vsel_mask = RK808_LDO_VSEL_MASK, .enable_reg = RK808_LDO_EN_REG, .enable_mask = BIT(7), + .enable_time = 400, .owner = THIS_MODULE, }, { .name = "SWITCH_REG1", diff --git a/drivers/regulator/stw481x-vmmc.c b/drivers/regulator/stw481x-vmmc.c index 89025f5..7d2ae3e 100644 --- a/drivers/regulator/stw481x-vmmc.c +++ b/drivers/regulator/stw481x-vmmc.c @@ -56,6 +56,7 @@ static int stw481x_vmmc_regulator_probe(struct platform_device *pdev) { struct stw481x *stw481x = dev_get_platdata(&pdev->dev); struct regulator_config config = { }; + struct regulator_dev *rdev; int ret; /* First disable the external VMMC if it's active */ @@ -75,12 +76,11 @@ static int stw481x_vmmc_regulator_probe(struct platform_device *pdev) pdev->dev.of_node, &vmmc_regulator); - stw481x->vmmc_regulator = devm_regulator_register(&pdev->dev, - &vmmc_regulator, &config); - if (IS_ERR(stw481x->vmmc_regulator)) { + rdev = devm_regulator_register(&pdev->dev, &vmmc_regulator, &config); + if (IS_ERR(rdev)) { dev_err(&pdev->dev, "error initializing STw481x VMMC regulator\n"); - return PTR_ERR(stw481x->vmmc_regulator); + return PTR_ERR(rdev); } dev_info(&pdev->dev, "initialized STw481x VMMC regulator\n"); diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index e2cffe0..fb991ec 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/err.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 7ec7c39..95f6b04 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1151,17 +1151,16 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { static irqreturn_t pmic_uv_handler(int irq, void *data) { struct regulator_dev *rdev = (struct regulator_dev *)data; - struct wm8350 *wm8350 = rdev_get_drvdata(rdev); mutex_lock(&rdev->mutex); if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2) regulator_notifier_call_chain(rdev, REGULATOR_EVENT_REGULATION_OUT, - wm8350); + NULL); else regulator_notifier_call_chain(rdev, REGULATOR_EVENT_UNDER_VOLTAGE, - wm8350); + NULL); mutex_unlock(&rdev->mutex); return IRQ_HANDLED; diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 92f6af6..73354ee 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -951,6 +951,7 @@ static int rpmsg_probe(struct virtio_device *vdev) void *bufs_va; int err = 0, i; size_t total_buf_space; + bool notify; vrp = kzalloc(sizeof(*vrp), GFP_KERNEL); if (!vrp) @@ -1030,8 +1031,22 @@ static int rpmsg_probe(struct virtio_device *vdev) } } + /* + * Prepare to kick but don't notify yet - we can't do this before + * device is ready. + */ + notify = virtqueue_kick_prepare(vrp->rvq); + + /* From this point on, we can notify and get callbacks. */ + virtio_device_ready(vdev); + /* tell the remote processor it can start sending messages */ - virtqueue_kick(vrp->rvq); + /* + * this might be concurrent with callbacks, but we are only + * doing notify, not a full kick here, so that's ok. + */ + if (notify) + virtqueue_notify(vrp->rvq); dev_info(&vdev->dev, "rpmsg host is online\n"); diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 472a5ad..c29ba7e 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -55,7 +55,7 @@ static int rtc_suspend(struct device *dev) struct timespec64 delta, delta_delta; int err; - if (has_persistent_clock()) + if (timekeeping_rtc_skipsuspend()) return 0; if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0) @@ -102,7 +102,7 @@ static int rtc_resume(struct device *dev) struct timespec64 sleep_time; int err; - if (has_persistent_clock()) + if (timekeeping_rtc_skipresume()) return 0; rtc_hctosys_ret = -ENODEV; @@ -117,10 +117,6 @@ static int rtc_resume(struct device *dev) return 0; } - if (rtc_valid_tm(&tm) != 0) { - pr_debug("%s: bogus resume time\n", dev_name(&rtc->dev)); - return 0; - } new_rtc.tv_sec = rtc_tm_to_time64(&tm); new_rtc.tv_nsec = 0; diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 37215cf..d43ee40 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -72,7 +72,11 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) err = -ENODEV; else if (rtc->ops->set_time) err = rtc->ops->set_time(rtc->dev.parent, tm); - else if (rtc->ops->set_mmss) { + else if (rtc->ops->set_mmss64) { + time64_t secs64 = rtc_tm_to_time64(tm); + + err = rtc->ops->set_mmss64(rtc->dev.parent, secs64); + } else if (rtc->ops->set_mmss) { time64_t secs64 = rtc_tm_to_time64(tm); err = rtc->ops->set_mmss(rtc->dev.parent, secs64); } else @@ -96,6 +100,8 @@ int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs) if (!rtc->ops) err = -ENODEV; + else if (rtc->ops->set_mmss64) + err = rtc->ops->set_mmss64(rtc->dev.parent, secs); else if (rtc->ops->set_mmss) err = rtc->ops->set_mmss(rtc->dev.parent, secs); else if (rtc->ops->read_time && rtc->ops->set_time) { diff --git a/drivers/rtc/rtc-ab3100.c b/drivers/rtc/rtc-ab3100.c index 1d0340f..9b725c5 100644 --- a/drivers/rtc/rtc-ab3100.c +++ b/drivers/rtc/rtc-ab3100.c @@ -43,21 +43,21 @@ /* * RTC clock functions and device struct declaration */ -static int ab3100_rtc_set_mmss(struct device *dev, unsigned long secs) +static int ab3100_rtc_set_mmss(struct device *dev, time64_t secs) { u8 regs[] = {AB3100_TI0, AB3100_TI1, AB3100_TI2, AB3100_TI3, AB3100_TI4, AB3100_TI5}; unsigned char buf[6]; - u64 fat_time = (u64) secs * AB3100_RTC_CLOCK_RATE * 2; + u64 hw_counter = secs * AB3100_RTC_CLOCK_RATE * 2; int err = 0; int i; - buf[0] = (fat_time) & 0xFF; - buf[1] = (fat_time >> 8) & 0xFF; - buf[2] = (fat_time >> 16) & 0xFF; - buf[3] = (fat_time >> 24) & 0xFF; - buf[4] = (fat_time >> 32) & 0xFF; - buf[5] = (fat_time >> 40) & 0xFF; + buf[0] = (hw_counter) & 0xFF; + buf[1] = (hw_counter >> 8) & 0xFF; + buf[2] = (hw_counter >> 16) & 0xFF; + buf[3] = (hw_counter >> 24) & 0xFF; + buf[4] = (hw_counter >> 32) & 0xFF; + buf[5] = (hw_counter >> 40) & 0xFF; for (i = 0; i < 6; i++) { err = abx500_set_register_interruptible(dev, 0, @@ -75,7 +75,7 @@ static int ab3100_rtc_set_mmss(struct device *dev, unsigned long secs) static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm) { - unsigned long time; + time64_t time; u8 rtcval; int err; @@ -88,7 +88,7 @@ static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm) dev_info(dev, "clock not set (lost power)"); return -EINVAL; } else { - u64 fat_time; + u64 hw_counter; u8 buf[6]; /* Read out time registers */ @@ -98,22 +98,21 @@ static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm) if (err != 0) return err; - fat_time = ((u64) buf[5] << 40) | ((u64) buf[4] << 32) | + hw_counter = ((u64) buf[5] << 40) | ((u64) buf[4] << 32) | ((u64) buf[3] << 24) | ((u64) buf[2] << 16) | ((u64) buf[1] << 8) | (u64) buf[0]; - time = (unsigned long) (fat_time / - (u64) (AB3100_RTC_CLOCK_RATE * 2)); + time = hw_counter / (u64) (AB3100_RTC_CLOCK_RATE * 2); } - rtc_time_to_tm(time, tm); + rtc_time64_to_tm(time, tm); return rtc_valid_tm(tm); } static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) { - unsigned long time; - u64 fat_time; + time64_t time; + u64 hw_counter; u8 buf[6]; u8 rtcval; int err; @@ -134,11 +133,11 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) AB3100_AL0, buf, 4); if (err) return err; - fat_time = ((u64) buf[3] << 40) | ((u64) buf[2] << 32) | + hw_counter = ((u64) buf[3] << 40) | ((u64) buf[2] << 32) | ((u64) buf[1] << 24) | ((u64) buf[0] << 16); - time = (unsigned long) (fat_time / (u64) (AB3100_RTC_CLOCK_RATE * 2)); + time = hw_counter / (u64) (AB3100_RTC_CLOCK_RATE * 2); - rtc_time_to_tm(time, &alarm->time); + rtc_time64_to_tm(time, &alarm->time); return rtc_valid_tm(&alarm->time); } @@ -147,17 +146,17 @@ static int ab3100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) { u8 regs[] = {AB3100_AL0, AB3100_AL1, AB3100_AL2, AB3100_AL3}; unsigned char buf[4]; - unsigned long secs; - u64 fat_time; + time64_t secs; + u64 hw_counter; int err; int i; - rtc_tm_to_time(&alarm->time, &secs); - fat_time = (u64) secs * AB3100_RTC_CLOCK_RATE * 2; - buf[0] = (fat_time >> 16) & 0xFF; - buf[1] = (fat_time >> 24) & 0xFF; - buf[2] = (fat_time >> 32) & 0xFF; - buf[3] = (fat_time >> 40) & 0xFF; + secs = rtc_tm_to_time64(&alarm->time); + hw_counter = secs * AB3100_RTC_CLOCK_RATE * 2; + buf[0] = (hw_counter >> 16) & 0xFF; + buf[1] = (hw_counter >> 24) & 0xFF; + buf[2] = (hw_counter >> 32) & 0xFF; + buf[3] = (hw_counter >> 40) & 0xFF; /* Set the alarm */ for (i = 0; i < 4; i++) { @@ -193,7 +192,7 @@ static int ab3100_rtc_irq_enable(struct device *dev, unsigned int enabled) static const struct rtc_class_ops ab3100_rtc_ops = { .read_time = ab3100_rtc_read_time, - .set_mmss = ab3100_rtc_set_mmss, + .set_mmss64 = ab3100_rtc_set_mmss, .read_alarm = ab3100_rtc_read_alarm, .set_alarm = ab3100_rtc_set_alarm, .alarm_irq_enable = ab3100_rtc_irq_enable, diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index 70a5d94..b283a1a 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -31,6 +31,7 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/suspend.h> #include <linux/uaccess.h> #include "rtc-at91rm9200.h" @@ -54,6 +55,10 @@ static void __iomem *at91_rtc_regs; static int irq; static DEFINE_SPINLOCK(at91_rtc_lock); static u32 at91_rtc_shadow_imr; +static bool suspended; +static DEFINE_SPINLOCK(suspended_lock); +static unsigned long cached_events; +static u32 at91_rtc_imr; static void at91_rtc_write_ier(u32 mask) { @@ -290,7 +295,9 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id) struct rtc_device *rtc = platform_get_drvdata(pdev); unsigned int rtsr; unsigned long events = 0; + int ret = IRQ_NONE; + spin_lock(&suspended_lock); rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_read_imr(); if (rtsr) { /* this interrupt is shared! Is it ours? */ if (rtsr & AT91_RTC_ALARM) @@ -304,14 +311,22 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id) at91_rtc_write(AT91_RTC_SCCR, rtsr); /* clear status reg */ - rtc_update_irq(rtc, 1, events); + if (!suspended) { + rtc_update_irq(rtc, 1, events); - dev_dbg(&pdev->dev, "%s(): num=%ld, events=0x%02lx\n", __func__, - events >> 8, events & 0x000000FF); + dev_dbg(&pdev->dev, "%s(): num=%ld, events=0x%02lx\n", + __func__, events >> 8, events & 0x000000FF); + } else { + cached_events |= events; + at91_rtc_write_idr(at91_rtc_imr); + pm_system_wakeup(); + } - return IRQ_HANDLED; + ret = IRQ_HANDLED; } - return IRQ_NONE; /* not handled */ + spin_unlock(&suspended_lock); + + return ret; } static const struct at91_rtc_config at91rm9200_config = { @@ -401,8 +416,8 @@ static int __init at91_rtc_probe(struct platform_device *pdev) AT91_RTC_CALEV); ret = devm_request_irq(&pdev->dev, irq, at91_rtc_interrupt, - IRQF_SHARED, - "at91_rtc", pdev); + IRQF_SHARED | IRQF_COND_SUSPEND, + "at91_rtc", pdev); if (ret) { dev_err(&pdev->dev, "IRQ %d already in use.\n", irq); return ret; @@ -454,8 +469,6 @@ static void at91_rtc_shutdown(struct platform_device *pdev) /* AT91RM9200 RTC Power management control */ -static u32 at91_rtc_imr; - static int at91_rtc_suspend(struct device *dev) { /* this IRQ is shared with DBGU and other hardware which isn't @@ -464,21 +477,42 @@ static int at91_rtc_suspend(struct device *dev) at91_rtc_imr = at91_rtc_read_imr() & (AT91_RTC_ALARM|AT91_RTC_SECEV); if (at91_rtc_imr) { - if (device_may_wakeup(dev)) + if (device_may_wakeup(dev)) { + unsigned long flags; + enable_irq_wake(irq); - else + + spin_lock_irqsave(&suspended_lock, flags); + suspended = true; + spin_unlock_irqrestore(&suspended_lock, flags); + } else { at91_rtc_write_idr(at91_rtc_imr); + } } return 0; } static int at91_rtc_resume(struct device *dev) { + struct rtc_device *rtc = dev_get_drvdata(dev); + if (at91_rtc_imr) { - if (device_may_wakeup(dev)) + if (device_may_wakeup(dev)) { + unsigned long flags; + + spin_lock_irqsave(&suspended_lock, flags); + + if (cached_events) { + rtc_update_irq(rtc, 1, cached_events); + cached_events = 0; + } + + suspended = false; + spin_unlock_irqrestore(&suspended_lock, flags); + disable_irq_wake(irq); - else - at91_rtc_write_ier(at91_rtc_imr); + } + at91_rtc_write_ier(at91_rtc_imr); } return 0; } diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index 2183fd2..5ccaee3 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c @@ -23,6 +23,7 @@ #include <linux/io.h> #include <linux/mfd/syscon.h> #include <linux/regmap.h> +#include <linux/suspend.h> #include <linux/clk.h> /* @@ -77,6 +78,9 @@ struct sam9_rtc { unsigned int gpbr_offset; int irq; struct clk *sclk; + bool suspended; + unsigned long events; + spinlock_t lock; }; #define rtt_readl(rtc, field) \ @@ -271,14 +275,9 @@ static int at91_rtc_proc(struct device *dev, struct seq_file *seq) return 0; } -/* - * IRQ handler for the RTC - */ -static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc) +static irqreturn_t at91_rtc_cache_events(struct sam9_rtc *rtc) { - struct sam9_rtc *rtc = _rtc; u32 sr, mr; - unsigned long events = 0; /* Shared interrupt may be for another device. Note: reading * SR clears it, so we must only read it in this irq handler! @@ -290,18 +289,54 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc) /* alarm status */ if (sr & AT91_RTT_ALMS) - events |= (RTC_AF | RTC_IRQF); + rtc->events |= (RTC_AF | RTC_IRQF); /* timer update/increment */ if (sr & AT91_RTT_RTTINC) - events |= (RTC_UF | RTC_IRQF); + rtc->events |= (RTC_UF | RTC_IRQF); + + return IRQ_HANDLED; +} + +static void at91_rtc_flush_events(struct sam9_rtc *rtc) +{ + if (!rtc->events) + return; - rtc_update_irq(rtc->rtcdev, 1, events); + rtc_update_irq(rtc->rtcdev, 1, rtc->events); + rtc->events = 0; pr_debug("%s: num=%ld, events=0x%02lx\n", __func__, - events >> 8, events & 0x000000FF); + rtc->events >> 8, rtc->events & 0x000000FF); +} - return IRQ_HANDLED; +/* + * IRQ handler for the RTC + */ +static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc) +{ + struct sam9_rtc *rtc = _rtc; + int ret; + + spin_lock(&rtc->lock); + + ret = at91_rtc_cache_events(rtc); + + /* We're called in suspended state */ + if (rtc->suspended) { + /* Mask irqs coming from this peripheral */ + rtt_writel(rtc, MR, + rtt_readl(rtc, MR) & + ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); + /* Trigger a system wakeup */ + pm_system_wakeup(); + } else { + at91_rtc_flush_events(rtc); + } + + spin_unlock(&rtc->lock); + + return ret; } static const struct rtc_class_ops at91_rtc_ops = { @@ -421,7 +456,8 @@ static int at91_rtc_probe(struct platform_device *pdev) /* register irq handler after we know what name we'll use */ ret = devm_request_irq(&pdev->dev, rtc->irq, at91_rtc_interrupt, - IRQF_SHARED, dev_name(&rtc->rtcdev->dev), rtc); + IRQF_SHARED | IRQF_COND_SUSPEND, + dev_name(&rtc->rtcdev->dev), rtc); if (ret) { dev_dbg(&pdev->dev, "can't share IRQ %d?\n", rtc->irq); return ret; @@ -482,7 +518,12 @@ static int at91_rtc_suspend(struct device *dev) rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); if (rtc->imr) { if (device_may_wakeup(dev) && (mr & AT91_RTT_ALMIEN)) { + unsigned long flags; + enable_irq_wake(rtc->irq); + spin_lock_irqsave(&rtc->lock, flags); + rtc->suspended = true; + spin_unlock_irqrestore(&rtc->lock, flags); /* don't let RTTINC cause wakeups */ if (mr & AT91_RTT_RTTINCIEN) rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN); @@ -499,10 +540,18 @@ static int at91_rtc_resume(struct device *dev) u32 mr; if (rtc->imr) { + unsigned long flags; + if (device_may_wakeup(dev)) disable_irq_wake(rtc->irq); mr = rtt_readl(rtc, MR); rtt_writel(rtc, MR, mr | rtc->imr); + + spin_lock_irqsave(&rtc->lock, flags); + rtc->suspended = false; + at91_rtc_cache_events(rtc); + at91_rtc_flush_events(rtc); + spin_unlock_irqrestore(&rtc->lock, flags); } return 0; diff --git a/drivers/rtc/rtc-ds1685.c b/drivers/rtc/rtc-ds1685.c index 8c3bfcb..803869c 100644 --- a/drivers/rtc/rtc-ds1685.c +++ b/drivers/rtc/rtc-ds1685.c @@ -399,21 +399,21 @@ ds1685_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) * of this RTC chip. We check for it anyways in case support is * added in the future. */ - if (unlikely((seconds >= 0xc0) && (seconds <= 0xff))) + if (unlikely(seconds >= 0xc0)) alrm->time.tm_sec = -1; else alrm->time.tm_sec = ds1685_rtc_bcd2bin(rtc, seconds, RTC_SECS_BCD_MASK, RTC_SECS_BIN_MASK); - if (unlikely((minutes >= 0xc0) && (minutes <= 0xff))) + if (unlikely(minutes >= 0xc0)) alrm->time.tm_min = -1; else alrm->time.tm_min = ds1685_rtc_bcd2bin(rtc, minutes, RTC_MINS_BCD_MASK, RTC_MINS_BIN_MASK); - if (unlikely((hours >= 0xc0) && (hours <= 0xff))) + if (unlikely(hours >= 0xc0)) alrm->time.tm_hour = -1; else alrm->time.tm_hour = ds1685_rtc_bcd2bin(rtc, hours, @@ -472,13 +472,13 @@ ds1685_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) * field, and we only support four fields. We put the support * here anyways for the future. */ - if (unlikely((seconds >= 0xc0) && (seconds <= 0xff))) + if (unlikely(seconds >= 0xc0)) seconds = 0xff; - if (unlikely((minutes >= 0xc0) && (minutes <= 0xff))) + if (unlikely(minutes >= 0xc0)) minutes = 0xff; - if (unlikely((hours >= 0xc0) && (hours <= 0xff))) + if (unlikely(hours >= 0xc0)) hours = 0xff; alrm->time.tm_mon = -1; @@ -528,7 +528,6 @@ ds1685_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) /* ----------------------------------------------------------------------- */ /* /dev/rtcX Interface functions */ -#ifdef CONFIG_RTC_INTF_DEV /** * ds1685_rtc_alarm_irq_enable - replaces ioctl() RTC_AIE on/off. * @dev: pointer to device structure. @@ -557,7 +556,6 @@ ds1685_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) return 0; } -#endif /* ----------------------------------------------------------------------- */ @@ -1612,7 +1610,7 @@ ds1685_rtc_sysfs_time_regs_show(struct device *dev, ds1685_rtc_sysfs_time_regs_lookup(attr->attr.name, false); /* Make sure we actually matched something. */ - if (!bcd_reg_info && !bin_reg_info) + if (!bcd_reg_info || !bin_reg_info) return -EINVAL; /* bcd_reg_info->reg == bin_reg_info->reg. */ @@ -1650,7 +1648,7 @@ ds1685_rtc_sysfs_time_regs_store(struct device *dev, return -EINVAL; /* Make sure we actually matched something. */ - if (!bcd_reg_info && !bin_reg_info) + if (!bcd_reg_info || !bin_reg_info) return -EINVAL; /* Check for a valid range. */ diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c index 5bce904..32df1d8 100644 --- a/drivers/rtc/rtc-mc13xxx.c +++ b/drivers/rtc/rtc-mc13xxx.c @@ -83,20 +83,19 @@ static int mc13xxx_rtc_read_time(struct device *dev, struct rtc_time *tm) return ret; } while (days1 != days2); - rtc_time_to_tm(days1 * SEC_PER_DAY + seconds, tm); + rtc_time64_to_tm((time64_t)days1 * SEC_PER_DAY + seconds, tm); return rtc_valid_tm(tm); } -static int mc13xxx_rtc_set_mmss(struct device *dev, unsigned long secs) +static int mc13xxx_rtc_set_mmss(struct device *dev, time64_t secs) { struct mc13xxx_rtc *priv = dev_get_drvdata(dev); unsigned int seconds, days; unsigned int alarmseconds; int ret; - seconds = secs % SEC_PER_DAY; - days = secs / SEC_PER_DAY; + days = div_s64_rem(secs, SEC_PER_DAY, &seconds); mc13xxx_lock(priv->mc13xxx); @@ -159,7 +158,7 @@ static int mc13xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) { struct mc13xxx_rtc *priv = dev_get_drvdata(dev); unsigned seconds, days; - unsigned long s1970; + time64_t s1970; int enabled, pending; int ret; @@ -189,10 +188,10 @@ out: alarm->enabled = enabled; alarm->pending = pending; - s1970 = days * SEC_PER_DAY + seconds; + s1970 = (time64_t)days * SEC_PER_DAY + seconds; - rtc_time_to_tm(s1970, &alarm->time); - dev_dbg(dev, "%s: %lu\n", __func__, s1970); + rtc_time64_to_tm(s1970, &alarm->time); + dev_dbg(dev, "%s: %lld\n", __func__, (long long)s1970); return 0; } @@ -200,8 +199,8 @@ out: static int mc13xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) { struct mc13xxx_rtc *priv = dev_get_drvdata(dev); - unsigned long s1970; - unsigned seconds, days; + time64_t s1970; + u32 seconds, days; int ret; mc13xxx_lock(priv->mc13xxx); @@ -215,20 +214,17 @@ static int mc13xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) if (unlikely(ret)) goto out; - ret = rtc_tm_to_time(&alarm->time, &s1970); - if (unlikely(ret)) - goto out; + s1970 = rtc_tm_to_time64(&alarm->time); - dev_dbg(dev, "%s: o%2.s %lu\n", __func__, alarm->enabled ? "n" : "ff", - s1970); + dev_dbg(dev, "%s: o%2.s %lld\n", __func__, alarm->enabled ? "n" : "ff", + (long long)s1970); ret = mc13xxx_rtc_irq_enable_unlocked(dev, alarm->enabled, MC13XXX_IRQ_TODA); if (unlikely(ret)) goto out; - seconds = s1970 % SEC_PER_DAY; - days = s1970 / SEC_PER_DAY; + days = div_s64_rem(s1970, SEC_PER_DAY, &seconds); ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCDAYA, days); if (unlikely(ret)) @@ -268,7 +264,7 @@ static irqreturn_t mc13xxx_rtc_update_handler(int irq, void *dev) static const struct rtc_class_ops mc13xxx_rtc_ops = { .read_time = mc13xxx_rtc_read_time, - .set_mmss = mc13xxx_rtc_set_mmss, + .set_mmss64 = mc13xxx_rtc_set_mmss, .read_alarm = mc13xxx_rtc_read_alarm, .set_alarm = mc13xxx_rtc_set_alarm, .alarm_irq_enable = mc13xxx_rtc_alarm_irq_enable, diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c index e2436d1..3a6fd3a 100644 --- a/drivers/rtc/rtc-mrst.c +++ b/drivers/rtc/rtc-mrst.c @@ -413,8 +413,8 @@ static void rtc_mrst_do_remove(struct device *dev) mrst->dev = NULL; } -#ifdef CONFIG_PM -static int mrst_suspend(struct device *dev, pm_message_t mesg) +#ifdef CONFIG_PM_SLEEP +static int mrst_suspend(struct device *dev) { struct mrst_rtc *mrst = dev_get_drvdata(dev); unsigned char tmp; @@ -453,7 +453,7 @@ static int mrst_suspend(struct device *dev, pm_message_t mesg) */ static inline int mrst_poweroff(struct device *dev) { - return mrst_suspend(dev, PMSG_HIBERNATE); + return mrst_suspend(dev); } static int mrst_resume(struct device *dev) @@ -490,9 +490,11 @@ static int mrst_resume(struct device *dev) return 0; } +static SIMPLE_DEV_PM_OPS(mrst_pm_ops, mrst_suspend, mrst_resume); +#define MRST_PM_OPS (&mrst_pm_ops) + #else -#define mrst_suspend NULL -#define mrst_resume NULL +#define MRST_PM_OPS NULL static inline int mrst_poweroff(struct device *dev) { @@ -529,9 +531,8 @@ static struct platform_driver vrtc_mrst_platform_driver = { .remove = vrtc_mrst_platform_remove, .shutdown = vrtc_mrst_platform_shutdown, .driver = { - .name = (char *) driver_name, - .suspend = mrst_suspend, - .resume = mrst_resume, + .name = driver_name, + .pm = MRST_PM_OPS, } }; diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c index 3c3f8d1..09d422b 100644 --- a/drivers/rtc/rtc-mxc.c +++ b/drivers/rtc/rtc-mxc.c @@ -106,7 +106,7 @@ static inline int is_imx1_rtc(struct rtc_plat_data *data) * This function is used to obtain the RTC time or the alarm value in * second. */ -static u32 get_alarm_or_time(struct device *dev, int time_alarm) +static time64_t get_alarm_or_time(struct device *dev, int time_alarm) { struct platform_device *pdev = to_platform_device(dev); struct rtc_plat_data *pdata = platform_get_drvdata(pdev); @@ -129,29 +129,28 @@ static u32 get_alarm_or_time(struct device *dev, int time_alarm) hr = hr_min >> 8; min = hr_min & 0xff; - return (((day * 24 + hr) * 60) + min) * 60 + sec; + return ((((time64_t)day * 24 + hr) * 60) + min) * 60 + sec; } /* * This function sets the RTC alarm value or the time value. */ -static void set_alarm_or_time(struct device *dev, int time_alarm, u32 time) +static void set_alarm_or_time(struct device *dev, int time_alarm, time64_t time) { - u32 day, hr, min, sec, temp; + u32 tod, day, hr, min, sec, temp; struct platform_device *pdev = to_platform_device(dev); struct rtc_plat_data *pdata = platform_get_drvdata(pdev); void __iomem *ioaddr = pdata->ioaddr; - day = time / 86400; - time -= day * 86400; + day = div_s64_rem(time, 86400, &tod); /* time is within a day now */ - hr = time / 3600; - time -= hr * 3600; + hr = tod / 3600; + tod -= hr * 3600; /* time is within an hour now */ - min = time / 60; - sec = time - min * 60; + min = tod / 60; + sec = tod - min * 60; temp = (hr << 8) + min; @@ -173,29 +172,18 @@ static void set_alarm_or_time(struct device *dev, int time_alarm, u32 time) * This function updates the RTC alarm registers and then clears all the * interrupt status bits. */ -static int rtc_update_alarm(struct device *dev, struct rtc_time *alrm) +static void rtc_update_alarm(struct device *dev, struct rtc_time *alrm) { - struct rtc_time alarm_tm, now_tm; - unsigned long now, time; + time64_t time; struct platform_device *pdev = to_platform_device(dev); struct rtc_plat_data *pdata = platform_get_drvdata(pdev); void __iomem *ioaddr = pdata->ioaddr; - now = get_alarm_or_time(dev, MXC_RTC_TIME); - rtc_time_to_tm(now, &now_tm); - alarm_tm.tm_year = now_tm.tm_year; - alarm_tm.tm_mon = now_tm.tm_mon; - alarm_tm.tm_mday = now_tm.tm_mday; - alarm_tm.tm_hour = alrm->tm_hour; - alarm_tm.tm_min = alrm->tm_min; - alarm_tm.tm_sec = alrm->tm_sec; - rtc_tm_to_time(&alarm_tm, &time); + time = rtc_tm_to_time64(alrm); /* clear all the interrupt status bits */ writew(readw(ioaddr + RTC_RTCISR), ioaddr + RTC_RTCISR); set_alarm_or_time(dev, MXC_RTC_ALARM, time); - - return 0; } static void mxc_rtc_irq_enable(struct device *dev, unsigned int bit, @@ -283,14 +271,14 @@ static int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) */ static int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm) { - u32 val; + time64_t val; /* Avoid roll-over from reading the different registers */ do { val = get_alarm_or_time(dev, MXC_RTC_TIME); } while (val != get_alarm_or_time(dev, MXC_RTC_TIME)); - rtc_time_to_tm(val, tm); + rtc_time64_to_tm(val, tm); return 0; } @@ -298,7 +286,7 @@ static int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm) /* * This function sets the internal RTC time based on tm in Gregorian date. */ -static int mxc_rtc_set_mmss(struct device *dev, unsigned long time) +static int mxc_rtc_set_mmss(struct device *dev, time64_t time) { struct platform_device *pdev = to_platform_device(dev); struct rtc_plat_data *pdata = platform_get_drvdata(pdev); @@ -309,9 +297,9 @@ static int mxc_rtc_set_mmss(struct device *dev, unsigned long time) if (is_imx1_rtc(pdata)) { struct rtc_time tm; - rtc_time_to_tm(time, &tm); + rtc_time64_to_tm(time, &tm); tm.tm_year = 70; - rtc_tm_to_time(&tm, &time); + time = rtc_tm_to_time64(&tm); } /* Avoid roll-over from reading the different registers */ @@ -333,7 +321,7 @@ static int mxc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) struct rtc_plat_data *pdata = platform_get_drvdata(pdev); void __iomem *ioaddr = pdata->ioaddr; - rtc_time_to_tm(get_alarm_or_time(dev, MXC_RTC_ALARM), &alrm->time); + rtc_time64_to_tm(get_alarm_or_time(dev, MXC_RTC_ALARM), &alrm->time); alrm->pending = ((readw(ioaddr + RTC_RTCISR) & RTC_ALM_BIT)) ? 1 : 0; return 0; @@ -346,11 +334,8 @@ static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct platform_device *pdev = to_platform_device(dev); struct rtc_plat_data *pdata = platform_get_drvdata(pdev); - int ret; - ret = rtc_update_alarm(dev, &alrm->time); - if (ret) - return ret; + rtc_update_alarm(dev, &alrm->time); memcpy(&pdata->g_rtc_alarm, &alrm->time, sizeof(struct rtc_time)); mxc_rtc_irq_enable(dev, RTC_ALM_BIT, alrm->enabled); @@ -362,7 +347,7 @@ static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) static struct rtc_class_ops mxc_rtc_ops = { .release = mxc_rtc_release, .read_time = mxc_rtc_read_time, - .set_mmss = mxc_rtc_set_mmss, + .set_mmss64 = mxc_rtc_set_mmss, .read_alarm = mxc_rtc_read_alarm, .set_alarm = mxc_rtc_set_alarm, .alarm_irq_enable = mxc_rtc_alarm_irq_enable, diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 4241eea..f4cf685 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -849,6 +849,7 @@ static struct s3c_rtc_data const s3c2443_rtc_data = { static struct s3c_rtc_data const s3c6410_rtc_data = { .max_user_freq = 32768, + .needs_src_clk = true, .irq_handler = s3c6410_rtc_irq, .set_freq = s3c6410_rtc_setfreq, .enable_tick = s3c6410_rtc_enable_tick, diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c index 8f86fa9..3a2da4c 100644 --- a/drivers/rtc/rtc-test.c +++ b/drivers/rtc/rtc-test.c @@ -13,6 +13,10 @@ #include <linux/rtc.h> #include <linux/platform_device.h> +static int test_mmss64; +module_param(test_mmss64, int, 0644); +MODULE_PARM_DESC(test_mmss64, "Test struct rtc_class_ops.set_mmss64()."); + static struct platform_device *test0 = NULL, *test1 = NULL; static int test_rtc_read_alarm(struct device *dev, @@ -30,7 +34,13 @@ static int test_rtc_set_alarm(struct device *dev, static int test_rtc_read_time(struct device *dev, struct rtc_time *tm) { - rtc_time_to_tm(get_seconds(), tm); + rtc_time64_to_tm(ktime_get_real_seconds(), tm); + return 0; +} + +static int test_rtc_set_mmss64(struct device *dev, time64_t secs) +{ + dev_info(dev, "%s, secs = %lld\n", __func__, (long long)secs); return 0; } @@ -55,7 +65,7 @@ static int test_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) return 0; } -static const struct rtc_class_ops test_rtc_ops = { +static struct rtc_class_ops test_rtc_ops = { .proc = test_rtc_proc, .read_time = test_rtc_read_time, .read_alarm = test_rtc_read_alarm, @@ -101,6 +111,11 @@ static int test_probe(struct platform_device *plat_dev) int err; struct rtc_device *rtc; + if (test_mmss64) { + test_rtc_ops.set_mmss64 = test_rtc_set_mmss64; + test_rtc_ops.set_mmss = NULL; + } + rtc = devm_rtc_device_register(&plat_dev->dev, "test", &test_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) { diff --git a/drivers/rtc/systohc.c b/drivers/rtc/systohc.c index eb71872..7728d5e 100644 --- a/drivers/rtc/systohc.c +++ b/drivers/rtc/systohc.c @@ -11,7 +11,7 @@ * rtc_set_ntp_time - Save NTP synchronized time to the RTC * @now: Current time of day * - * Replacement for the NTP platform function update_persistent_clock + * Replacement for the NTP platform function update_persistent_clock64 * that stores time for later retrieval by rtc_hctosys. * * Returns 0 on successful RTC update, -ENODEV if a RTC update is not @@ -35,7 +35,10 @@ int rtc_set_ntp_time(struct timespec64 now) if (rtc) { /* rtc_hctosys exclusively uses UTC, so we call set_time here, * not set_mmss. */ - if (rtc->ops && (rtc->ops->set_time || rtc->ops->set_mmss)) + if (rtc->ops && + (rtc->ops->set_time || + rtc->ops->set_mmss64 || + rtc->ops->set_mmss)) err = rtc_set_time(rtc, &tm); rtc_class_close(rtc); } diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 96128cb..da21281 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -547,7 +547,7 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char * parse input */ num_of_segments = 0; - for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) { + for (i = 0; (i < count && (buf[i] != '\0') && (buf[i] != '\n')); i++) { for (j = i; (buf[j] != ':') && (buf[j] != '\0') && (buf[j] != '\n') && diff --git a/drivers/s390/block/scm_blk_cluster.c b/drivers/s390/block/scm_blk_cluster.c index 09db452..7497ddde 100644 --- a/drivers/s390/block/scm_blk_cluster.c +++ b/drivers/s390/block/scm_blk_cluster.c @@ -92,7 +92,7 @@ bool scm_reserve_cluster(struct scm_request *scmrq) add = 0; continue; } - for (pos = 0; pos <= iter->aob->request.msb_count; pos++) { + for (pos = 0; pos < iter->aob->request.msb_count; pos++) { if (clusters_intersect(req, iter->request[pos]) && (rq_data_dir(req) == WRITE || rq_data_dir(iter->request[pos]) == WRITE)) { diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index a7cc618..923a2b5 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -5734,9 +5734,9 @@ free_port: hba_free: if (phba->msix_enabled) pci_disable_msix(phba->pcidev); - iscsi_host_remove(phba->shost); pci_dev_put(phba->pcidev); iscsi_host_free(phba->shost); + pci_set_drvdata(pcidev, NULL); disable_pci: pci_disable_device(pcidev); return ret; diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 9219953..d9afc51 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -6815,7 +6815,8 @@ static struct ata_port_operations ipr_sata_ops = { }; static struct ata_port_info sata_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA, + .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | + ATA_FLAG_SAS_HOST, .pio_mask = ATA_PIO4_ONLY, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 932d9cc..9c706d8 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -547,7 +547,8 @@ static struct ata_port_operations sas_sata_ops = { }; static struct ata_port_info sata_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, + .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ | + ATA_FLAG_SAS_HOST, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 62b58d3..60de662 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -500,6 +500,7 @@ static void sas_revalidate_domain(struct work_struct *work) struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; struct sas_ha_struct *ha = port->ha; + struct domain_device *ddev = port->port_dev; /* prevent revalidation from finding sata links in recovery */ mutex_lock(&ha->disco_mutex); @@ -514,8 +515,9 @@ static void sas_revalidate_domain(struct work_struct *work) SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id, task_pid_nr(current)); - if (port->port_dev) - res = sas_ex_revalidate_domain(port->port_dev); + if (ddev && (ddev->dev_type == SAS_FANOUT_EXPANDER_DEVICE || + ddev->dev_type == SAS_EDGE_EXPANDER_DEVICE)) + res = sas_ex_revalidate_domain(ddev); SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n", port->id, task_pid_nr(current), res); diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 99f43b7..ab4879e 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -1596,7 +1596,7 @@ static int tcm_qla2xxx_check_initiator_node_acl( /* * Finally register the new FC Nexus with TCM */ - __transport_register_session(se_nacl->se_tpg, se_nacl, se_sess, sess); + transport_register_session(se_nacl->se_tpg, se_nacl, se_sess, sess); return 0; } diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 54d7a6c..b1a2631 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1311,9 +1311,11 @@ scsi_prep_state_check(struct scsi_device *sdev, struct request *req) "rejecting I/O to dead device\n"); ret = BLKPREP_KILL; break; - case SDEV_QUIESCE: case SDEV_BLOCK: case SDEV_CREATED_BLOCK: + ret = BLKPREP_DEFER; + break; + case SDEV_QUIESCE: /* * If the devices is blocked we defer normal commands. */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 5d60a86..2aa85e3 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4225,22 +4225,15 @@ static struct scsi_host_template ufshcd_driver_template = { static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg, int ua) { - int ret = 0; - struct regulator *reg = vreg->reg; - const char *name = vreg->name; + int ret; - BUG_ON(!vreg); + if (!vreg) + return 0; - ret = regulator_set_optimum_mode(reg, ua); - if (ret >= 0) { - /* - * regulator_set_optimum_mode() returns new regulator - * mode upon success. - */ - ret = 0; - } else { - dev_err(dev, "%s: %s set optimum mode(ua=%d) failed, err=%d\n", - __func__, name, ua, ret); + ret = regulator_set_load(vreg->reg, ua); + if (ret < 0) { + dev_err(dev, "%s: %s set load (ua=%d) failed, err=%d\n", + __func__, vreg->name, ua, ret); } return ret; @@ -4249,18 +4242,12 @@ static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg, static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba, struct ufs_vreg *vreg) { - if (!vreg) - return 0; - return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA); } static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba, struct ufs_vreg *vreg) { - if (!vreg) - return 0; - return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA); } diff --git a/drivers/sh/pm_runtime.c b/drivers/sh/pm_runtime.c index f3ee439d..cd4c293 100644 --- a/drivers/sh/pm_runtime.c +++ b/drivers/sh/pm_runtime.c @@ -81,7 +81,9 @@ static int __init sh_pm_runtime_init(void) if (!of_machine_is_compatible("renesas,emev2") && !of_machine_is_compatible("renesas,r7s72100") && !of_machine_is_compatible("renesas,r8a73a4") && +#ifndef CONFIG_PM_GENERIC_DOMAINS_OF !of_machine_is_compatible("renesas,r8a7740") && +#endif !of_machine_is_compatible("renesas,r8a7778") && !of_machine_is_compatible("renesas,r8a7779") && !of_machine_is_compatible("renesas,r8a7790") && diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index ab8dfbe..198f96b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -159,10 +159,9 @@ config SPI_BUTTERFLY config SPI_CADENCE tristate "Cadence SPI controller" - depends on ARM help This selects the Cadence SPI controller master driver - used by Xilinx Zynq. + used by Xilinx Zynq and ZynqMP. config SPI_CLPS711X tristate "CLPS711X host SPI controller" @@ -632,7 +631,7 @@ config SPI_DW_PCI config SPI_DW_MID_DMA bool "DMA support for DW SPI controller on Intel MID platform" - depends on SPI_DW_PCI && INTEL_MID_DMAC + depends on SPI_DW_PCI && DW_DMAC_PCI config SPI_DW_MMIO tristate "Memory-mapped io interface driver for DW SPI core" diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 9af7841..a2f40b1 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -180,11 +180,17 @@ | SPI_BF(name, value)) /* Register access macros */ +#ifdef CONFIG_AVR32 #define spi_readl(port, reg) \ __raw_readl((port)->regs + SPI_##reg) #define spi_writel(port, reg, value) \ __raw_writel((value), (port)->regs + SPI_##reg) - +#else +#define spi_readl(port, reg) \ + readl_relaxed((port)->regs + SPI_##reg) +#define spi_writel(port, reg, value) \ + writel_relaxed((value), (port)->regs + SPI_##reg) +#endif /* use PIO for small transfers, avoiding DMA setup/teardown overhead and * cache operations; better heuristics consider wordsize and bitrate. */ @@ -764,17 +770,17 @@ static void atmel_spi_pdc_next_xfer(struct spi_master *master, (unsigned long long)xfer->rx_dma); } - /* REVISIT: We're waiting for ENDRX before we start the next + /* REVISIT: We're waiting for RXBUFF before we start the next * transfer because we need to handle some difficult timing - * issues otherwise. If we wait for ENDTX in one transfer and - * then starts waiting for ENDRX in the next, it's difficult - * to tell the difference between the ENDRX interrupt we're - * actually waiting for and the ENDRX interrupt of the + * issues otherwise. If we wait for TXBUFE in one transfer and + * then starts waiting for RXBUFF in the next, it's difficult + * to tell the difference between the RXBUFF interrupt we're + * actually waiting for and the RXBUFF interrupt of the * previous transfer. * * It should be doable, though. Just not now... */ - spi_writel(as, IER, SPI_BIT(ENDRX) | SPI_BIT(OVRES)); + spi_writel(as, IER, SPI_BIT(RXBUFF) | SPI_BIT(OVRES)); spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN)); } diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 419a782..f63864a 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -3,6 +3,7 @@ * * Copyright (C) 2012 Chris Boot * Copyright (C) 2013 Stephen Warren + * Copyright (C) 2015 Martin Sperl * * This driver is inspired by: * spi-ath79.c, Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org> @@ -29,6 +30,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> +#include <linux/of_gpio.h> #include <linux/of_device.h> #include <linux/spi/spi.h> @@ -66,8 +68,10 @@ #define BCM2835_SPI_CS_CS_10 0x00000002 #define BCM2835_SPI_CS_CS_01 0x00000001 -#define BCM2835_SPI_TIMEOUT_MS 30000 -#define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS) +#define BCM2835_SPI_POLLING_LIMIT_US 30 +#define BCM2835_SPI_TIMEOUT_MS 30000 +#define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \ + | SPI_NO_CS | SPI_3WIRE) #define DRV_NAME "spi-bcm2835" @@ -75,10 +79,10 @@ struct bcm2835_spi { void __iomem *regs; struct clk *clk; int irq; - struct completion done; const u8 *tx_buf; u8 *rx_buf; - int len; + int tx_len; + int rx_len; }; static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg) @@ -91,205 +95,315 @@ static inline void bcm2835_wr(struct bcm2835_spi *bs, unsigned reg, u32 val) writel(val, bs->regs + reg); } -static inline void bcm2835_rd_fifo(struct bcm2835_spi *bs, int len) +static inline void bcm2835_rd_fifo(struct bcm2835_spi *bs) { u8 byte; - while (len--) { + while ((bs->rx_len) && + (bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_RXD)) { byte = bcm2835_rd(bs, BCM2835_SPI_FIFO); if (bs->rx_buf) *bs->rx_buf++ = byte; + bs->rx_len--; } } -static inline void bcm2835_wr_fifo(struct bcm2835_spi *bs, int len) +static inline void bcm2835_wr_fifo(struct bcm2835_spi *bs) { u8 byte; - if (len > bs->len) - len = bs->len; - - while (len--) { + while ((bs->tx_len) && + (bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_TXD)) { byte = bs->tx_buf ? *bs->tx_buf++ : 0; bcm2835_wr(bs, BCM2835_SPI_FIFO, byte); - bs->len--; + bs->tx_len--; } } +static void bcm2835_spi_reset_hw(struct spi_master *master) +{ + struct bcm2835_spi *bs = spi_master_get_devdata(master); + u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); + + /* Disable SPI interrupts and transfer */ + cs &= ~(BCM2835_SPI_CS_INTR | + BCM2835_SPI_CS_INTD | + BCM2835_SPI_CS_TA); + /* and reset RX/TX FIFOS */ + cs |= BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX; + + /* and reset the SPI_HW */ + bcm2835_wr(bs, BCM2835_SPI_CS, cs); +} + static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id) { struct spi_master *master = dev_id; struct bcm2835_spi *bs = spi_master_get_devdata(master); - u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); - /* - * RXR - RX needs Reading. This means 12 (or more) bytes have been - * transmitted and hence 12 (or more) bytes have been received. - * - * The FIFO is 16-bytes deep. We check for this interrupt to keep the - * FIFO full; we have a 4-byte-time buffer for IRQ latency. We check - * this before DONE (TX empty) just in case we delayed processing this - * interrupt for some reason. - * - * We only check for this case if we have more bytes to TX; at the end - * of the transfer, we ignore this pipelining optimization, and let - * bcm2835_spi_finish_transfer() drain the RX FIFO. + /* Read as many bytes as possible from FIFO */ + bcm2835_rd_fifo(bs); + /* Write as many bytes as possible to FIFO */ + bcm2835_wr_fifo(bs); + + /* based on flags decide if we can finish the transfer */ + if (bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_DONE) { + /* Transfer complete - reset SPI HW */ + bcm2835_spi_reset_hw(master); + /* wake up the framework */ + complete(&master->xfer_completion); + } + + return IRQ_HANDLED; +} + +static int bcm2835_spi_transfer_one_poll(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr, + u32 cs, + unsigned long xfer_time_us) +{ + struct bcm2835_spi *bs = spi_master_get_devdata(master); + unsigned long timeout = jiffies + + max(4 * xfer_time_us * HZ / 1000000, 2uL); + + /* enable HW block without interrupts */ + bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA); + + /* set timeout to 4x the expected time, or 2 jiffies */ + /* loop until finished the transfer */ + while (bs->rx_len) { + /* read from fifo as much as possible */ + bcm2835_rd_fifo(bs); + /* fill in tx fifo as much as possible */ + bcm2835_wr_fifo(bs); + /* if we still expect some data after the read, + * check for a possible timeout + */ + if (bs->rx_len && time_after(jiffies, timeout)) { + /* Transfer complete - reset SPI HW */ + bcm2835_spi_reset_hw(master); + /* and return timeout */ + return -ETIMEDOUT; + } + } + + /* Transfer complete - reset SPI HW */ + bcm2835_spi_reset_hw(master); + /* and return without waiting for completion */ + return 0; +} + +static int bcm2835_spi_transfer_one_irq(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr, + u32 cs) +{ + struct bcm2835_spi *bs = spi_master_get_devdata(master); + + /* fill in fifo if we have gpio-cs + * note that there have been rare events where the native-CS + * flapped for <1us which may change the behaviour + * with gpio-cs this does not happen, so it is implemented + * only for this case */ - if (bs->len && (cs & BCM2835_SPI_CS_RXR)) { - /* Read 12 bytes of data */ - bcm2835_rd_fifo(bs, 12); - - /* Write up to 12 bytes */ - bcm2835_wr_fifo(bs, 12); - - /* - * We must have written something to the TX FIFO due to the - * bs->len check above, so cannot be DONE. Hence, return - * early. Note that DONE could also be set if we serviced an - * RXR interrupt really late. + if (gpio_is_valid(spi->cs_gpio)) { + /* enable HW block, but without interrupts enabled + * this would triggern an immediate interrupt */ - return IRQ_HANDLED; + bcm2835_wr(bs, BCM2835_SPI_CS, + cs | BCM2835_SPI_CS_TA); + /* fill in tx fifo as much as possible */ + bcm2835_wr_fifo(bs); } /* - * DONE - TX empty. This occurs when we first enable the transfer - * since we do not pre-fill the TX FIFO. At any other time, given that - * we refill the TX FIFO above based on RXR, and hence ignore DONE if - * RXR is set, DONE really does mean end-of-transfer. + * Enable the HW block. This will immediately trigger a DONE (TX + * empty) interrupt, upon which we will fill the TX FIFO with the + * first TX bytes. Pre-filling the TX FIFO here to avoid the + * interrupt doesn't work:-( */ - if (cs & BCM2835_SPI_CS_DONE) { - if (bs->len) { /* First interrupt in a transfer */ - bcm2835_wr_fifo(bs, 16); - } else { /* Transfer complete */ - /* Disable SPI interrupts */ - cs &= ~(BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD); - bcm2835_wr(bs, BCM2835_SPI_CS, cs); - - /* - * Wake up bcm2835_spi_transfer_one(), which will call - * bcm2835_spi_finish_transfer(), to drain the RX FIFO. - */ - complete(&bs->done); - } - - return IRQ_HANDLED; - } + cs |= BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD | BCM2835_SPI_CS_TA; + bcm2835_wr(bs, BCM2835_SPI_CS, cs); - return IRQ_NONE; + /* signal that we need to wait for completion */ + return 1; } -static int bcm2835_spi_start_transfer(struct spi_device *spi, - struct spi_transfer *tfr) +static int bcm2835_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr) { - struct bcm2835_spi *bs = spi_master_get_devdata(spi->master); + struct bcm2835_spi *bs = spi_master_get_devdata(master); unsigned long spi_hz, clk_hz, cdiv; - u32 cs = BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD | BCM2835_SPI_CS_TA; + unsigned long spi_used_hz, xfer_time_us; + u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); + /* set clock */ spi_hz = tfr->speed_hz; clk_hz = clk_get_rate(bs->clk); if (spi_hz >= clk_hz / 2) { cdiv = 2; /* clk_hz/2 is the fastest we can go */ } else if (spi_hz) { - /* CDIV must be a power of two */ - cdiv = roundup_pow_of_two(DIV_ROUND_UP(clk_hz, spi_hz)); + /* CDIV must be a multiple of two */ + cdiv = DIV_ROUND_UP(clk_hz, spi_hz); + cdiv += (cdiv % 2); if (cdiv >= 65536) cdiv = 0; /* 0 is the slowest we can go */ - } else + } else { cdiv = 0; /* 0 is the slowest we can go */ + } + spi_used_hz = cdiv ? (clk_hz / cdiv) : (clk_hz / 65536); + bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv); + /* handle all the modes */ + if ((spi->mode & SPI_3WIRE) && (tfr->rx_buf)) + cs |= BCM2835_SPI_CS_REN; if (spi->mode & SPI_CPOL) cs |= BCM2835_SPI_CS_CPOL; if (spi->mode & SPI_CPHA) cs |= BCM2835_SPI_CS_CPHA; - if (!(spi->mode & SPI_NO_CS)) { - if (spi->mode & SPI_CS_HIGH) { - cs |= BCM2835_SPI_CS_CSPOL; - cs |= BCM2835_SPI_CS_CSPOL0 << spi->chip_select; - } - - cs |= spi->chip_select; - } + /* for gpio_cs set dummy CS so that no HW-CS get changed + * we can not run this in bcm2835_spi_set_cs, as it does + * not get called for cs_gpio cases, so we need to do it here + */ + if (gpio_is_valid(spi->cs_gpio) || (spi->mode & SPI_NO_CS)) + cs |= BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01; - reinit_completion(&bs->done); + /* set transmit buffers and length */ bs->tx_buf = tfr->tx_buf; bs->rx_buf = tfr->rx_buf; - bs->len = tfr->len; + bs->tx_len = tfr->len; + bs->rx_len = tfr->len; - bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv); - /* - * Enable the HW block. This will immediately trigger a DONE (TX - * empty) interrupt, upon which we will fill the TX FIFO with the - * first TX bytes. Pre-filling the TX FIFO here to avoid the - * interrupt doesn't work:-( - */ - bcm2835_wr(bs, BCM2835_SPI_CS, cs); + /* calculate the estimated time in us the transfer runs */ + xfer_time_us = tfr->len + * 9 /* clocks/byte - SPI-HW waits 1 clock after each byte */ + * 1000000 / spi_used_hz; - return 0; + /* for short requests run polling*/ + if (xfer_time_us <= BCM2835_SPI_POLLING_LIMIT_US) + return bcm2835_spi_transfer_one_poll(master, spi, tfr, + cs, xfer_time_us); + + return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs); } -static int bcm2835_spi_finish_transfer(struct spi_device *spi, - struct spi_transfer *tfr, bool cs_change) +static void bcm2835_spi_handle_err(struct spi_master *master, + struct spi_message *msg) { - struct bcm2835_spi *bs = spi_master_get_devdata(spi->master); - u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); + bcm2835_spi_reset_hw(master); +} + +static void bcm2835_spi_set_cs(struct spi_device *spi, bool gpio_level) +{ + /* + * we can assume that we are "native" as per spi_set_cs + * calling us ONLY when cs_gpio is not set + * we can also assume that we are CS < 3 as per bcm2835_spi_setup + * we would not get called because of error handling there. + * the level passed is the electrical level not enabled/disabled + * so it has to get translated back to enable/disable + * see spi_set_cs in spi.c for the implementation + */ - /* Drain RX FIFO */ - while (cs & BCM2835_SPI_CS_RXD) { - bcm2835_rd_fifo(bs, 1); - cs = bcm2835_rd(bs, BCM2835_SPI_CS); + struct spi_master *master = spi->master; + struct bcm2835_spi *bs = spi_master_get_devdata(master); + u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); + bool enable; + + /* calculate the enable flag from the passed gpio_level */ + enable = (spi->mode & SPI_CS_HIGH) ? gpio_level : !gpio_level; + + /* set flags for "reverse" polarity in the registers */ + if (spi->mode & SPI_CS_HIGH) { + /* set the correct CS-bits */ + cs |= BCM2835_SPI_CS_CSPOL; + cs |= BCM2835_SPI_CS_CSPOL0 << spi->chip_select; + } else { + /* clean the CS-bits */ + cs &= ~BCM2835_SPI_CS_CSPOL; + cs &= ~(BCM2835_SPI_CS_CSPOL0 << spi->chip_select); } - if (tfr->delay_usecs) - udelay(tfr->delay_usecs); + /* select the correct chip_select depending on disabled/enabled */ + if (enable) { + /* set cs correctly */ + if (spi->mode & SPI_NO_CS) { + /* use the "undefined" chip-select */ + cs |= BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01; + } else { + /* set the chip select */ + cs &= ~(BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01); + cs |= spi->chip_select; + } + } else { + /* disable CSPOL which puts HW-CS into deselected state */ + cs &= ~BCM2835_SPI_CS_CSPOL; + /* use the "undefined" chip-select as precaution */ + cs |= BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01; + } - if (cs_change) - /* Clear TA flag */ - bcm2835_wr(bs, BCM2835_SPI_CS, cs & ~BCM2835_SPI_CS_TA); + /* finally set the calculated flags in SPI_CS */ + bcm2835_wr(bs, BCM2835_SPI_CS, cs); +} - return 0; +static int chip_match_name(struct gpio_chip *chip, void *data) +{ + return !strcmp(chip->label, data); } -static int bcm2835_spi_transfer_one(struct spi_master *master, - struct spi_message *mesg) +static int bcm2835_spi_setup(struct spi_device *spi) { - struct bcm2835_spi *bs = spi_master_get_devdata(master); - struct spi_transfer *tfr; - struct spi_device *spi = mesg->spi; - int err = 0; - unsigned int timeout; - bool cs_change; - - list_for_each_entry(tfr, &mesg->transfers, transfer_list) { - err = bcm2835_spi_start_transfer(spi, tfr); - if (err) - goto out; - - timeout = wait_for_completion_timeout(&bs->done, - msecs_to_jiffies(BCM2835_SPI_TIMEOUT_MS)); - if (!timeout) { - err = -ETIMEDOUT; - goto out; - } + int err; + struct gpio_chip *chip; + /* + * sanity checking the native-chipselects + */ + if (spi->mode & SPI_NO_CS) + return 0; + if (gpio_is_valid(spi->cs_gpio)) + return 0; + if (spi->chip_select > 1) { + /* error in the case of native CS requested with CS > 1 + * officially there is a CS2, but it is not documented + * which GPIO is connected with that... + */ + dev_err(&spi->dev, + "setup: only two native chip-selects are supported\n"); + return -EINVAL; + } + /* now translate native cs to GPIO */ - cs_change = tfr->cs_change || - list_is_last(&tfr->transfer_list, &mesg->transfers); + /* get the gpio chip for the base */ + chip = gpiochip_find("pinctrl-bcm2835", chip_match_name); + if (!chip) + return 0; - err = bcm2835_spi_finish_transfer(spi, tfr, cs_change); - if (err) - goto out; + /* and calculate the real CS */ + spi->cs_gpio = chip->base + 8 - spi->chip_select; - mesg->actual_length += (tfr->len - bs->len); - } + /* and set up the "mode" and level */ + dev_info(&spi->dev, "setting up native-CS%i as GPIO %i\n", + spi->chip_select, spi->cs_gpio); -out: - /* Clear FIFOs, and disable the HW block */ - bcm2835_wr(bs, BCM2835_SPI_CS, - BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); - mesg->status = err; - spi_finalize_current_message(master); + /* set up GPIO as output and pull to the correct level */ + err = gpio_direction_output(spi->cs_gpio, + (spi->mode & SPI_CS_HIGH) ? 0 : 1); + if (err) { + dev_err(&spi->dev, + "could not set CS%i gpio %i as output: %i", + spi->chip_select, spi->cs_gpio, err); + return err; + } + /* the implementation of pinctrl-bcm2835 currently does not + * set the GPIO value when using gpio_direction_output + * so we are setting it here explicitly + */ + gpio_set_value(spi->cs_gpio, (spi->mode & SPI_CS_HIGH) ? 0 : 1); return 0; } @@ -312,13 +426,14 @@ static int bcm2835_spi_probe(struct platform_device *pdev) master->mode_bits = BCM2835_SPI_MODE_BITS; master->bits_per_word_mask = SPI_BPW_MASK(8); master->num_chipselect = 3; - master->transfer_one_message = bcm2835_spi_transfer_one; + master->setup = bcm2835_spi_setup; + master->set_cs = bcm2835_spi_set_cs; + master->transfer_one = bcm2835_spi_transfer_one; + master->handle_err = bcm2835_spi_handle_err; master->dev.of_node = pdev->dev.of_node; bs = spi_master_get_devdata(master); - init_completion(&bs->done); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); bs->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(bs->regs)) { @@ -343,13 +458,13 @@ static int bcm2835_spi_probe(struct platform_device *pdev) clk_prepare_enable(bs->clk); err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt, 0, - dev_name(&pdev->dev), master); + dev_name(&pdev->dev), master); if (err) { dev_err(&pdev->dev, "could not request IRQ: %d\n", err); goto out_clk_disable; } - /* initialise the hardware */ + /* initialise the hardware with the default polarities */ bcm2835_wr(bs, BCM2835_SPI_CS, BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); diff --git a/drivers/spi/spi-bcm53xx.c b/drivers/spi/spi-bcm53xx.c index 3fb91c8..1520554 100644 --- a/drivers/spi/spi-bcm53xx.c +++ b/drivers/spi/spi-bcm53xx.c @@ -44,7 +44,7 @@ static int bcm53xxspi_wait(struct bcm53xxspi *b53spi, unsigned int timeout_ms) u32 tmp; /* SPE bit has to be 0 before we read MSPI STATUS */ - deadline = jiffies + BCM53XXSPI_SPE_TIMEOUT_MS * HZ / 1000; + deadline = jiffies + msecs_to_jiffies(BCM53XXSPI_SPE_TIMEOUT_MS); do { tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2); if (!(tmp & B53SPI_MSPI_SPCR2_SPE)) @@ -56,7 +56,7 @@ static int bcm53xxspi_wait(struct bcm53xxspi *b53spi, unsigned int timeout_ms) goto spi_timeout; /* Check status */ - deadline = jiffies + timeout_ms * HZ / 1000; + deadline = jiffies + msecs_to_jiffies(timeout_ms); do { tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_MSPI_STATUS); if (tmp & B53SPI_MSPI_MSPI_STATUS_SPIF) { diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index 3707993..a3d65b4 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -559,7 +559,7 @@ static void bfin_spi_pump_transfers(unsigned long data) struct spi_transfer *previous = NULL; struct bfin_spi_slave_data *chip = NULL; unsigned int bits_per_word; - u16 cr, cr_width, dma_width, dma_config; + u16 cr, cr_width = 0, dma_width, dma_config; u32 tranf_success = 1; u8 full_duplex = 0; @@ -648,7 +648,6 @@ static void bfin_spi_pump_transfers(unsigned long data) } else if (bits_per_word == 8) { drv_data->n_bytes = bits_per_word/8; drv_data->len = transfer->len; - cr_width = 0; drv_data->ops = &bfin_bfin_spi_transfer_ops_u8; } cr = bfin_read(&drv_data->regs->ctl) & ~(BIT_CTL_TIMOD | BIT_CTL_WORDSIZE); diff --git a/drivers/spi/spi-bitbang-txrx.h b/drivers/spi/spi-bitbang-txrx.h index c616e41..06b34e5 100644 --- a/drivers/spi/spi-bitbang-txrx.h +++ b/drivers/spi/spi-bitbang-txrx.h @@ -49,12 +49,17 @@ bitbang_txrx_be_cpha0(struct spi_device *spi, { /* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */ + bool oldbit = !(word & 1); /* clock starts at inactive polarity */ for (word <<= (32 - bits); likely(bits); bits--) { /* setup MSB (to slave) on trailing edge */ - if ((flags & SPI_MASTER_NO_TX) == 0) - setmosi(spi, word & (1 << 31)); + if ((flags & SPI_MASTER_NO_TX) == 0) { + if ((word & (1 << 31)) != oldbit) { + setmosi(spi, word & (1 << 31)); + oldbit = word & (1 << 31); + } + } spidelay(nsecs); /* T(setup) */ setsck(spi, !cpol); @@ -76,13 +81,18 @@ bitbang_txrx_be_cpha1(struct spi_device *spi, { /* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */ + bool oldbit = !(word & (1 << 31)); /* clock starts at inactive polarity */ for (word <<= (32 - bits); likely(bits); bits--) { /* setup MSB (to slave) on leading edge */ setsck(spi, !cpol); - if ((flags & SPI_MASTER_NO_TX) == 0) - setmosi(spi, word & (1 << 31)); + if ((flags & SPI_MASTER_NO_TX) == 0) { + if ((word & (1 << 31)) != oldbit) { + setmosi(spi, word & (1 << 31)); + oldbit = word & (1 << 31); + } + } spidelay(nsecs); /* T(setup) */ setsck(spi, cpol); diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index a0197fd..bb1052e 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -23,29 +23,31 @@ #include "spi-dw.h" #ifdef CONFIG_SPI_DW_MID_DMA -#include <linux/intel_mid_dma.h> #include <linux/pci.h> +#include <linux/platform_data/dma-dw.h> #define RX_BUSY 0 #define TX_BUSY 1 -struct mid_dma { - struct intel_mid_dma_slave dmas_tx; - struct intel_mid_dma_slave dmas_rx; -}; +static struct dw_dma_slave mid_dma_tx = { .dst_id = 1 }; +static struct dw_dma_slave mid_dma_rx = { .src_id = 0 }; static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) { - struct dw_spi *dws = param; + struct dw_dma_slave *s = param; + + if (s->dma_dev != chan->device->dev) + return false; - return dws->dma_dev == chan->device->dev; + chan->private = s; + return true; } static int mid_spi_dma_init(struct dw_spi *dws) { - struct mid_dma *dw_dma = dws->dma_priv; struct pci_dev *dma_dev; - struct intel_mid_dma_slave *rxs, *txs; + struct dw_dma_slave *tx = dws->dma_tx; + struct dw_dma_slave *rx = dws->dma_rx; dma_cap_mask_t mask; /* @@ -56,28 +58,22 @@ static int mid_spi_dma_init(struct dw_spi *dws) if (!dma_dev) return -ENODEV; - dws->dma_dev = &dma_dev->dev; - dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); /* 1. Init rx channel */ - dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws); + rx->dma_dev = &dma_dev->dev; + dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, rx); if (!dws->rxchan) goto err_exit; - rxs = &dw_dma->dmas_rx; - rxs->hs_mode = LNW_DMA_HW_HS; - rxs->cfg_mode = LNW_DMA_PER_TO_MEM; - dws->rxchan->private = rxs; + dws->master->dma_rx = dws->rxchan; /* 2. Init tx channel */ - dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws); + tx->dma_dev = &dma_dev->dev; + dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, tx); if (!dws->txchan) goto free_rxchan; - txs = &dw_dma->dmas_tx; - txs->hs_mode = LNW_DMA_HW_HS; - txs->cfg_mode = LNW_DMA_MEM_TO_PER; - dws->txchan->private = txs; + dws->master->dma_tx = dws->txchan; dws->dma_inited = 1; return 0; @@ -100,6 +96,42 @@ static void mid_spi_dma_exit(struct dw_spi *dws) dma_release_channel(dws->rxchan); } +static irqreturn_t dma_transfer(struct dw_spi *dws) +{ + u16 irq_status = dw_readl(dws, DW_SPI_ISR); + + if (!irq_status) + return IRQ_NONE; + + dw_readl(dws, DW_SPI_ICR); + spi_reset_chip(dws); + + dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__); + dws->master->cur_msg->status = -EIO; + spi_finalize_current_transfer(dws->master); + return IRQ_HANDLED; +} + +static bool mid_spi_can_dma(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct dw_spi *dws = spi_master_get_devdata(master); + + if (!dws->dma_inited) + return false; + + return xfer->len > dws->fifo_len; +} + +static enum dma_slave_buswidth convert_dma_width(u32 dma_width) { + if (dma_width == 1) + return DMA_SLAVE_BUSWIDTH_1_BYTE; + else if (dma_width == 2) + return DMA_SLAVE_BUSWIDTH_2_BYTES; + + return DMA_SLAVE_BUSWIDTH_UNDEFINED; +} + /* * dws->dma_chan_busy is set before the dma transfer starts, callback for tx * channel will clear a corresponding bit. @@ -108,37 +140,38 @@ static void dw_spi_dma_tx_done(void *arg) { struct dw_spi *dws = arg; - if (test_and_clear_bit(TX_BUSY, &dws->dma_chan_busy) & BIT(RX_BUSY)) + clear_bit(TX_BUSY, &dws->dma_chan_busy); + if (test_bit(RX_BUSY, &dws->dma_chan_busy)) return; - dw_spi_xfer_done(dws); + spi_finalize_current_transfer(dws->master); } -static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws) +static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws, + struct spi_transfer *xfer) { struct dma_slave_config txconf; struct dma_async_tx_descriptor *txdesc; - if (!dws->tx_dma) + if (!xfer->tx_buf) return NULL; txconf.direction = DMA_MEM_TO_DEV; txconf.dst_addr = dws->dma_addr; - txconf.dst_maxburst = LNW_DMA_MSIZE_16; + txconf.dst_maxburst = 16; txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - txconf.dst_addr_width = dws->dma_width; + txconf.dst_addr_width = convert_dma_width(dws->dma_width); txconf.device_fc = false; dmaengine_slave_config(dws->txchan, &txconf); - memset(&dws->tx_sgl, 0, sizeof(dws->tx_sgl)); - dws->tx_sgl.dma_address = dws->tx_dma; - dws->tx_sgl.length = dws->len; - txdesc = dmaengine_prep_slave_sg(dws->txchan, - &dws->tx_sgl, - 1, + xfer->tx_sg.sgl, + xfer->tx_sg.nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!txdesc) + return NULL; + txdesc->callback = dw_spi_dma_tx_done; txdesc->callback_param = dws; @@ -153,74 +186,74 @@ static void dw_spi_dma_rx_done(void *arg) { struct dw_spi *dws = arg; - if (test_and_clear_bit(RX_BUSY, &dws->dma_chan_busy) & BIT(TX_BUSY)) + clear_bit(RX_BUSY, &dws->dma_chan_busy); + if (test_bit(TX_BUSY, &dws->dma_chan_busy)) return; - dw_spi_xfer_done(dws); + spi_finalize_current_transfer(dws->master); } -static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws) +static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, + struct spi_transfer *xfer) { struct dma_slave_config rxconf; struct dma_async_tx_descriptor *rxdesc; - if (!dws->rx_dma) + if (!xfer->rx_buf) return NULL; rxconf.direction = DMA_DEV_TO_MEM; rxconf.src_addr = dws->dma_addr; - rxconf.src_maxburst = LNW_DMA_MSIZE_16; + rxconf.src_maxburst = 16; rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - rxconf.src_addr_width = dws->dma_width; + rxconf.src_addr_width = convert_dma_width(dws->dma_width); rxconf.device_fc = false; dmaengine_slave_config(dws->rxchan, &rxconf); - memset(&dws->rx_sgl, 0, sizeof(dws->rx_sgl)); - dws->rx_sgl.dma_address = dws->rx_dma; - dws->rx_sgl.length = dws->len; - rxdesc = dmaengine_prep_slave_sg(dws->rxchan, - &dws->rx_sgl, - 1, + xfer->rx_sg.sgl, + xfer->rx_sg.nents, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!rxdesc) + return NULL; + rxdesc->callback = dw_spi_dma_rx_done; rxdesc->callback_param = dws; return rxdesc; } -static void dw_spi_dma_setup(struct dw_spi *dws) +static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) { u16 dma_ctrl = 0; - spi_enable_chip(dws, 0); - - dw_writew(dws, DW_SPI_DMARDLR, 0xf); - dw_writew(dws, DW_SPI_DMATDLR, 0x10); + dw_writel(dws, DW_SPI_DMARDLR, 0xf); + dw_writel(dws, DW_SPI_DMATDLR, 0x10); - if (dws->tx_dma) + if (xfer->tx_buf) dma_ctrl |= SPI_DMA_TDMAE; - if (dws->rx_dma) + if (xfer->rx_buf) dma_ctrl |= SPI_DMA_RDMAE; - dw_writew(dws, DW_SPI_DMACR, dma_ctrl); + dw_writel(dws, DW_SPI_DMACR, dma_ctrl); + + /* Set the interrupt mask */ + spi_umask_intr(dws, SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI); + + dws->transfer_handler = dma_transfer; - spi_enable_chip(dws, 1); + return 0; } -static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) +static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) { struct dma_async_tx_descriptor *txdesc, *rxdesc; - /* 1. setup DMA related registers */ - if (cs_change) - dw_spi_dma_setup(dws); - - /* 2. Prepare the TX dma transfer */ - txdesc = dw_spi_dma_prepare_tx(dws); + /* Prepare the TX dma transfer */ + txdesc = dw_spi_dma_prepare_tx(dws, xfer); - /* 3. Prepare the RX dma transfer */ - rxdesc = dw_spi_dma_prepare_rx(dws); + /* Prepare the RX dma transfer */ + rxdesc = dw_spi_dma_prepare_rx(dws, xfer); /* rx must be started before tx due to spi instinct */ if (rxdesc) { @@ -238,10 +271,25 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) return 0; } +static void mid_spi_dma_stop(struct dw_spi *dws) +{ + if (test_bit(TX_BUSY, &dws->dma_chan_busy)) { + dmaengine_terminate_all(dws->txchan); + clear_bit(TX_BUSY, &dws->dma_chan_busy); + } + if (test_bit(RX_BUSY, &dws->dma_chan_busy)) { + dmaengine_terminate_all(dws->rxchan); + clear_bit(RX_BUSY, &dws->dma_chan_busy); + } +} + static struct dw_spi_dma_ops mid_dma_ops = { .dma_init = mid_spi_dma_init, .dma_exit = mid_spi_dma_exit, + .dma_setup = mid_spi_dma_setup, + .can_dma = mid_spi_can_dma, .dma_transfer = mid_spi_dma_transfer, + .dma_stop = mid_spi_dma_stop, }; #endif @@ -274,9 +322,8 @@ int dw_spi_mid_init(struct dw_spi *dws) iounmap(clk_reg); #ifdef CONFIG_SPI_DW_MID_DMA - dws->dma_priv = kzalloc(sizeof(struct mid_dma), GFP_KERNEL); - if (!dws->dma_priv) - return -ENOMEM; + dws->dma_tx = &mid_dma_tx; + dws->dma_rx = &mid_dma_rx; dws->dma_ops = &mid_dma_ops; #endif return 0; diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index 5ba3310..6d331e0 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -36,13 +36,13 @@ struct spi_pci_desc { static struct spi_pci_desc spi_pci_mid_desc_1 = { .setup = dw_spi_mid_init, - .num_cs = 32, + .num_cs = 5, .bus_num = 0, }; static struct spi_pci_desc spi_pci_mid_desc_2 = { .setup = dw_spi_mid_init, - .num_cs = 4, + .num_cs = 2, .bus_num = 1, }; diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 5a97a62..8d67d03 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -28,11 +28,6 @@ #include <linux/debugfs.h> #endif -#define START_STATE ((void *)0) -#define RUNNING_STATE ((void *)1) -#define DONE_STATE ((void *)2) -#define ERROR_STATE ((void *)-1) - /* Slave spi_dev related */ struct chip_data { u16 cr0; @@ -143,13 +138,26 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws) } #endif /* CONFIG_DEBUG_FS */ +static void dw_spi_set_cs(struct spi_device *spi, bool enable) +{ + struct dw_spi *dws = spi_master_get_devdata(spi->master); + struct chip_data *chip = spi_get_ctldata(spi); + + /* Chip select logic is inverted from spi_set_cs() */ + if (chip && chip->cs_control) + chip->cs_control(!enable); + + if (!enable) + dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select)); +} + /* Return the max entries we can fill into tx fifo */ static inline u32 tx_max(struct dw_spi *dws) { u32 tx_left, tx_room, rxtx_gap; tx_left = (dws->tx_end - dws->tx) / dws->n_bytes; - tx_room = dws->fifo_len - dw_readw(dws, DW_SPI_TXFLR); + tx_room = dws->fifo_len - dw_readl(dws, DW_SPI_TXFLR); /* * Another concern is about the tx/rx mismatch, we @@ -170,7 +178,7 @@ static inline u32 rx_max(struct dw_spi *dws) { u32 rx_left = (dws->rx_end - dws->rx) / dws->n_bytes; - return min_t(u32, rx_left, dw_readw(dws, DW_SPI_RXFLR)); + return min_t(u32, rx_left, dw_readl(dws, DW_SPI_RXFLR)); } static void dw_writer(struct dw_spi *dws) @@ -186,7 +194,7 @@ static void dw_writer(struct dw_spi *dws) else txw = *(u16 *)(dws->tx); } - dw_writew(dws, DW_SPI_DR, txw); + dw_writel(dws, DW_SPI_DR, txw); dws->tx += dws->n_bytes; } } @@ -197,7 +205,7 @@ static void dw_reader(struct dw_spi *dws) u16 rxw; while (max--) { - rxw = dw_readw(dws, DW_SPI_DR); + rxw = dw_readl(dws, DW_SPI_DR); /* Care rx only if the transfer's original "rx" is not null */ if (dws->rx_end - dws->len) { if (dws->n_bytes == 1) @@ -209,103 +217,22 @@ static void dw_reader(struct dw_spi *dws) } } -static void *next_transfer(struct dw_spi *dws) -{ - struct spi_message *msg = dws->cur_msg; - struct spi_transfer *trans = dws->cur_transfer; - - /* Move to next transfer */ - if (trans->transfer_list.next != &msg->transfers) { - dws->cur_transfer = - list_entry(trans->transfer_list.next, - struct spi_transfer, - transfer_list); - return RUNNING_STATE; - } - - return DONE_STATE; -} - -/* - * Note: first step is the protocol driver prepares - * a dma-capable memory, and this func just need translate - * the virt addr to physical - */ -static int map_dma_buffers(struct dw_spi *dws) -{ - if (!dws->cur_msg->is_dma_mapped - || !dws->dma_inited - || !dws->cur_chip->enable_dma - || !dws->dma_ops) - return 0; - - if (dws->cur_transfer->tx_dma) - dws->tx_dma = dws->cur_transfer->tx_dma; - - if (dws->cur_transfer->rx_dma) - dws->rx_dma = dws->cur_transfer->rx_dma; - - return 1; -} - -/* Caller already set message->status; dma and pio irqs are blocked */ -static void giveback(struct dw_spi *dws) -{ - struct spi_transfer *last_transfer; - struct spi_message *msg; - - msg = dws->cur_msg; - dws->cur_msg = NULL; - dws->cur_transfer = NULL; - dws->prev_chip = dws->cur_chip; - dws->cur_chip = NULL; - dws->dma_mapped = 0; - - last_transfer = list_last_entry(&msg->transfers, struct spi_transfer, - transfer_list); - - if (!last_transfer->cs_change) - spi_chip_sel(dws, msg->spi, 0); - - spi_finalize_current_message(dws->master); -} - static void int_error_stop(struct dw_spi *dws, const char *msg) { - /* Stop the hw */ - spi_enable_chip(dws, 0); + spi_reset_chip(dws); dev_err(&dws->master->dev, "%s\n", msg); - dws->cur_msg->state = ERROR_STATE; - tasklet_schedule(&dws->pump_transfers); + dws->master->cur_msg->status = -EIO; + spi_finalize_current_transfer(dws->master); } -void dw_spi_xfer_done(struct dw_spi *dws) -{ - /* Update total byte transferred return count actual bytes read */ - dws->cur_msg->actual_length += dws->len; - - /* Move to next transfer */ - dws->cur_msg->state = next_transfer(dws); - - /* Handle end of message */ - if (dws->cur_msg->state == DONE_STATE) { - dws->cur_msg->status = 0; - giveback(dws); - } else - tasklet_schedule(&dws->pump_transfers); -} -EXPORT_SYMBOL_GPL(dw_spi_xfer_done); - static irqreturn_t interrupt_transfer(struct dw_spi *dws) { - u16 irq_status = dw_readw(dws, DW_SPI_ISR); + u16 irq_status = dw_readl(dws, DW_SPI_ISR); /* Error handling */ if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) { - dw_readw(dws, DW_SPI_TXOICR); - dw_readw(dws, DW_SPI_RXOICR); - dw_readw(dws, DW_SPI_RXUICR); + dw_readl(dws, DW_SPI_ICR); int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun"); return IRQ_HANDLED; } @@ -313,7 +240,7 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws) dw_reader(dws); if (dws->rx_end == dws->rx) { spi_mask_intr(dws, SPI_INT_TXEI); - dw_spi_xfer_done(dws); + spi_finalize_current_transfer(dws->master); return IRQ_HANDLED; } if (irq_status & SPI_INT_TXEI) { @@ -328,13 +255,14 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws) static irqreturn_t dw_spi_irq(int irq, void *dev_id) { - struct dw_spi *dws = dev_id; - u16 irq_status = dw_readw(dws, DW_SPI_ISR) & 0x3f; + struct spi_master *master = dev_id; + struct dw_spi *dws = spi_master_get_devdata(master); + u16 irq_status = dw_readl(dws, DW_SPI_ISR) & 0x3f; if (!irq_status) return IRQ_NONE; - if (!dws->cur_msg) { + if (!master->cur_msg) { spi_mask_intr(dws, SPI_INT_TXEI); return IRQ_HANDLED; } @@ -343,7 +271,7 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id) } /* Must be called inside pump_transfers() */ -static void poll_transfer(struct dw_spi *dws) +static int poll_transfer(struct dw_spi *dws) { do { dw_writer(dws); @@ -351,64 +279,32 @@ static void poll_transfer(struct dw_spi *dws) cpu_relax(); } while (dws->rx_end > dws->rx); - dw_spi_xfer_done(dws); + return 0; } -static void pump_transfers(unsigned long data) +static int dw_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, struct spi_transfer *transfer) { - struct dw_spi *dws = (struct dw_spi *)data; - struct spi_message *message = NULL; - struct spi_transfer *transfer = NULL; - struct spi_transfer *previous = NULL; - struct spi_device *spi = NULL; - struct chip_data *chip = NULL; - u8 bits = 0; + struct dw_spi *dws = spi_master_get_devdata(master); + struct chip_data *chip = spi_get_ctldata(spi); u8 imask = 0; - u8 cs_change = 0; - u16 txint_level = 0; + u16 txlevel = 0; u16 clk_div = 0; u32 speed = 0; u32 cr0 = 0; + int ret; - /* Get current state information */ - message = dws->cur_msg; - transfer = dws->cur_transfer; - chip = dws->cur_chip; - spi = message->spi; - - if (message->state == ERROR_STATE) { - message->status = -EIO; - goto early_exit; - } - - /* Handle end of message */ - if (message->state == DONE_STATE) { - message->status = 0; - goto early_exit; - } - - /* Delay if requested at end of transfer */ - if (message->state == RUNNING_STATE) { - previous = list_entry(transfer->transfer_list.prev, - struct spi_transfer, - transfer_list); - if (previous->delay_usecs) - udelay(previous->delay_usecs); - } - + dws->dma_mapped = 0; dws->n_bytes = chip->n_bytes; dws->dma_width = chip->dma_width; - dws->cs_control = chip->cs_control; - dws->rx_dma = transfer->rx_dma; - dws->tx_dma = transfer->tx_dma; dws->tx = (void *)transfer->tx_buf; dws->tx_end = dws->tx + transfer->len; dws->rx = transfer->rx_buf; dws->rx_end = dws->rx + transfer->len; - dws->len = dws->cur_transfer->len; - if (chip != dws->prev_chip) - cs_change = 1; + dws->len = transfer->len; + + spi_enable_chip(dws, 0); cr0 = chip->cr0; @@ -416,32 +312,37 @@ static void pump_transfers(unsigned long data) if (transfer->speed_hz) { speed = chip->speed_hz; - if ((transfer->speed_hz != speed) || (!chip->clk_div)) { + if ((transfer->speed_hz != speed) || !chip->clk_div) { speed = transfer->speed_hz; /* clk_div doesn't support odd number */ - clk_div = dws->max_freq / speed; - clk_div = (clk_div + 1) & 0xfffe; + clk_div = (dws->max_freq / speed + 1) & 0xfffe; chip->speed_hz = speed; chip->clk_div = clk_div; + + spi_set_clk(dws, chip->clk_div); } } if (transfer->bits_per_word) { - bits = transfer->bits_per_word; - dws->n_bytes = dws->dma_width = bits >> 3; - cr0 = (bits - 1) + if (transfer->bits_per_word == 8) { + dws->n_bytes = 1; + dws->dma_width = 1; + } else if (transfer->bits_per_word == 16) { + dws->n_bytes = 2; + dws->dma_width = 2; + } + cr0 = (transfer->bits_per_word - 1) | (chip->type << SPI_FRF_OFFSET) | (spi->mode << SPI_MODE_OFFSET) | (chip->tmode << SPI_TMOD_OFFSET); } - message->state = RUNNING_STATE; /* * Adjust transfer mode if necessary. Requires platform dependent * chipselect mechanism. */ - if (dws->cs_control) { + if (chip->cs_control) { if (dws->rx && dws->tx) chip->tmode = SPI_TMOD_TR; else if (dws->rx) @@ -453,80 +354,60 @@ static void pump_transfers(unsigned long data) cr0 |= (chip->tmode << SPI_TMOD_OFFSET); } + dw_writel(dws, DW_SPI_CTRL0, cr0); + /* Check if current transfer is a DMA transaction */ - dws->dma_mapped = map_dma_buffers(dws); + if (master->can_dma && master->can_dma(master, spi, transfer)) + dws->dma_mapped = master->cur_msg_mapped; + + /* For poll mode just disable all interrupts */ + spi_mask_intr(dws, 0xff); /* * Interrupt mode * we only need set the TXEI IRQ, as TX/RX always happen syncronizely */ - if (!dws->dma_mapped && !chip->poll_mode) { - int templen = dws->len / dws->n_bytes; - - txint_level = dws->fifo_len / 2; - txint_level = (templen > txint_level) ? txint_level : templen; + if (dws->dma_mapped) { + ret = dws->dma_ops->dma_setup(dws, transfer); + if (ret < 0) { + spi_enable_chip(dws, 1); + return ret; + } + } else if (!chip->poll_mode) { + txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes); + dw_writel(dws, DW_SPI_TXFLTR, txlevel); + /* Set the interrupt mask */ imask |= SPI_INT_TXEI | SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI; + spi_umask_intr(dws, imask); + dws->transfer_handler = interrupt_transfer; } - /* - * Reprogram registers only if - * 1. chip select changes - * 2. clk_div is changed - * 3. control value changes - */ - if (dw_readw(dws, DW_SPI_CTRL0) != cr0 || cs_change || clk_div || imask) { - spi_enable_chip(dws, 0); - - if (dw_readw(dws, DW_SPI_CTRL0) != cr0) - dw_writew(dws, DW_SPI_CTRL0, cr0); - - spi_set_clk(dws, clk_div ? clk_div : chip->clk_div); - spi_chip_sel(dws, spi, 1); - - /* Set the interrupt mask, for poll mode just disable all int */ - spi_mask_intr(dws, 0xff); - if (imask) - spi_umask_intr(dws, imask); - if (txint_level) - dw_writew(dws, DW_SPI_TXFLTR, txint_level); + spi_enable_chip(dws, 1); - spi_enable_chip(dws, 1); - if (cs_change) - dws->prev_chip = chip; + if (dws->dma_mapped) { + ret = dws->dma_ops->dma_transfer(dws, transfer); + if (ret < 0) + return ret; } - if (dws->dma_mapped) - dws->dma_ops->dma_transfer(dws, cs_change); - if (chip->poll_mode) - poll_transfer(dws); - - return; + return poll_transfer(dws); -early_exit: - giveback(dws); + return 1; } -static int dw_spi_transfer_one_message(struct spi_master *master, +static void dw_spi_handle_err(struct spi_master *master, struct spi_message *msg) { struct dw_spi *dws = spi_master_get_devdata(master); - dws->cur_msg = msg; - /* Initial message state */ - dws->cur_msg->state = START_STATE; - dws->cur_transfer = list_entry(dws->cur_msg->transfers.next, - struct spi_transfer, - transfer_list); - dws->cur_chip = spi_get_ctldata(dws->cur_msg->spi); - - /* Launch transfers */ - tasklet_schedule(&dws->pump_transfers); + if (dws->dma_mapped) + dws->dma_ops->dma_stop(dws); - return 0; + spi_reset_chip(dws); } /* This may be called twice for each spi dev */ @@ -561,8 +442,6 @@ static int dw_spi_setup(struct spi_device *spi) chip->rx_threshold = 0; chip->tx_threshold = 0; - - chip->enable_dma = chip_info->enable_dma; } if (spi->bits_per_word == 8) { @@ -610,9 +489,7 @@ static void dw_spi_cleanup(struct spi_device *spi) /* Restart the controller, disable all interrupts, clean rx fifo */ static void spi_hw_init(struct device *dev, struct dw_spi *dws) { - spi_enable_chip(dws, 0); - spi_mask_intr(dws, 0xff); - spi_enable_chip(dws, 1); + spi_reset_chip(dws); /* * Try to detect the FIFO depth if not set by interface driver, @@ -621,14 +498,14 @@ static void spi_hw_init(struct device *dev, struct dw_spi *dws) if (!dws->fifo_len) { u32 fifo; - for (fifo = 2; fifo <= 256; fifo++) { - dw_writew(dws, DW_SPI_TXFLTR, fifo); - if (fifo != dw_readw(dws, DW_SPI_TXFLTR)) + for (fifo = 1; fifo < 256; fifo++) { + dw_writel(dws, DW_SPI_TXFLTR, fifo); + if (fifo != dw_readl(dws, DW_SPI_TXFLTR)) break; } - dw_writew(dws, DW_SPI_TXFLTR, 0); + dw_writel(dws, DW_SPI_TXFLTR, 0); - dws->fifo_len = (fifo == 2) ? 0 : fifo - 1; + dws->fifo_len = (fifo == 1) ? 0 : fifo; dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len); } } @@ -646,13 +523,12 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) dws->master = master; dws->type = SSI_MOTO_SPI; - dws->prev_chip = NULL; dws->dma_inited = 0; dws->dma_addr = (dma_addr_t)(dws->paddr + 0x60); snprintf(dws->name, sizeof(dws->name), "dw_spi%d", dws->bus_num); ret = devm_request_irq(dev, dws->irq, dw_spi_irq, IRQF_SHARED, - dws->name, dws); + dws->name, master); if (ret < 0) { dev_err(&master->dev, "can not get IRQ\n"); goto err_free_master; @@ -664,7 +540,9 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->num_chipselect = dws->num_cs; master->setup = dw_spi_setup; master->cleanup = dw_spi_cleanup; - master->transfer_one_message = dw_spi_transfer_one_message; + master->set_cs = dw_spi_set_cs; + master->transfer_one = dw_spi_transfer_one; + master->handle_err = dw_spi_handle_err; master->max_speed_hz = dws->max_freq; master->dev.of_node = dev->of_node; @@ -676,11 +554,11 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) if (ret) { dev_warn(dev, "DMA init failed\n"); dws->dma_inited = 0; + } else { + master->can_dma = dws->dma_ops->can_dma; } } - tasklet_init(&dws->pump_transfers, pump_transfers, (unsigned long)dws); - spi_master_set_devdata(master, dws); ret = devm_spi_register_master(dev, master); if (ret) { diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 3d32be6..6c91391 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -91,12 +91,15 @@ struct dw_spi; struct dw_spi_dma_ops { int (*dma_init)(struct dw_spi *dws); void (*dma_exit)(struct dw_spi *dws); - int (*dma_transfer)(struct dw_spi *dws, int cs_change); + int (*dma_setup)(struct dw_spi *dws, struct spi_transfer *xfer); + bool (*can_dma)(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer); + int (*dma_transfer)(struct dw_spi *dws, struct spi_transfer *xfer); + void (*dma_stop)(struct dw_spi *dws); }; struct dw_spi { struct spi_master *master; - struct spi_device *cur_dev; enum dw_ssi_type type; char name[16]; @@ -109,41 +112,26 @@ struct dw_spi { u16 bus_num; u16 num_cs; /* supported slave numbers */ - /* Message Transfer pump */ - struct tasklet_struct pump_transfers; - /* Current message transfer state info */ - struct spi_message *cur_msg; - struct spi_transfer *cur_transfer; - struct chip_data *cur_chip; - struct chip_data *prev_chip; size_t len; void *tx; void *tx_end; void *rx; void *rx_end; int dma_mapped; - dma_addr_t rx_dma; - dma_addr_t tx_dma; - size_t rx_map_len; - size_t tx_map_len; u8 n_bytes; /* current is a 1/2 bytes op */ - u8 max_bits_per_word; /* maxim is 16b */ u32 dma_width; irqreturn_t (*transfer_handler)(struct dw_spi *dws); - void (*cs_control)(u32 command); - /* Dma info */ + /* DMA info */ int dma_inited; struct dma_chan *txchan; - struct scatterlist tx_sgl; struct dma_chan *rxchan; - struct scatterlist rx_sgl; unsigned long dma_chan_busy; - struct device *dma_dev; dma_addr_t dma_addr; /* phy address of the Data register */ struct dw_spi_dma_ops *dma_ops; - void *dma_priv; /* platform relate info */ + void *dma_tx; + void *dma_rx; /* Bus interface info */ void *priv; @@ -162,16 +150,6 @@ static inline void dw_writel(struct dw_spi *dws, u32 offset, u32 val) __raw_writel(val, dws->regs + offset); } -static inline u16 dw_readw(struct dw_spi *dws, u32 offset) -{ - return __raw_readw(dws->regs + offset); -} - -static inline void dw_writew(struct dw_spi *dws, u32 offset, u16 val) -{ - __raw_writew(val, dws->regs + offset); -} - static inline void spi_enable_chip(struct dw_spi *dws, int enable) { dw_writel(dws, DW_SPI_SSIENR, (enable ? 1 : 0)); @@ -182,22 +160,6 @@ static inline void spi_set_clk(struct dw_spi *dws, u16 div) dw_writel(dws, DW_SPI_BAUDR, div); } -static inline void spi_chip_sel(struct dw_spi *dws, struct spi_device *spi, - int active) -{ - u16 cs = spi->chip_select; - int gpio_val = active ? (spi->mode & SPI_CS_HIGH) : - !(spi->mode & SPI_CS_HIGH); - - if (dws->cs_control) - dws->cs_control(active); - if (gpio_is_valid(spi->cs_gpio)) - gpio_set_value(spi->cs_gpio, gpio_val); - - if (active) - dw_writel(dws, DW_SPI_SER, 1 << cs); -} - /* Disable IRQ bits */ static inline void spi_mask_intr(struct dw_spi *dws, u32 mask) { @@ -217,15 +179,26 @@ static inline void spi_umask_intr(struct dw_spi *dws, u32 mask) } /* + * This does disable the SPI controller, interrupts, and re-enable the + * controller back. Transmit and receive FIFO buffers are cleared when the + * device is disabled. + */ +static inline void spi_reset_chip(struct dw_spi *dws) +{ + spi_enable_chip(dws, 0); + spi_mask_intr(dws, 0xff); + spi_enable_chip(dws, 1); +} + +/* * Each SPI slave device to work with dw_api controller should - * has such a structure claiming its working mode (PIO/DMA etc), + * has such a structure claiming its working mode (poll or PIO/DMA), * which can be save in the "controller_data" member of the * struct spi_device. */ struct dw_spi_chip { u8 poll_mode; /* 1 for controller polling mode */ u8 type; /* SPI/SSP/MicroWire */ - u8 enable_dma; void (*cs_control)(u32 command); }; @@ -233,7 +206,6 @@ extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws); extern void dw_spi_remove_host(struct dw_spi *dws); extern int dw_spi_suspend_host(struct dw_spi *dws); extern int dw_spi_resume_host(struct dw_spi *dws); -extern void dw_spi_xfer_done(struct dw_spi *dws); /* platform related setup */ extern int dw_spi_mid_init(struct dw_spi *dws); /* Intel MID platforms */ diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index d1a3924..5fe54cd 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -20,6 +20,7 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> +#include <linux/math64.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> @@ -29,6 +30,7 @@ #include <linux/sched.h> #include <linux/spi/spi.h> #include <linux/spi/spi_bitbang.h> +#include <linux/time.h> #define DRIVER_NAME "fsl-dspi" @@ -51,7 +53,7 @@ #define SPI_CTAR_CPOL(x) ((x) << 26) #define SPI_CTAR_CPHA(x) ((x) << 25) #define SPI_CTAR_LSBFE(x) ((x) << 24) -#define SPI_CTAR_PCSSCR(x) (((x) & 0x00000003) << 22) +#define SPI_CTAR_PCSSCK(x) (((x) & 0x00000003) << 22) #define SPI_CTAR_PASC(x) (((x) & 0x00000003) << 20) #define SPI_CTAR_PDT(x) (((x) & 0x00000003) << 18) #define SPI_CTAR_PBR(x) (((x) & 0x00000003) << 16) @@ -59,6 +61,7 @@ #define SPI_CTAR_ASC(x) (((x) & 0x0000000f) << 8) #define SPI_CTAR_DT(x) (((x) & 0x0000000f) << 4) #define SPI_CTAR_BR(x) ((x) & 0x0000000f) +#define SPI_CTAR_SCALE_BITS 0xf #define SPI_CTAR0_SLAVE 0x0c @@ -148,23 +151,66 @@ static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 }; - int temp, i = 0, j = 0; + int scale_needed, scale, minscale = INT_MAX; + int i, j; + + scale_needed = clkrate / speed_hz; + if (clkrate % speed_hz) + scale_needed++; + + for (i = 0; i < ARRAY_SIZE(brs); i++) + for (j = 0; j < ARRAY_SIZE(pbr_tbl); j++) { + scale = brs[i] * pbr_tbl[j]; + if (scale >= scale_needed) { + if (scale < minscale) { + minscale = scale; + *br = i; + *pbr = j; + } + break; + } + } - temp = clkrate / 2 / speed_hz; + if (minscale == INT_MAX) { + pr_warn("Can not find valid baud rate,speed_hz is %d,clkrate is %ld, we use the max prescaler value.\n", + speed_hz, clkrate); + *pbr = ARRAY_SIZE(pbr_tbl) - 1; + *br = ARRAY_SIZE(brs) - 1; + } +} - for (i = 0; i < ARRAY_SIZE(pbr_tbl); i++) - for (j = 0; j < ARRAY_SIZE(brs); j++) { - if (pbr_tbl[i] * brs[j] >= temp) { - *pbr = i; - *br = j; - return; +static void ns_delay_scale(char *psc, char *sc, int delay_ns, + unsigned long clkrate) +{ + int pscale_tbl[4] = {1, 3, 5, 7}; + int scale_needed, scale, minscale = INT_MAX; + int i, j; + u32 remainder; + + scale_needed = div_u64_rem((u64)delay_ns * clkrate, NSEC_PER_SEC, + &remainder); + if (remainder) + scale_needed++; + + for (i = 0; i < ARRAY_SIZE(pscale_tbl); i++) + for (j = 0; j <= SPI_CTAR_SCALE_BITS; j++) { + scale = pscale_tbl[i] * (2 << j); + if (scale >= scale_needed) { + if (scale < minscale) { + minscale = scale; + *psc = i; + *sc = j; + } + break; } } - pr_warn("Can not find valid baud rate,speed_hz is %d,clkrate is %ld\ - ,we use the max prescaler value.\n", speed_hz, clkrate); - *pbr = ARRAY_SIZE(pbr_tbl) - 1; - *br = ARRAY_SIZE(brs) - 1; + if (minscale == INT_MAX) { + pr_warn("Cannot find correct scale values for %dns delay at clkrate %ld, using max prescaler value", + delay_ns, clkrate); + *psc = ARRAY_SIZE(pscale_tbl) - 1; + *sc = SPI_CTAR_SCALE_BITS; + } } static int dspi_transfer_write(struct fsl_dspi *dspi) @@ -345,7 +391,10 @@ static int dspi_setup(struct spi_device *spi) { struct chip_data *chip; struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); - unsigned char br = 0, pbr = 0, fmsz = 0; + u32 cs_sck_delay = 0, sck_cs_delay = 0; + unsigned char br = 0, pbr = 0, pcssck = 0, cssck = 0; + unsigned char pasc = 0, asc = 0, fmsz = 0; + unsigned long clkrate; if ((spi->bits_per_word >= 4) && (spi->bits_per_word <= 16)) { fmsz = spi->bits_per_word - 1; @@ -362,18 +411,34 @@ static int dspi_setup(struct spi_device *spi) return -ENOMEM; } + of_property_read_u32(spi->dev.of_node, "fsl,spi-cs-sck-delay", + &cs_sck_delay); + + of_property_read_u32(spi->dev.of_node, "fsl,spi-sck-cs-delay", + &sck_cs_delay); + chip->mcr_val = SPI_MCR_MASTER | SPI_MCR_PCSIS | SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF; chip->void_write_data = 0; - hz_to_spi_baud(&pbr, &br, - spi->max_speed_hz, clk_get_rate(dspi->clk)); + clkrate = clk_get_rate(dspi->clk); + hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate); + + /* Set PCS to SCK delay scale values */ + ns_delay_scale(&pcssck, &cssck, cs_sck_delay, clkrate); + + /* Set After SCK delay scale values */ + ns_delay_scale(&pasc, &asc, sck_cs_delay, clkrate); chip->ctar_val = SPI_CTAR_FMSZ(fmsz) | SPI_CTAR_CPOL(spi->mode & SPI_CPOL ? 1 : 0) | SPI_CTAR_CPHA(spi->mode & SPI_CPHA ? 1 : 0) | SPI_CTAR_LSBFE(spi->mode & SPI_LSB_FIRST ? 1 : 0) + | SPI_CTAR_PCSSCK(pcssck) + | SPI_CTAR_CSSCK(cssck) + | SPI_CTAR_PASC(pasc) + | SPI_CTAR_ASC(asc) | SPI_CTAR_PBR(pbr) | SPI_CTAR_BR(br); diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index c01567d..788e2b1 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -12,6 +12,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/dmaengine.h> +#include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> @@ -122,36 +123,31 @@ static inline void spfi_start(struct img_spfi *spfi) spfi_writel(spfi, val, SPFI_CONTROL); } -static inline void spfi_stop(struct img_spfi *spfi) -{ - u32 val; - - val = spfi_readl(spfi, SPFI_CONTROL); - val &= ~SPFI_CONTROL_SPFI_EN; - spfi_writel(spfi, val, SPFI_CONTROL); -} - static inline void spfi_reset(struct img_spfi *spfi) { spfi_writel(spfi, SPFI_CONTROL_SOFT_RESET, SPFI_CONTROL); - udelay(1); spfi_writel(spfi, 0, SPFI_CONTROL); } -static void spfi_flush_tx_fifo(struct img_spfi *spfi) +static int spfi_wait_all_done(struct img_spfi *spfi) { - unsigned long timeout = jiffies + msecs_to_jiffies(10); + unsigned long timeout = jiffies + msecs_to_jiffies(50); - spfi_writel(spfi, SPFI_INTERRUPT_SDE, SPFI_INTERRUPT_CLEAR); while (time_before(jiffies, timeout)) { - if (spfi_readl(spfi, SPFI_INTERRUPT_STATUS) & - SPFI_INTERRUPT_SDE) - return; + u32 status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS); + + if (status & SPFI_INTERRUPT_ALLDONETRIG) { + spfi_writel(spfi, SPFI_INTERRUPT_ALLDONETRIG, + SPFI_INTERRUPT_CLEAR); + return 0; + } cpu_relax(); } - dev_err(spfi->dev, "Timed out waiting for FIFO to drain\n"); + dev_err(spfi->dev, "Timed out waiting for transaction to complete\n"); spfi_reset(spfi); + + return -ETIMEDOUT; } static unsigned int spfi_pio_write32(struct img_spfi *spfi, const u32 *buf, @@ -237,6 +233,7 @@ static int img_spfi_start_pio(struct spi_master *master, const void *tx_buf = xfer->tx_buf; void *rx_buf = xfer->rx_buf; unsigned long timeout; + int ret; if (tx_buf) tx_bytes = xfer->len; @@ -269,16 +266,15 @@ static int img_spfi_start_pio(struct spi_master *master, cpu_relax(); } + ret = spfi_wait_all_done(spfi); + if (ret < 0) + return ret; + if (rx_bytes > 0 || tx_bytes > 0) { dev_err(spfi->dev, "PIO transfer timed out\n"); - spfi_reset(spfi); return -ETIMEDOUT; } - if (tx_buf) - spfi_flush_tx_fifo(spfi); - spfi_stop(spfi); - return 0; } @@ -287,14 +283,12 @@ static void img_spfi_dma_rx_cb(void *data) struct img_spfi *spfi = data; unsigned long flags; - spin_lock_irqsave(&spfi->lock, flags); + spfi_wait_all_done(spfi); + spin_lock_irqsave(&spfi->lock, flags); spfi->rx_dma_busy = false; - if (!spfi->tx_dma_busy) { - spfi_stop(spfi); + if (!spfi->tx_dma_busy) spi_finalize_current_transfer(spfi->master); - } - spin_unlock_irqrestore(&spfi->lock, flags); } @@ -303,16 +297,12 @@ static void img_spfi_dma_tx_cb(void *data) struct img_spfi *spfi = data; unsigned long flags; - spfi_flush_tx_fifo(spfi); + spfi_wait_all_done(spfi); spin_lock_irqsave(&spfi->lock, flags); - spfi->tx_dma_busy = false; - if (!spfi->rx_dma_busy) { - spfi_stop(spfi); + if (!spfi->rx_dma_busy) spi_finalize_current_transfer(spfi->master); - } - spin_unlock_irqrestore(&spfi->lock, flags); } @@ -397,6 +387,75 @@ stop_dma: return -EIO; } +static void img_spfi_handle_err(struct spi_master *master, + struct spi_message *msg) +{ + struct img_spfi *spfi = spi_master_get_devdata(master); + unsigned long flags; + + /* + * Stop all DMA and reset the controller if the previous transaction + * timed-out and never completed it's DMA. + */ + spin_lock_irqsave(&spfi->lock, flags); + if (spfi->tx_dma_busy || spfi->rx_dma_busy) { + spfi->tx_dma_busy = false; + spfi->rx_dma_busy = false; + + dmaengine_terminate_all(spfi->tx_ch); + dmaengine_terminate_all(spfi->rx_ch); + } + spin_unlock_irqrestore(&spfi->lock, flags); +} + +static int img_spfi_prepare(struct spi_master *master, struct spi_message *msg) +{ + struct img_spfi *spfi = spi_master_get_devdata(master); + u32 val; + + val = spfi_readl(spfi, SPFI_PORT_STATE); + if (msg->spi->mode & SPI_CPHA) + val |= SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select); + else + val &= ~SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select); + if (msg->spi->mode & SPI_CPOL) + val |= SPFI_PORT_STATE_CK_POL(msg->spi->chip_select); + else + val &= ~SPFI_PORT_STATE_CK_POL(msg->spi->chip_select); + spfi_writel(spfi, val, SPFI_PORT_STATE); + + return 0; +} + +static int img_spfi_unprepare(struct spi_master *master, + struct spi_message *msg) +{ + struct img_spfi *spfi = spi_master_get_devdata(master); + + spfi_reset(spfi); + + return 0; +} + +static int img_spfi_setup(struct spi_device *spi) +{ + int ret; + + ret = gpio_request_one(spi->cs_gpio, (spi->mode & SPI_CS_HIGH) ? + GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, + dev_name(&spi->dev)); + if (ret) + dev_err(&spi->dev, "can't request chipselect gpio %d\n", + spi->cs_gpio); + + return ret; +} + +static void img_spfi_cleanup(struct spi_device *spi) +{ + gpio_free(spi->cs_gpio); +} + static void img_spfi_config(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { @@ -405,10 +464,10 @@ static void img_spfi_config(struct spi_master *master, struct spi_device *spi, /* * output = spfi_clk * (BITCLK / 512), where BITCLK must be a - * power of 2 up to 256 (where 255 == 256 since BITCLK is 8 bits) + * power of 2 up to 128 */ - div = DIV_ROUND_UP(master->max_speed_hz, xfer->speed_hz); - div = clamp(512 / (1 << get_count_order(div)), 1, 255); + div = DIV_ROUND_UP(clk_get_rate(spfi->spfi_clk), xfer->speed_hz); + div = clamp(512 / (1 << get_count_order(div)), 1, 128); val = spfi_readl(spfi, SPFI_DEVICE_PARAMETER(spi->chip_select)); val &= ~(SPFI_DEVICE_PARAMETER_BITCLK_MASK << @@ -416,6 +475,9 @@ static void img_spfi_config(struct spi_master *master, struct spi_device *spi, val |= div << SPFI_DEVICE_PARAMETER_BITCLK_SHIFT; spfi_writel(spfi, val, SPFI_DEVICE_PARAMETER(spi->chip_select)); + spfi_writel(spfi, xfer->len << SPFI_TRANSACTION_TSIZE_SHIFT, + SPFI_TRANSACTION); + val = spfi_readl(spfi, SPFI_CONTROL); val &= ~(SPFI_CONTROL_SEND_DMA | SPFI_CONTROL_GET_DMA); if (xfer->tx_buf) @@ -429,25 +491,7 @@ static void img_spfi_config(struct spi_master *master, struct spi_device *spi, else if (xfer->tx_nbits == SPI_NBITS_QUAD && xfer->rx_nbits == SPI_NBITS_QUAD) val |= SPFI_CONTROL_TMODE_QUAD << SPFI_CONTROL_TMODE_SHIFT; - val &= ~SPFI_CONTROL_CONTINUE; - if (!xfer->cs_change && !list_is_last(&xfer->transfer_list, - &master->cur_msg->transfers)) - val |= SPFI_CONTROL_CONTINUE; spfi_writel(spfi, val, SPFI_CONTROL); - - val = spfi_readl(spfi, SPFI_PORT_STATE); - if (spi->mode & SPI_CPHA) - val |= SPFI_PORT_STATE_CK_PHASE(spi->chip_select); - else - val &= ~SPFI_PORT_STATE_CK_PHASE(spi->chip_select); - if (spi->mode & SPI_CPOL) - val |= SPFI_PORT_STATE_CK_POL(spi->chip_select); - else - val &= ~SPFI_PORT_STATE_CK_POL(spi->chip_select); - spfi_writel(spfi, val, SPFI_PORT_STATE); - - spfi_writel(spfi, xfer->len << SPFI_TRANSACTION_TSIZE_SHIFT, - SPFI_TRANSACTION); } static int img_spfi_transfer_one(struct spi_master *master, @@ -455,25 +499,13 @@ static int img_spfi_transfer_one(struct spi_master *master, struct spi_transfer *xfer) { struct img_spfi *spfi = spi_master_get_devdata(spi->master); - bool dma_reset = false; - unsigned long flags; int ret; - /* - * Stop all DMA and reset the controller if the previous transaction - * timed-out and never completed it's DMA. - */ - spin_lock_irqsave(&spfi->lock, flags); - if (spfi->tx_dma_busy || spfi->rx_dma_busy) { - dev_err(spfi->dev, "SPI DMA still busy\n"); - dma_reset = true; - } - spin_unlock_irqrestore(&spfi->lock, flags); - - if (dma_reset) { - dmaengine_terminate_all(spfi->tx_ch); - dmaengine_terminate_all(spfi->rx_ch); - spfi_reset(spfi); + if (xfer->len > SPFI_TRANSACTION_TSIZE_MASK) { + dev_err(spfi->dev, + "Transfer length (%d) is greater than the max supported (%d)", + xfer->len, SPFI_TRANSACTION_TSIZE_MASK); + return -EINVAL; } img_spfi_config(master, spi, xfer); @@ -485,17 +517,6 @@ static int img_spfi_transfer_one(struct spi_master *master, return ret; } -static void img_spfi_set_cs(struct spi_device *spi, bool enable) -{ - struct img_spfi *spfi = spi_master_get_devdata(spi->master); - u32 val; - - val = spfi_readl(spfi, SPFI_PORT_STATE); - val &= ~(SPFI_PORT_STATE_DEV_SEL_MASK << SPFI_PORT_STATE_DEV_SEL_SHIFT); - val |= spi->chip_select << SPFI_PORT_STATE_DEV_SEL_SHIFT; - spfi_writel(spfi, val, SPFI_PORT_STATE); -} - static bool img_spfi_can_dma(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { @@ -584,14 +605,17 @@ static int img_spfi_probe(struct platform_device *pdev) master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_TX_DUAL | SPI_RX_DUAL; if (of_property_read_bool(spfi->dev->of_node, "img,supports-quad-mode")) master->mode_bits |= SPI_TX_QUAD | SPI_RX_QUAD; - master->num_chipselect = 5; master->dev.of_node = pdev->dev.of_node; master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(8); - master->max_speed_hz = clk_get_rate(spfi->spfi_clk); - master->min_speed_hz = master->max_speed_hz / 512; + master->max_speed_hz = clk_get_rate(spfi->spfi_clk) / 4; + master->min_speed_hz = clk_get_rate(spfi->spfi_clk) / 512; - master->set_cs = img_spfi_set_cs; + master->setup = img_spfi_setup; + master->cleanup = img_spfi_cleanup; master->transfer_one = img_spfi_transfer_one; + master->prepare_message = img_spfi_prepare; + master->unprepare_message = img_spfi_unprepare; + master->handle_err = img_spfi_handle_err; spfi->tx_ch = dma_request_slave_channel(spfi->dev, "tx"); spfi->rx_ch = dma_request_slave_channel(spfi->dev, "rx"); diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 6fea4af..f08e812 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -370,8 +370,6 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, if (spi_imx->dma_is_inited) { dma = readl(spi_imx->base + MX51_ECSPI_DMA); - spi_imx->tx_wml = spi_imx_get_fifosize(spi_imx) / 2; - spi_imx->rx_wml = spi_imx_get_fifosize(spi_imx) / 2; spi_imx->rxt_wml = spi_imx_get_fifosize(spi_imx) / 2; rx_wml_cfg = spi_imx->rx_wml << MX51_ECSPI_DMA_RX_WML_OFFSET; tx_wml_cfg = spi_imx->tx_wml << MX51_ECSPI_DMA_TX_WML_OFFSET; @@ -868,6 +866,8 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx, master->max_dma_len = MAX_SDMA_BD_BYTES; spi_imx->bitbang.master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX; + spi_imx->tx_wml = spi_imx_get_fifosize(spi_imx) / 2; + spi_imx->rx_wml = spi_imx_get_fifosize(spi_imx) / 2; spi_imx->dma_is_inited = 1; return 0; @@ -903,7 +903,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, if (tx) { desc_tx = dmaengine_prep_slave_sg(master->dma_tx, - tx->sgl, tx->nents, DMA_TO_DEVICE, + tx->sgl, tx->nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_tx) goto no_dma; @@ -915,7 +915,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, if (rx) { desc_rx = dmaengine_prep_slave_sg(master->dma_rx, - rx->sgl, rx->nents, DMA_FROM_DEVICE, + rx->sgl, rx->nents, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_rx) goto no_dma; diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index ecae0d4..965d2bd 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -588,7 +588,7 @@ static int mpc512x_psc_spi_of_remove(struct platform_device *op) return mpc512x_psc_spi_do_remove(&op->dev); } -static struct of_device_id mpc512x_psc_spi_of_match[] = { +static const struct of_device_id mpc512x_psc_spi_of_match[] = { { .compatible = "fsl,mpc5121-psc-spi", }, {}, }; diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c index b283d53..e99d6a9 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-octeon.c @@ -238,7 +238,7 @@ static int octeon_spi_remove(struct platform_device *pdev) return 0; } -static struct of_device_id octeon_spi_match[] = { +static const struct of_device_id octeon_spi_match[] = { { .compatible = "cavium,octeon-3010-spi", }, {}, }; diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index d890d30..35b332d 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -24,6 +24,7 @@ #include <linux/device.h> #include <linux/delay.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/err.h> #include <linux/clk.h> #include <linux/io.h> @@ -294,16 +295,6 @@ static int omap1_spi100k_setup(struct spi_device *spi) return ret; } -static int omap1_spi100k_prepare_hardware(struct spi_master *master) -{ - struct omap1_spi100k *spi100k = spi_master_get_devdata(master); - - clk_prepare_enable(spi100k->ick); - clk_prepare_enable(spi100k->fck); - - return 0; -} - static int omap1_spi100k_transfer_one_message(struct spi_master *master, struct spi_message *m) { @@ -372,16 +363,6 @@ static int omap1_spi100k_transfer_one_message(struct spi_master *master, return status; } -static int omap1_spi100k_unprepare_hardware(struct spi_master *master) -{ - struct omap1_spi100k *spi100k = spi_master_get_devdata(master); - - clk_disable_unprepare(spi100k->ick); - clk_disable_unprepare(spi100k->fck); - - return 0; -} - static int omap1_spi100k_probe(struct platform_device *pdev) { struct spi_master *master; @@ -402,14 +383,12 @@ static int omap1_spi100k_probe(struct platform_device *pdev) master->setup = omap1_spi100k_setup; master->transfer_one_message = omap1_spi100k_transfer_one_message; - master->prepare_transfer_hardware = omap1_spi100k_prepare_hardware; - master->unprepare_transfer_hardware = omap1_spi100k_unprepare_hardware; - master->cleanup = NULL; master->num_chipselect = 2; master->mode_bits = MODEBITS; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->min_speed_hz = OMAP1_SPI100K_MAX_FREQ/(1<<16); master->max_speed_hz = OMAP1_SPI100K_MAX_FREQ; + master->auto_runtime_pm = true; spi100k = spi_master_get_devdata(master); @@ -434,22 +413,96 @@ static int omap1_spi100k_probe(struct platform_device *pdev) goto err; } + status = clk_prepare_enable(spi100k->ick); + if (status != 0) { + dev_err(&pdev->dev, "failed to enable ick: %d\n", status); + goto err; + } + + status = clk_prepare_enable(spi100k->fck); + if (status != 0) { + dev_err(&pdev->dev, "failed to enable fck: %d\n", status); + goto err_ick; + } + + pm_runtime_enable(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + status = devm_spi_register_master(&pdev->dev, master); if (status < 0) - goto err; + goto err_fck; return status; +err_fck: + clk_disable_unprepare(spi100k->fck); +err_ick: + clk_disable_unprepare(spi100k->ick); err: spi_master_put(master); return status; } +static int omap1_spi100k_remove(struct platform_device *pdev) +{ + struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); + + pm_runtime_disable(&pdev->dev); + + clk_disable_unprepare(spi100k->fck); + clk_disable_unprepare(spi100k->ick); + + return 0; +} + +#ifdef CONFIG_PM +static int omap1_spi100k_runtime_suspend(struct device *dev) +{ + struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); + + clk_disable_unprepare(spi100k->ick); + clk_disable_unprepare(spi100k->fck); + + return 0; +} + +static int omap1_spi100k_runtime_resume(struct device *dev) +{ + struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); + int ret; + + ret = clk_prepare_enable(spi100k->ick); + if (ret != 0) { + dev_err(dev, "Failed to enable ick: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(spi100k->fck); + if (ret != 0) { + dev_err(dev, "Failed to enable fck: %d\n", ret); + clk_disable_unprepare(spi100k->ick); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops omap1_spi100k_pm = { + SET_RUNTIME_PM_OPS(omap1_spi100k_runtime_suspend, + omap1_spi100k_runtime_resume, NULL) +}; + static struct platform_driver omap1_spi100k_driver = { .driver = { .name = "omap1_spi100k", + .pm = &omap1_spi100k_pm, }, .probe = omap1_spi100k_probe, + .remove = omap1_spi100k_remove, }; module_platform_driver(omap1_spi100k_driver); diff --git a/drivers/spi/spi-omap-uwire.c b/drivers/spi/spi-omap-uwire.c index 3c08444..55576db 100644 --- a/drivers/spi/spi-omap-uwire.c +++ b/drivers/spi/spi-omap-uwire.c @@ -44,7 +44,6 @@ #include <linux/module.h> #include <linux/io.h> -#include <asm/irq.h> #include <mach/hardware.h> #include <asm/mach-types.h> diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 89ca162..94af806 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -285,7 +285,12 @@ */ #define DEFAULT_SSP_REG_IMSC 0x0UL #define DISABLE_ALL_INTERRUPTS DEFAULT_SSP_REG_IMSC -#define ENABLE_ALL_INTERRUPTS (~DEFAULT_SSP_REG_IMSC) +#define ENABLE_ALL_INTERRUPTS ( \ + SSP_IMSC_MASK_RORIM | \ + SSP_IMSC_MASK_RTIM | \ + SSP_IMSC_MASK_RXIM | \ + SSP_IMSC_MASK_TXIM \ +) #define CLEAR_ALL_INTERRUPTS 0x3 @@ -534,12 +539,12 @@ static void giveback(struct pl022 *pl022) pl022->cur_msg = NULL; pl022->cur_transfer = NULL; pl022->cur_chip = NULL; - spi_finalize_current_message(pl022->master); /* disable the SPI/SSP operation */ writew((readw(SSP_CR1(pl022->virtbase)) & (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase)); + spi_finalize_current_message(pl022->master); } /** @@ -1251,7 +1256,6 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id) struct pl022 *pl022 = dev_id; struct spi_message *msg = pl022->cur_msg; u16 irq_status = 0; - u16 flag = 0; if (unlikely(!msg)) { dev_err(&pl022->adev->dev, @@ -1280,9 +1284,6 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id) if (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RFF) dev_err(&pl022->adev->dev, "RXFIFO is full\n"); - if (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_TNF) - dev_err(&pl022->adev->dev, - "TXFIFO is full\n"); /* * Disable and clear interrupts, disable SSP, @@ -1303,8 +1304,7 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id) readwriter(pl022); - if ((pl022->tx == pl022->tx_end) && (flag == 0)) { - flag = 1; + if (pl022->tx == pl022->tx_end) { /* Disable Transmit interrupt, enable receive interrupt */ writew((readw(SSP_IMSC(pl022->virtbase)) & ~SSP_IMSC_MASK_TXIM) | SSP_IMSC_MASK_RXIM, diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 6f72ad0..e3223ac 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -20,6 +20,7 @@ #include <linux/errno.h> #include <linux/err.h> #include <linux/interrupt.h> +#include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/spi/pxa2xx_spi.h> #include <linux/spi/spi.h> @@ -30,10 +31,6 @@ #include <linux/pm_runtime.h> #include <linux/acpi.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/delay.h> - #include "spi-pxa2xx.h" MODULE_AUTHOR("Stephen Street"); @@ -67,54 +64,6 @@ MODULE_ALIAS("platform:pxa2xx-spi"); #define LPSS_TX_LOTHRESH_DFLT 160 #define LPSS_TX_HITHRESH_DFLT 224 -struct quark_spi_rate { - u32 bitrate; - u32 dds_clk_rate; - u32 clk_div; -}; - -/* - * 'rate', 'dds', 'clk_div' lookup table, which is defined in - * the Quark SPI datasheet. - */ -static const struct quark_spi_rate quark_spi_rate_table[] = { -/* bitrate, dds_clk_rate, clk_div */ - {50000000, 0x800000, 0}, - {40000000, 0x666666, 0}, - {25000000, 0x400000, 0}, - {20000000, 0x666666, 1}, - {16667000, 0x800000, 2}, - {13333000, 0x666666, 2}, - {12500000, 0x200000, 0}, - {10000000, 0x800000, 4}, - {8000000, 0x666666, 4}, - {6250000, 0x400000, 3}, - {5000000, 0x400000, 4}, - {4000000, 0x666666, 9}, - {3125000, 0x80000, 0}, - {2500000, 0x400000, 9}, - {2000000, 0x666666, 19}, - {1563000, 0x40000, 0}, - {1250000, 0x200000, 9}, - {1000000, 0x400000, 24}, - {800000, 0x666666, 49}, - {781250, 0x20000, 0}, - {625000, 0x200000, 19}, - {500000, 0x400000, 49}, - {400000, 0x666666, 99}, - {390625, 0x10000, 0}, - {250000, 0x400000, 99}, - {200000, 0x666666, 199}, - {195313, 0x8000, 0}, - {125000, 0x100000, 49}, - {100000, 0x200000, 124}, - {50000, 0x100000, 124}, - {25000, 0x80000, 124}, - {10016, 0x20000, 77}, - {5040, 0x20000, 154}, - {1002, 0x8000, 194}, -}; - /* Offset from drv_data->lpss_base */ #define GENERAL_REG 0x08 #define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24) @@ -701,25 +650,124 @@ static irqreturn_t ssp_int(int irq, void *dev_id) } /* - * The Quark SPI data sheet gives a table, and for the given 'rate', - * the 'dds' and 'clk_div' can be found in the table. + * The Quark SPI has an additional 24 bit register (DDS_CLK_RATE) to multiply + * input frequency by fractions of 2^24. It also has a divider by 5. + * + * There are formulas to get baud rate value for given input frequency and + * divider parameters, such as DDS_CLK_RATE and SCR: + * + * Fsys = 200MHz + * + * Fssp = Fsys * DDS_CLK_RATE / 2^24 (1) + * Baud rate = Fsclk = Fssp / (2 * (SCR + 1)) (2) + * + * DDS_CLK_RATE either 2^n or 2^n / 5. + * SCR is in range 0 .. 255 + * + * Divisor = 5^i * 2^j * 2 * k + * i = [0, 1] i = 1 iff j = 0 or j > 3 + * j = [0, 23] j = 0 iff i = 1 + * k = [1, 256] + * Special case: j = 0, i = 1: Divisor = 2 / 5 + * + * Accordingly to the specification the recommended values for DDS_CLK_RATE + * are: + * Case 1: 2^n, n = [0, 23] + * Case 2: 2^24 * 2 / 5 (0x666666) + * Case 3: less than or equal to 2^24 / 5 / 16 (0x33333) + * + * In all cases the lowest possible value is better. + * + * The function calculates parameters for all cases and chooses the one closest + * to the asked baud rate. */ -static u32 quark_x1000_set_clk_regvals(u32 rate, u32 *dds, u32 *clk_div) +static unsigned int quark_x1000_get_clk_div(int rate, u32 *dds) { - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(quark_spi_rate_table); i++) { - if (rate >= quark_spi_rate_table[i].bitrate) { - *dds = quark_spi_rate_table[i].dds_clk_rate; - *clk_div = quark_spi_rate_table[i].clk_div; - return quark_spi_rate_table[i].bitrate; + unsigned long xtal = 200000000; + unsigned long fref = xtal / 2; /* mandatory division by 2, + see (2) */ + /* case 3 */ + unsigned long fref1 = fref / 2; /* case 1 */ + unsigned long fref2 = fref * 2 / 5; /* case 2 */ + unsigned long scale; + unsigned long q, q1, q2; + long r, r1, r2; + u32 mul; + + /* Case 1 */ + + /* Set initial value for DDS_CLK_RATE */ + mul = (1 << 24) >> 1; + + /* Calculate initial quot */ + q1 = DIV_ROUND_CLOSEST(fref1, rate); + + /* Scale q1 if it's too big */ + if (q1 > 256) { + /* Scale q1 to range [1, 512] */ + scale = fls_long(q1 - 1); + if (scale > 9) { + q1 >>= scale - 9; + mul >>= scale - 9; } + + /* Round the result if we have a remainder */ + q1 += q1 & 1; } - *dds = quark_spi_rate_table[i-1].dds_clk_rate; - *clk_div = quark_spi_rate_table[i-1].clk_div; + /* Decrease DDS_CLK_RATE as much as we can without loss in precision */ + scale = __ffs(q1); + q1 >>= scale; + mul >>= scale; + + /* Get the remainder */ + r1 = abs(fref1 / (1 << (24 - fls_long(mul))) / q1 - rate); + + /* Case 2 */ + + q2 = DIV_ROUND_CLOSEST(fref2, rate); + r2 = abs(fref2 / q2 - rate); - return quark_spi_rate_table[i-1].bitrate; + /* + * Choose the best between two: less remainder we have the better. We + * can't go case 2 if q2 is greater than 256 since SCR register can + * hold only values 0 .. 255. + */ + if (r2 >= r1 || q2 > 256) { + /* case 1 is better */ + r = r1; + q = q1; + } else { + /* case 2 is better */ + r = r2; + q = q2; + mul = (1 << 24) * 2 / 5; + } + + /* Check case 3 only If the divisor is big enough */ + if (fref / rate >= 80) { + u64 fssp; + u32 m; + + /* Calculate initial quot */ + q1 = DIV_ROUND_CLOSEST(fref, rate); + m = (1 << 24) / q1; + + /* Get the remainder */ + fssp = (u64)fref * m; + do_div(fssp, 1 << 24); + r1 = abs(fssp - rate); + + /* Choose this one if it suits better */ + if (r1 < r) { + /* case 3 is better */ + q = 1; + mul = m; + } + } + + *dds = mul; + return q - 1; } static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate) @@ -730,23 +778,25 @@ static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate) rate = min_t(int, ssp_clk, rate); if (ssp->type == PXA25x_SSP || ssp->type == CE4100_SSP) - return ((ssp_clk / (2 * rate) - 1) & 0xff) << 8; + return (ssp_clk / (2 * rate) - 1) & 0xff; else - return ((ssp_clk / rate - 1) & 0xfff) << 8; + return (ssp_clk / rate - 1) & 0xfff; } static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data, struct chip_data *chip, int rate) { - u32 clk_div; + unsigned int clk_div; switch (drv_data->ssp_type) { case QUARK_X1000_SSP: - quark_x1000_set_clk_regvals(rate, &chip->dds_rate, &clk_div); - return clk_div << 8; + clk_div = quark_x1000_get_clk_div(rate, &chip->dds_rate); + break; default: - return ssp_get_clk_div(drv_data, rate); + clk_div = ssp_get_clk_div(drv_data, rate); + break; } + return clk_div << 8; } static void pump_transfers(unsigned long data) diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index ff9cdbd..810a7fa 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -22,6 +22,8 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/spi/spi.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> #define QUP_CONFIG 0x0000 #define QUP_STATE 0x0004 @@ -116,6 +118,8 @@ #define SPI_NUM_CHIPSELECTS 4 +#define SPI_MAX_DMA_XFER (SZ_64K - 64) + /* high speed mode is when bus rate is greater then 26MHz */ #define SPI_HS_MIN_RATE 26000000 #define SPI_MAX_RATE 50000000 @@ -140,9 +144,14 @@ struct spi_qup { struct completion done; int error; int w_size; /* bytes per SPI word */ + int n_words; int tx_bytes; int rx_bytes; int qup_v1; + + int use_dma; + struct dma_slave_config rx_conf; + struct dma_slave_config tx_conf; }; @@ -198,7 +207,6 @@ static int spi_qup_set_state(struct spi_qup *controller, u32 state) return 0; } - static void spi_qup_fifo_read(struct spi_qup *controller, struct spi_transfer *xfer) { @@ -266,6 +274,107 @@ static void spi_qup_fifo_write(struct spi_qup *controller, } } +static void spi_qup_dma_done(void *data) +{ + struct spi_qup *qup = data; + + complete(&qup->done); +} + +static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer, + enum dma_transfer_direction dir, + dma_async_tx_callback callback) +{ + struct spi_qup *qup = spi_master_get_devdata(master); + unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE; + struct dma_async_tx_descriptor *desc; + struct scatterlist *sgl; + struct dma_chan *chan; + dma_cookie_t cookie; + unsigned int nents; + + if (dir == DMA_MEM_TO_DEV) { + chan = master->dma_tx; + nents = xfer->tx_sg.nents; + sgl = xfer->tx_sg.sgl; + } else { + chan = master->dma_rx; + nents = xfer->rx_sg.nents; + sgl = xfer->rx_sg.sgl; + } + + desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags); + if (!desc) + return -EINVAL; + + desc->callback = callback; + desc->callback_param = qup; + + cookie = dmaengine_submit(desc); + + return dma_submit_error(cookie); +} + +static void spi_qup_dma_terminate(struct spi_master *master, + struct spi_transfer *xfer) +{ + if (xfer->tx_buf) + dmaengine_terminate_all(master->dma_tx); + if (xfer->rx_buf) + dmaengine_terminate_all(master->dma_rx); +} + +static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer) +{ + dma_async_tx_callback rx_done = NULL, tx_done = NULL; + int ret; + + if (xfer->rx_buf) + rx_done = spi_qup_dma_done; + else if (xfer->tx_buf) + tx_done = spi_qup_dma_done; + + if (xfer->rx_buf) { + ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done); + if (ret) + return ret; + + dma_async_issue_pending(master->dma_rx); + } + + if (xfer->tx_buf) { + ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done); + if (ret) + return ret; + + dma_async_issue_pending(master->dma_tx); + } + + return 0; +} + +static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer) +{ + struct spi_qup *qup = spi_master_get_devdata(master); + int ret; + + ret = spi_qup_set_state(qup, QUP_STATE_RUN); + if (ret) { + dev_warn(qup->dev, "cannot set RUN state\n"); + return ret; + } + + ret = spi_qup_set_state(qup, QUP_STATE_PAUSE); + if (ret) { + dev_warn(qup->dev, "cannot set PAUSE state\n"); + return ret; + } + + spi_qup_fifo_write(qup, xfer); + + return 0; +} + static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) { struct spi_qup *controller = dev_id; @@ -315,11 +424,13 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) error = -EIO; } - if (opflags & QUP_OP_IN_SERVICE_FLAG) - spi_qup_fifo_read(controller, xfer); + if (!controller->use_dma) { + if (opflags & QUP_OP_IN_SERVICE_FLAG) + spi_qup_fifo_read(controller, xfer); - if (opflags & QUP_OP_OUT_SERVICE_FLAG) - spi_qup_fifo_write(controller, xfer); + if (opflags & QUP_OP_OUT_SERVICE_FLAG) + spi_qup_fifo_write(controller, xfer); + } spin_lock_irqsave(&controller->lock, flags); controller->error = error; @@ -332,13 +443,35 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static u32 +spi_qup_get_mode(struct spi_master *master, struct spi_transfer *xfer) +{ + struct spi_qup *qup = spi_master_get_devdata(master); + u32 mode; + + qup->w_size = 4; + + if (xfer->bits_per_word <= 8) + qup->w_size = 1; + else if (xfer->bits_per_word <= 16) + qup->w_size = 2; + + qup->n_words = xfer->len / qup->w_size; + + if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32))) + mode = QUP_IO_M_MODE_FIFO; + else + mode = QUP_IO_M_MODE_BLOCK; + + return mode; +} /* set clock freq ... bits per word */ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) { struct spi_qup *controller = spi_master_get_devdata(spi->master); u32 config, iomode, mode, control; - int ret, n_words, w_size; + int ret, n_words; if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { dev_err(controller->dev, "too big size for loopback %d > %d\n", @@ -358,35 +491,54 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) return -EIO; } - w_size = 4; - if (xfer->bits_per_word <= 8) - w_size = 1; - else if (xfer->bits_per_word <= 16) - w_size = 2; + mode = spi_qup_get_mode(spi->master, xfer); + n_words = controller->n_words; - n_words = xfer->len / w_size; - controller->w_size = w_size; - - if (n_words <= (controller->in_fifo_sz / sizeof(u32))) { - mode = QUP_IO_M_MODE_FIFO; + if (mode == QUP_IO_M_MODE_FIFO) { writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT); /* must be zero for FIFO */ writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); - } else { - mode = QUP_IO_M_MODE_BLOCK; + } else if (!controller->use_dma) { writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); /* must be zero for BLOCK and BAM */ writel_relaxed(0, controller->base + QUP_MX_READ_CNT); writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); + } else { + mode = QUP_IO_M_MODE_BAM; + writel_relaxed(0, controller->base + QUP_MX_READ_CNT); + writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); + + if (!controller->qup_v1) { + void __iomem *input_cnt; + + input_cnt = controller->base + QUP_MX_INPUT_CNT; + /* + * for DMA transfers, both QUP_MX_INPUT_CNT and + * QUP_MX_OUTPUT_CNT must be zero to all cases but one. + * That case is a non-balanced transfer when there is + * only a rx_buf. + */ + if (xfer->tx_buf) + writel_relaxed(0, input_cnt); + else + writel_relaxed(n_words, input_cnt); + + writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); + } } iomode = readl_relaxed(controller->base + QUP_IO_M_MODES); /* Set input and output transfer mode */ iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK); - iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); + + if (!controller->use_dma) + iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); + else + iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN; + iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); @@ -428,11 +580,31 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N); config |= xfer->bits_per_word - 1; config |= QUP_CONFIG_SPI_MODE; + + if (controller->use_dma) { + if (!xfer->tx_buf) + config |= QUP_CONFIG_NO_OUTPUT; + if (!xfer->rx_buf) + config |= QUP_CONFIG_NO_INPUT; + } + writel_relaxed(config, controller->base + QUP_CONFIG); /* only write to OPERATIONAL_MASK when register is present */ - if (!controller->qup_v1) - writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK); + if (!controller->qup_v1) { + u32 mask = 0; + + /* + * mask INPUT and OUTPUT service flags to prevent IRQs on FIFO + * status change in BAM mode + */ + + if (mode == QUP_IO_M_MODE_BAM) + mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG; + + writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK); + } + return 0; } @@ -461,17 +633,13 @@ static int spi_qup_transfer_one(struct spi_master *master, controller->tx_bytes = 0; spin_unlock_irqrestore(&controller->lock, flags); - if (spi_qup_set_state(controller, QUP_STATE_RUN)) { - dev_warn(controller->dev, "cannot set RUN state\n"); - goto exit; - } + if (controller->use_dma) + ret = spi_qup_do_dma(master, xfer); + else + ret = spi_qup_do_pio(master, xfer); - if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) { - dev_warn(controller->dev, "cannot set PAUSE state\n"); + if (ret) goto exit; - } - - spi_qup_fifo_write(controller, xfer); if (spi_qup_set_state(controller, QUP_STATE_RUN)) { dev_warn(controller->dev, "cannot set EXECUTE state\n"); @@ -480,6 +648,7 @@ static int spi_qup_transfer_one(struct spi_master *master, if (!wait_for_completion_timeout(&controller->done, timeout)) ret = -ETIMEDOUT; + exit: spi_qup_set_state(controller, QUP_STATE_RESET); spin_lock_irqsave(&controller->lock, flags); @@ -487,6 +656,97 @@ exit: if (!ret) ret = controller->error; spin_unlock_irqrestore(&controller->lock, flags); + + if (ret && controller->use_dma) + spi_qup_dma_terminate(master, xfer); + + return ret; +} + +static bool spi_qup_can_dma(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct spi_qup *qup = spi_master_get_devdata(master); + size_t dma_align = dma_get_cache_alignment(); + u32 mode; + + qup->use_dma = 0; + + if (xfer->rx_buf && (xfer->len % qup->in_blk_sz || + IS_ERR_OR_NULL(master->dma_rx) || + !IS_ALIGNED((size_t)xfer->rx_buf, dma_align))) + return false; + + if (xfer->tx_buf && (xfer->len % qup->out_blk_sz || + IS_ERR_OR_NULL(master->dma_tx) || + !IS_ALIGNED((size_t)xfer->tx_buf, dma_align))) + return false; + + mode = spi_qup_get_mode(master, xfer); + if (mode == QUP_IO_M_MODE_FIFO) + return false; + + qup->use_dma = 1; + + return true; +} + +static void spi_qup_release_dma(struct spi_master *master) +{ + if (!IS_ERR_OR_NULL(master->dma_rx)) + dma_release_channel(master->dma_rx); + if (!IS_ERR_OR_NULL(master->dma_tx)) + dma_release_channel(master->dma_tx); +} + +static int spi_qup_init_dma(struct spi_master *master, resource_size_t base) +{ + struct spi_qup *spi = spi_master_get_devdata(master); + struct dma_slave_config *rx_conf = &spi->rx_conf, + *tx_conf = &spi->tx_conf; + struct device *dev = spi->dev; + int ret; + + /* allocate dma resources, if available */ + master->dma_rx = dma_request_slave_channel_reason(dev, "rx"); + if (IS_ERR(master->dma_rx)) + return PTR_ERR(master->dma_rx); + + master->dma_tx = dma_request_slave_channel_reason(dev, "tx"); + if (IS_ERR(master->dma_tx)) { + ret = PTR_ERR(master->dma_tx); + goto err_tx; + } + + /* set DMA parameters */ + rx_conf->direction = DMA_DEV_TO_MEM; + rx_conf->device_fc = 1; + rx_conf->src_addr = base + QUP_INPUT_FIFO; + rx_conf->src_maxburst = spi->in_blk_sz; + + tx_conf->direction = DMA_MEM_TO_DEV; + tx_conf->device_fc = 1; + tx_conf->dst_addr = base + QUP_OUTPUT_FIFO; + tx_conf->dst_maxburst = spi->out_blk_sz; + + ret = dmaengine_slave_config(master->dma_rx, rx_conf); + if (ret) { + dev_err(dev, "failed to configure RX channel\n"); + goto err; + } + + ret = dmaengine_slave_config(master->dma_tx, tx_conf); + if (ret) { + dev_err(dev, "failed to configure TX channel\n"); + goto err; + } + + return 0; + +err: + dma_release_channel(master->dma_tx); +err_tx: + dma_release_channel(master->dma_rx); return ret; } @@ -498,7 +758,7 @@ static int spi_qup_probe(struct platform_device *pdev) struct resource *res; struct device *dev; void __iomem *base; - u32 max_freq, iomode; + u32 max_freq, iomode, num_cs; int ret, irq, size; dev = &pdev->dev; @@ -550,10 +810,11 @@ static int spi_qup_probe(struct platform_device *pdev) } /* use num-cs unless not present or out of range */ - if (of_property_read_u16(dev->of_node, "num-cs", - &master->num_chipselect) || - (master->num_chipselect > SPI_NUM_CHIPSELECTS)) + if (of_property_read_u32(dev->of_node, "num-cs", &num_cs) || + num_cs > SPI_NUM_CHIPSELECTS) master->num_chipselect = SPI_NUM_CHIPSELECTS; + else + master->num_chipselect = num_cs; master->bus_num = pdev->id; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; @@ -562,6 +823,8 @@ static int spi_qup_probe(struct platform_device *pdev) master->transfer_one = spi_qup_transfer_one; master->dev.of_node = pdev->dev.of_node; master->auto_runtime_pm = true; + master->dma_alignment = dma_get_cache_alignment(); + master->max_dma_len = SPI_MAX_DMA_XFER; platform_set_drvdata(pdev, master); @@ -573,6 +836,12 @@ static int spi_qup_probe(struct platform_device *pdev) controller->cclk = cclk; controller->irq = irq; + ret = spi_qup_init_dma(master, res->start); + if (ret == -EPROBE_DEFER) + goto error; + else if (!ret) + master->can_dma = spi_qup_can_dma; + /* set v1 flag if device is version 1 */ if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1")) controller->qup_v1 = 1; @@ -609,7 +878,7 @@ static int spi_qup_probe(struct platform_device *pdev) ret = spi_qup_set_state(controller, QUP_STATE_RESET); if (ret) { dev_err(dev, "cannot set RESET state\n"); - goto error; + goto error_dma; } writel_relaxed(0, base + QUP_OPERATIONAL); @@ -633,7 +902,7 @@ static int spi_qup_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, spi_qup_qup_irq, IRQF_TRIGGER_HIGH, pdev->name, controller); if (ret) - goto error; + goto error_dma; pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); pm_runtime_use_autosuspend(dev); @@ -648,6 +917,8 @@ static int spi_qup_probe(struct platform_device *pdev) disable_pm: pm_runtime_disable(&pdev->dev); +error_dma: + spi_qup_release_dma(master); error: clk_disable_unprepare(cclk); clk_disable_unprepare(iclk); @@ -739,6 +1010,8 @@ static int spi_qup_remove(struct platform_device *pdev) if (ret) return ret; + spi_qup_release_dma(master); + clk_disable_unprepare(controller->cclk); clk_disable_unprepare(controller->iclk); diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 1a777dc..68e7efe 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -179,6 +179,7 @@ struct rockchip_spi { u8 tmode; u8 bpw; u8 n_bytes; + u8 rsd_nsecs; unsigned len; u32 speed; @@ -302,8 +303,8 @@ static int rockchip_spi_prepare_message(struct spi_master *master, return 0; } -static int rockchip_spi_unprepare_message(struct spi_master *master, - struct spi_message *msg) +static void rockchip_spi_handle_err(struct spi_master *master, + struct spi_message *msg) { unsigned long flags; struct rockchip_spi *rs = spi_master_get_devdata(master); @@ -313,8 +314,8 @@ static int rockchip_spi_unprepare_message(struct spi_master *master, /* * For DMA mode, we need terminate DMA channel and flush * fifo for the next transfer if DMA thansfer timeout. - * unprepare_message() was called by core if transfer complete - * or timeout. Maybe it is reasonable for error handling here. + * handle_err() was called by core if transfer failed. + * Maybe it is reasonable for error handling here. */ if (rs->use_dma) { if (rs->state & RXBUSY) { @@ -327,6 +328,12 @@ static int rockchip_spi_unprepare_message(struct spi_master *master, } spin_unlock_irqrestore(&rs->lock, flags); +} + +static int rockchip_spi_unprepare_message(struct spi_master *master, + struct spi_message *msg) +{ + struct rockchip_spi *rs = spi_master_get_devdata(master); spi_enable_chip(rs, 0); @@ -493,6 +500,7 @@ static void rockchip_spi_config(struct rockchip_spi *rs) { u32 div = 0; u32 dmacr = 0; + int rsd = 0; u32 cr0 = (CR0_BHT_8BIT << CR0_BHT_OFFSET) | (CR0_SSD_ONE << CR0_SSD_OFFSET); @@ -519,9 +527,23 @@ static void rockchip_spi_config(struct rockchip_spi *rs) } /* div doesn't support odd number */ - div = max_t(u32, rs->max_freq / rs->speed, 1); + div = DIV_ROUND_UP(rs->max_freq, rs->speed); div = (div + 1) & 0xfffe; + /* Rx sample delay is expressed in parent clock cycles (max 3) */ + rsd = DIV_ROUND_CLOSEST(rs->rsd_nsecs * (rs->max_freq >> 8), + 1000000000 >> 8); + if (!rsd && rs->rsd_nsecs) { + pr_warn_once("rockchip-spi: %u Hz are too slow to express %u ns delay\n", + rs->max_freq, rs->rsd_nsecs); + } else if (rsd > 3) { + rsd = 3; + pr_warn_once("rockchip-spi: %u Hz are too fast to express %u ns delay, clamping at %u ns\n", + rs->max_freq, rs->rsd_nsecs, + rsd * 1000000000U / rs->max_freq); + } + cr0 |= rsd << CR0_RSD_OFFSET; + writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0); writel_relaxed(rs->len - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); @@ -614,6 +636,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) struct rockchip_spi *rs; struct spi_master *master; struct resource *mem; + u32 rsd_nsecs; master = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi)); if (!master) @@ -665,6 +688,10 @@ static int rockchip_spi_probe(struct platform_device *pdev) rs->dev = &pdev->dev; rs->max_freq = clk_get_rate(rs->spiclk); + if (!of_property_read_u32(pdev->dev.of_node, "rx-sample-delay-ns", + &rsd_nsecs)) + rs->rsd_nsecs = rsd_nsecs; + rs->fifo_len = get_fifo_len(rs); if (!rs->fifo_len) { dev_err(&pdev->dev, "Failed to get fifo length\n"); @@ -688,6 +715,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) master->prepare_message = rockchip_spi_prepare_message; master->unprepare_message = rockchip_spi_unprepare_message; master->transfer_one = rockchip_spi_transfer_one; + master->handle_err = rockchip_spi_handle_err; rs->dma_tx.ch = dma_request_slave_channel(rs->dev, "tx"); if (!rs->dma_tx.ch) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 46ce470..186924a 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -177,6 +177,13 @@ #define SPBFCR_RXRST 0x40 /* Receive Buffer Data Reset */ #define SPBFCR_TXTRG_MASK 0x30 /* Transmit Buffer Data Triggering Number */ #define SPBFCR_RXTRG_MASK 0x07 /* Receive Buffer Data Triggering Number */ +/* QSPI on R-Car Gen2 */ +#define SPBFCR_TXTRG_1B 0x00 /* 31 bytes (1 byte available) */ +#define SPBFCR_TXTRG_32B 0x30 /* 0 byte (32 bytes available) */ +#define SPBFCR_RXTRG_1B 0x00 /* 1 byte (31 bytes available) */ +#define SPBFCR_RXTRG_32B 0x07 /* 32 bytes (0 byte available) */ + +#define QSPI_BUFFER_SIZE 32u struct rspi_data { void __iomem *addr; @@ -366,6 +373,52 @@ static int qspi_set_config_register(struct rspi_data *rspi, int access_size) return 0; } +static void qspi_update(const struct rspi_data *rspi, u8 mask, u8 val, u8 reg) +{ + u8 data; + + data = rspi_read8(rspi, reg); + data &= ~mask; + data |= (val & mask); + rspi_write8(rspi, data, reg); +} + +static int qspi_set_send_trigger(struct rspi_data *rspi, unsigned int len) +{ + unsigned int n; + + n = min(len, QSPI_BUFFER_SIZE); + + if (len >= QSPI_BUFFER_SIZE) { + /* sets triggering number to 32 bytes */ + qspi_update(rspi, SPBFCR_TXTRG_MASK, + SPBFCR_TXTRG_32B, QSPI_SPBFCR); + } else { + /* sets triggering number to 1 byte */ + qspi_update(rspi, SPBFCR_TXTRG_MASK, + SPBFCR_TXTRG_1B, QSPI_SPBFCR); + } + + return n; +} + +static void qspi_set_receive_trigger(struct rspi_data *rspi, unsigned int len) +{ + unsigned int n; + + n = min(len, QSPI_BUFFER_SIZE); + + if (len >= QSPI_BUFFER_SIZE) { + /* sets triggering number to 32 bytes */ + qspi_update(rspi, SPBFCR_RXTRG_MASK, + SPBFCR_RXTRG_32B, QSPI_SPBFCR); + } else { + /* sets triggering number to 1 byte */ + qspi_update(rspi, SPBFCR_RXTRG_MASK, + SPBFCR_RXTRG_1B, QSPI_SPBFCR); + } +} + #define set_config_register(spi, n) spi->ops->set_config_register(spi, n) static void rspi_enable_irq(const struct rspi_data *rspi, u8 enable) @@ -609,19 +662,29 @@ static bool rspi_can_dma(struct spi_master *master, struct spi_device *spi, return __rspi_can_dma(rspi, xfer); } -static int rspi_common_transfer(struct rspi_data *rspi, - struct spi_transfer *xfer) +static int rspi_dma_check_then_transfer(struct rspi_data *rspi, + struct spi_transfer *xfer) { - int ret; - if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) { /* rx_buf can be NULL on RSPI on SH in TX-only Mode */ - ret = rspi_dma_transfer(rspi, &xfer->tx_sg, + int ret = rspi_dma_transfer(rspi, &xfer->tx_sg, xfer->rx_buf ? &xfer->rx_sg : NULL); if (ret != -EAGAIN) - return ret; + return 0; } + return -EAGAIN; +} + +static int rspi_common_transfer(struct rspi_data *rspi, + struct spi_transfer *xfer) +{ + int ret; + + ret = rspi_dma_check_then_transfer(rspi, xfer); + if (ret != -EAGAIN) + return ret; + ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); if (ret < 0) return ret; @@ -661,12 +724,59 @@ static int rspi_rz_transfer_one(struct spi_master *master, return rspi_common_transfer(rspi, xfer); } +static int qspi_trigger_transfer_out_int(struct rspi_data *rspi, const u8 *tx, + u8 *rx, unsigned int len) +{ + int i, n, ret; + int error; + + while (len > 0) { + n = qspi_set_send_trigger(rspi, len); + qspi_set_receive_trigger(rspi, len); + if (n == QSPI_BUFFER_SIZE) { + error = rspi_wait_for_tx_empty(rspi); + if (error < 0) { + dev_err(&rspi->master->dev, "transmit timeout\n"); + return error; + } + for (i = 0; i < n; i++) + rspi_write_data(rspi, *tx++); + + error = rspi_wait_for_rx_full(rspi); + if (error < 0) { + dev_err(&rspi->master->dev, "receive timeout\n"); + return error; + } + for (i = 0; i < n; i++) + *rx++ = rspi_read_data(rspi); + } else { + ret = rspi_pio_transfer(rspi, tx, rx, n); + if (ret < 0) + return ret; + } + len -= n; + } + + return 0; +} + static int qspi_transfer_out_in(struct rspi_data *rspi, struct spi_transfer *xfer) { + int ret; + qspi_receive_init(rspi); - return rspi_common_transfer(rspi, xfer); + ret = rspi_dma_check_then_transfer(rspi, xfer); + if (ret != -EAGAIN) + return ret; + + ret = qspi_trigger_transfer_out_int(rspi, xfer->tx_buf, + xfer->rx_buf, xfer->len); + if (ret < 0) + return ret; + + return 0; } static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 9231c34..b1c6731 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -324,7 +324,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) /* Acquire DMA channels */ sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter, - (void *)sdd->rx_dma.dmach, dev, "rx"); + (void *)(long)sdd->rx_dma.dmach, dev, "rx"); if (!sdd->rx_dma.ch) { dev_err(dev, "Failed to get RX DMA channel\n"); ret = -EBUSY; @@ -333,7 +333,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) spi->dma_rx = sdd->rx_dma.ch; sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter, - (void *)sdd->tx_dma.dmach, dev, "tx"); + (void *)(long)sdd->tx_dma.dmach, dev, "tx"); if (!sdd->tx_dma.ch) { dev_err(dev, "Failed to get TX DMA channel\n"); ret = -EBUSY; diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c index 5a56acf..36af4d4 100644 --- a/drivers/spi/spi-sc18is602.c +++ b/drivers/spi/spi-sc18is602.c @@ -286,7 +286,7 @@ static int sc18is602_probe(struct i2c_client *client, hw->freq = SC18IS602_CLOCK; break; } - master->bus_num = client->adapter->nr; + master->bus_num = np ? -1 : client->adapter->nr; master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST; master->bits_per_word_mask = SPI_BPW_MASK(8); master->setup = sc18is602_setup; diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c index 2faeaa7..f17c0ab 100644 --- a/drivers/spi/spi-st-ssc4.c +++ b/drivers/spi/spi-st-ssc4.c @@ -482,7 +482,7 @@ static const struct dev_pm_ops spi_st_pm = { SET_RUNTIME_PM_OPS(spi_st_runtime_suspend, spi_st_runtime_resume, NULL) }; -static struct of_device_id stm_spi_match[] = { +static const struct of_device_id stm_spi_match[] = { { .compatible = "st,comms-ssc4-spi", }, {}, }; diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index 884a716..5c06168 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -101,6 +101,7 @@ struct ti_qspi { #define QSPI_FLEN(n) ((n - 1) << 0) /* STATUS REGISTER */ +#define BUSY 0x01 #define WC 0x02 /* INTERRUPT REGISTER */ @@ -199,6 +200,21 @@ static void ti_qspi_restore_ctx(struct ti_qspi *qspi) ti_qspi_write(qspi, ctx_reg->clkctrl, QSPI_SPI_CLOCK_CNTRL_REG); } +static inline u32 qspi_is_busy(struct ti_qspi *qspi) +{ + u32 stat; + unsigned long timeout = jiffies + QSPI_COMPLETION_TIMEOUT; + + stat = ti_qspi_read(qspi, QSPI_SPI_STATUS_REG); + while ((stat & BUSY) && time_after(timeout, jiffies)) { + cpu_relax(); + stat = ti_qspi_read(qspi, QSPI_SPI_STATUS_REG); + } + + WARN(stat & BUSY, "qspi busy\n"); + return stat & BUSY; +} + static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t) { int wlen, count; @@ -211,6 +227,9 @@ static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t) wlen = t->bits_per_word >> 3; /* in bytes */ while (count) { + if (qspi_is_busy(qspi)) + return -EBUSY; + switch (wlen) { case 1: dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %02x\n", @@ -266,6 +285,9 @@ static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t) while (count) { dev_dbg(qspi->dev, "rx cmd %08x dc %08x\n", cmd, qspi->dc); + if (qspi_is_busy(qspi)) + return -EBUSY; + ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG); if (!wait_for_completion_timeout(&qspi->transfer_complete, QSPI_COMPLETION_TIMEOUT)) { diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index c64a3e5..d5d7d22 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -16,7 +16,6 @@ */ #include <linux/kernel.h> -#include <linux/kmod.h> #include <linux/device.h> #include <linux/init.h> #include <linux/cache.h> @@ -129,125 +128,11 @@ static int spi_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } -#ifdef CONFIG_PM_SLEEP -static int spi_legacy_suspend(struct device *dev, pm_message_t message) -{ - int value = 0; - struct spi_driver *drv = to_spi_driver(dev->driver); - - /* suspend will stop irqs and dma; no more i/o */ - if (drv) { - if (drv->suspend) - value = drv->suspend(to_spi_device(dev), message); - else - dev_dbg(dev, "... can't suspend\n"); - } - return value; -} - -static int spi_legacy_resume(struct device *dev) -{ - int value = 0; - struct spi_driver *drv = to_spi_driver(dev->driver); - - /* resume may restart the i/o queue */ - if (drv) { - if (drv->resume) - value = drv->resume(to_spi_device(dev)); - else - dev_dbg(dev, "... can't resume\n"); - } - return value; -} - -static int spi_pm_suspend(struct device *dev) -{ - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - if (pm) - return pm_generic_suspend(dev); - else - return spi_legacy_suspend(dev, PMSG_SUSPEND); -} - -static int spi_pm_resume(struct device *dev) -{ - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - if (pm) - return pm_generic_resume(dev); - else - return spi_legacy_resume(dev); -} - -static int spi_pm_freeze(struct device *dev) -{ - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - if (pm) - return pm_generic_freeze(dev); - else - return spi_legacy_suspend(dev, PMSG_FREEZE); -} - -static int spi_pm_thaw(struct device *dev) -{ - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - if (pm) - return pm_generic_thaw(dev); - else - return spi_legacy_resume(dev); -} - -static int spi_pm_poweroff(struct device *dev) -{ - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - if (pm) - return pm_generic_poweroff(dev); - else - return spi_legacy_suspend(dev, PMSG_HIBERNATE); -} - -static int spi_pm_restore(struct device *dev) -{ - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - if (pm) - return pm_generic_restore(dev); - else - return spi_legacy_resume(dev); -} -#else -#define spi_pm_suspend NULL -#define spi_pm_resume NULL -#define spi_pm_freeze NULL -#define spi_pm_thaw NULL -#define spi_pm_poweroff NULL -#define spi_pm_restore NULL -#endif - -static const struct dev_pm_ops spi_pm = { - .suspend = spi_pm_suspend, - .resume = spi_pm_resume, - .freeze = spi_pm_freeze, - .thaw = spi_pm_thaw, - .poweroff = spi_pm_poweroff, - .restore = spi_pm_restore, - SET_RUNTIME_PM_OPS( - pm_generic_runtime_suspend, - pm_generic_runtime_resume, - NULL - ) -}; - struct bus_type spi_bus_type = { .name = "spi", .dev_groups = spi_dev_groups, .match = spi_match_device, .uevent = spi_uevent, - .pm = &spi_pm, }; EXPORT_SYMBOL_GPL(spi_bus_type); @@ -851,6 +736,9 @@ out: if (msg->status == -EINPROGRESS) msg->status = ret; + if (msg->status && master->handle_err) + master->handle_err(master, msg); + spi_finalize_current_message(master); return ret; @@ -1105,13 +993,14 @@ void spi_finalize_current_message(struct spi_master *master) "failed to unprepare message: %d\n", ret); } } + + trace_spi_message_done(mesg); + master->cur_msg_prepared = false; mesg->state = NULL; if (mesg->complete) mesg->complete(mesg->context); - - trace_spi_message_done(mesg); } EXPORT_SYMBOL_GPL(spi_finalize_current_message); @@ -1359,7 +1248,6 @@ of_register_spi_device(struct spi_master *master, struct device_node *nc) spi->dev.of_node = nc; /* Register the new device */ - request_module("%s%s", SPI_MODULE_PREFIX, spi->modalias); rc = spi_add_device(spi); if (rc) { dev_err(&master->dev, "spi_device register error %s\n", @@ -1893,6 +1781,8 @@ int spi_setup(struct spi_device *spi) if (!spi->max_speed_hz) spi->max_speed_hz = spi->master->max_speed_hz; + spi_set_cs(spi, false); + if (spi->master->setup) status = spi->master->setup(spi); diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 4eb7a98..92c909e 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -223,7 +223,7 @@ static int spidev_message(struct spidev_data *spidev, struct spi_transfer *k_xfers; struct spi_transfer *k_tmp; struct spi_ioc_transfer *u_tmp; - unsigned n, total; + unsigned n, total, tx_total, rx_total; u8 *tx_buf, *rx_buf; int status = -EFAULT; @@ -239,33 +239,52 @@ static int spidev_message(struct spidev_data *spidev, tx_buf = spidev->tx_buffer; rx_buf = spidev->rx_buffer; total = 0; + tx_total = 0; + rx_total = 0; for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers; n; n--, k_tmp++, u_tmp++) { k_tmp->len = u_tmp->len; total += k_tmp->len; - if (total > bufsiz) { + /* Since the function returns the total length of transfers + * on success, restrict the total to positive int values to + * avoid the return value looking like an error. Also check + * each transfer length to avoid arithmetic overflow. + */ + if (total > INT_MAX || k_tmp->len > INT_MAX) { status = -EMSGSIZE; goto done; } if (u_tmp->rx_buf) { + /* this transfer needs space in RX bounce buffer */ + rx_total += k_tmp->len; + if (rx_total > bufsiz) { + status = -EMSGSIZE; + goto done; + } k_tmp->rx_buf = rx_buf; if (!access_ok(VERIFY_WRITE, (u8 __user *) (uintptr_t) u_tmp->rx_buf, u_tmp->len)) goto done; + rx_buf += k_tmp->len; } if (u_tmp->tx_buf) { + /* this transfer needs space in TX bounce buffer */ + tx_total += k_tmp->len; + if (tx_total > bufsiz) { + status = -EMSGSIZE; + goto done; + } k_tmp->tx_buf = tx_buf; if (copy_from_user(tx_buf, (const u8 __user *) (uintptr_t) u_tmp->tx_buf, u_tmp->len)) goto done; + tx_buf += k_tmp->len; } - tx_buf += k_tmp->len; - rx_buf += k_tmp->len; k_tmp->cs_change = !!u_tmp->cs_change; k_tmp->tx_nbits = u_tmp->tx_nbits; @@ -303,8 +322,8 @@ static int spidev_message(struct spidev_data *spidev, status = -EFAULT; goto done; } + rx_buf += u_tmp->len; } - rx_buf += u_tmp->len; } status = total; @@ -684,6 +703,14 @@ static const struct file_operations spidev_fops = { static struct class *spidev_class; +#ifdef CONFIG_OF +static const struct of_device_id spidev_dt_ids[] = { + { .compatible = "rohm,dh2228fv" }, + {}, +}; +MODULE_DEVICE_TABLE(of, spidev_dt_ids); +#endif + /*-------------------------------------------------------------------------*/ static int spidev_probe(struct spi_device *spi) @@ -692,6 +719,17 @@ static int spidev_probe(struct spi_device *spi) int status; unsigned long minor; + /* + * spidev should never be referenced in DT without a specific + * compatbile string, it is a Linux implementation thing + * rather than a description of the hardware. + */ + if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) { + dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n"); + WARN_ON(spi->dev.of_node && + !of_match_device(spidev_dt_ids, &spi->dev)); + } + /* Allocate driver data */ spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); if (!spidev) @@ -758,13 +796,6 @@ static int spidev_remove(struct spi_device *spi) return 0; } -static const struct of_device_id spidev_dt_ids[] = { - { .compatible = "rohm,dh2228fv" }, - {}, -}; - -MODULE_DEVICE_TABLE(of, spidev_dt_ids); - static struct spi_driver spidev_spi_driver = { .driver = { .name = "spidev", diff --git a/drivers/staging/comedi/drivers/adv_pci1710.c b/drivers/staging/comedi/drivers/adv_pci1710.c index 9800c01..3f72451 100644 --- a/drivers/staging/comedi/drivers/adv_pci1710.c +++ b/drivers/staging/comedi/drivers/adv_pci1710.c @@ -426,7 +426,6 @@ static int pci171x_ai_insn_read(struct comedi_device *dev, unsigned int *data) { struct pci1710_private *devpriv = dev->private; - unsigned int chan = CR_CHAN(insn->chanspec); int ret = 0; int i; @@ -447,7 +446,7 @@ static int pci171x_ai_insn_read(struct comedi_device *dev, if (ret) break; - ret = pci171x_ai_read_sample(dev, s, chan, &val); + ret = pci171x_ai_read_sample(dev, s, 0, &val); if (ret) break; diff --git a/drivers/staging/comedi/drivers/comedi_isadma.c b/drivers/staging/comedi/drivers/comedi_isadma.c index dbdea71..e856f01 100644 --- a/drivers/staging/comedi/drivers/comedi_isadma.c +++ b/drivers/staging/comedi/drivers/comedi_isadma.c @@ -91,9 +91,10 @@ unsigned int comedi_isadma_disable_on_sample(unsigned int dma_chan, stalled++; if (stalled > 10) break; + } else { + residue = new_residue; + stalled = 0; } - residue = new_residue; - stalled = 0; } return residue; } diff --git a/drivers/staging/comedi/drivers/vmk80xx.c b/drivers/staging/comedi/drivers/vmk80xx.c index e371183..a090668 100644 --- a/drivers/staging/comedi/drivers/vmk80xx.c +++ b/drivers/staging/comedi/drivers/vmk80xx.c @@ -103,11 +103,6 @@ enum vmk80xx_model { VMK8061_MODEL }; -struct firmware_version { - unsigned char ic3_vers[32]; /* USB-Controller */ - unsigned char ic6_vers[32]; /* CPU */ -}; - static const struct comedi_lrange vmk8061_range = { 2, { UNI_RANGE(5), @@ -156,68 +151,12 @@ static const struct vmk80xx_board vmk80xx_boardinfo[] = { struct vmk80xx_private { struct usb_endpoint_descriptor *ep_rx; struct usb_endpoint_descriptor *ep_tx; - struct firmware_version fw; struct semaphore limit_sem; unsigned char *usb_rx_buf; unsigned char *usb_tx_buf; enum vmk80xx_model model; }; -static int vmk80xx_check_data_link(struct comedi_device *dev) -{ - struct vmk80xx_private *devpriv = dev->private; - struct usb_device *usb = comedi_to_usb_dev(dev); - unsigned int tx_pipe; - unsigned int rx_pipe; - unsigned char tx[1]; - unsigned char rx[2]; - - tx_pipe = usb_sndbulkpipe(usb, 0x01); - rx_pipe = usb_rcvbulkpipe(usb, 0x81); - - tx[0] = VMK8061_CMD_RD_PWR_STAT; - - /* - * Check that IC6 (PIC16F871) is powered and - * running and the data link between IC3 and - * IC6 is working properly - */ - usb_bulk_msg(usb, tx_pipe, tx, 1, NULL, devpriv->ep_tx->bInterval); - usb_bulk_msg(usb, rx_pipe, rx, 2, NULL, HZ * 10); - - return (int)rx[1]; -} - -static void vmk80xx_read_eeprom(struct comedi_device *dev, int flag) -{ - struct vmk80xx_private *devpriv = dev->private; - struct usb_device *usb = comedi_to_usb_dev(dev); - unsigned int tx_pipe; - unsigned int rx_pipe; - unsigned char tx[1]; - unsigned char rx[64]; - int cnt; - - tx_pipe = usb_sndbulkpipe(usb, 0x01); - rx_pipe = usb_rcvbulkpipe(usb, 0x81); - - tx[0] = VMK8061_CMD_RD_VERSION; - - /* - * Read the firmware version info of IC3 and - * IC6 from the internal EEPROM of the IC - */ - usb_bulk_msg(usb, tx_pipe, tx, 1, NULL, devpriv->ep_tx->bInterval); - usb_bulk_msg(usb, rx_pipe, rx, 64, &cnt, HZ * 10); - - rx[cnt] = '\0'; - - if (flag & IC3_VERSION) - strncpy(devpriv->fw.ic3_vers, rx + 1, 24); - else /* IC6_VERSION */ - strncpy(devpriv->fw.ic6_vers, rx + 25, 24); -} - static void vmk80xx_do_bulk_msg(struct comedi_device *dev) { struct vmk80xx_private *devpriv = dev->private; @@ -878,16 +817,6 @@ static int vmk80xx_auto_attach(struct comedi_device *dev, usb_set_intfdata(intf, devpriv); - if (devpriv->model == VMK8061_MODEL) { - vmk80xx_read_eeprom(dev, IC3_VERSION); - dev_info(&intf->dev, "%s\n", devpriv->fw.ic3_vers); - - if (vmk80xx_check_data_link(dev)) { - vmk80xx_read_eeprom(dev, IC6_VERSION); - dev_info(&intf->dev, "%s\n", devpriv->fw.ic6_vers); - } - } - if (devpriv->model == VMK8055_MODEL) vmk80xx_reset_device(dev); diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index 2418302..6d5b38d 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -38,6 +38,7 @@ config IIO_SIMPLE_DUMMY_EVENTS config IIO_SIMPLE_DUMMY_BUFFER bool "Buffered capture support" select IIO_BUFFER + select IIO_TRIGGER select IIO_KFIFO_BUF help Add buffered data capture to the simple dummy driver. diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index d9d6fad..8161743 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -214,11 +214,17 @@ struct mxs_lradc { unsigned long is_divided; /* - * Touchscreen LRADC channels receives a private slot in the CTRL4 - * register, the slot #7. Therefore only 7 slots instead of 8 in the - * CTRL4 register can be mapped to LRADC channels when using the - * touchscreen. - * + * When the touchscreen is enabled, we give it two private virtual + * channels: #6 and #7. This means that only 6 virtual channels (instead + * of 8) will be available for buffered capture. + */ +#define TOUCHSCREEN_VCHANNEL1 7 +#define TOUCHSCREEN_VCHANNEL2 6 +#define BUFFER_VCHANS_LIMITED 0x3f +#define BUFFER_VCHANS_ALL 0xff + u8 buffer_vchans; + + /* * Furthermore, certain LRADC channels are shared between touchscreen * and/or touch-buttons and generic LRADC block. Therefore when using * either of these, these channels are not available for the regular @@ -342,6 +348,9 @@ struct mxs_lradc { #define LRADC_CTRL4 0x140 #define LRADC_CTRL4_LRADCSELECT_MASK(n) (0xf << ((n) * 4)) #define LRADC_CTRL4_LRADCSELECT_OFFSET(n) ((n) * 4) +#define LRADC_CTRL4_LRADCSELECT(n, x) \ + (((x) << LRADC_CTRL4_LRADCSELECT_OFFSET(n)) & \ + LRADC_CTRL4_LRADCSELECT_MASK(n)) #define LRADC_RESOLUTION 12 #define LRADC_SINGLE_SAMPLE_MASK ((1 << LRADC_RESOLUTION) - 1) @@ -416,6 +425,14 @@ static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc) LRADC_STATUS_TOUCH_DETECT_RAW); } +static void mxs_lradc_map_channel(struct mxs_lradc *lradc, unsigned vch, + unsigned ch) +{ + mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch), + LRADC_CTRL4); + mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4); +} + static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch) { /* @@ -450,12 +467,8 @@ static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch) LRADC_DELAY_DELAY(lradc->over_sample_delay - 1), LRADC_DELAY(3)); - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(2) | - LRADC_CTRL1_LRADC_IRQ(3) | LRADC_CTRL1_LRADC_IRQ(4) | - LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1); + mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1); - /* wake us again, when the complete conversion is done */ - mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(ch), LRADC_CTRL1); /* * after changing the touchscreen plates setting * the signals need some initial time to settle. Start the @@ -509,12 +522,8 @@ static void mxs_lradc_setup_ts_pressure(struct mxs_lradc *lradc, unsigned ch1, LRADC_DELAY_DELAY(lradc->over_sample_delay - 1), LRADC_DELAY(3)); - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(2) | - LRADC_CTRL1_LRADC_IRQ(3) | LRADC_CTRL1_LRADC_IRQ(4) | - LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1); + mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1); - /* wake us again, when the conversions are done */ - mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(ch2), LRADC_CTRL1); /* * after changing the touchscreen plates setting * the signals need some initial time to settle. Start the @@ -580,36 +589,6 @@ static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc *lradc, #define TS_CH_XM 4 #define TS_CH_YM 5 -static int mxs_lradc_read_ts_channel(struct mxs_lradc *lradc) -{ - u32 reg; - int val; - - reg = readl(lradc->base + LRADC_CTRL1); - - /* only channels 3 to 5 are of interest here */ - if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_YP)) { - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_YP) | - LRADC_CTRL1_LRADC_IRQ(TS_CH_YP), LRADC_CTRL1); - val = mxs_lradc_read_raw_channel(lradc, TS_CH_YP); - } else if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_XM)) { - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_XM) | - LRADC_CTRL1_LRADC_IRQ(TS_CH_XM), LRADC_CTRL1); - val = mxs_lradc_read_raw_channel(lradc, TS_CH_XM); - } else if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_YM)) { - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_YM) | - LRADC_CTRL1_LRADC_IRQ(TS_CH_YM), LRADC_CTRL1); - val = mxs_lradc_read_raw_channel(lradc, TS_CH_YM); - } else { - return -EIO; - } - - mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2)); - mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3)); - - return val; -} - /* * YP(open)--+-------------+ * | |--+ @@ -653,7 +632,8 @@ static void mxs_lradc_prepare_x_pos(struct mxs_lradc *lradc) mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0); lradc->cur_plate = LRADC_SAMPLE_X; - mxs_lradc_setup_ts_channel(lradc, TS_CH_YP); + mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP); + mxs_lradc_setup_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1); } /* @@ -674,7 +654,8 @@ static void mxs_lradc_prepare_y_pos(struct mxs_lradc *lradc) mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0); lradc->cur_plate = LRADC_SAMPLE_Y; - mxs_lradc_setup_ts_channel(lradc, TS_CH_XM); + mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM); + mxs_lradc_setup_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1); } /* @@ -695,7 +676,10 @@ static void mxs_lradc_prepare_pressure(struct mxs_lradc *lradc) mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0); lradc->cur_plate = LRADC_SAMPLE_PRESSURE; - mxs_lradc_setup_ts_pressure(lradc, TS_CH_XP, TS_CH_YM); + mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM); + mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP); + mxs_lradc_setup_ts_pressure(lradc, TOUCHSCREEN_VCHANNEL2, + TOUCHSCREEN_VCHANNEL1); } static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc) @@ -708,6 +692,19 @@ static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc) mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1); } +static void mxs_lradc_start_touch_event(struct mxs_lradc *lradc) +{ + mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, + LRADC_CTRL1); + mxs_lradc_reg_set(lradc, + LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1), LRADC_CTRL1); + /* + * start with the Y-pos, because it uses nearly the same plate + * settings like the touch detection + */ + mxs_lradc_prepare_y_pos(lradc); +} + static void mxs_lradc_report_ts_event(struct mxs_lradc *lradc) { input_report_abs(lradc->ts_input, ABS_X, lradc->ts_x_pos); @@ -725,10 +722,12 @@ static void mxs_lradc_complete_touch_event(struct mxs_lradc *lradc) * start a dummy conversion to burn time to settle the signals * note: we are not interested in the conversion's value */ - mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(5)); - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1); - mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(5), LRADC_CTRL1); - mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << 5) | + mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1)); + mxs_lradc_reg_clear(lradc, + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1); + mxs_lradc_reg_wrt(lradc, + LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) | LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */ LRADC_DELAY(2)); } @@ -760,59 +759,45 @@ static void mxs_lradc_finish_touch_event(struct mxs_lradc *lradc, bool valid) /* if it is released, wait for the next touch via IRQ */ lradc->cur_plate = LRADC_TOUCH; - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ, LRADC_CTRL1); + mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2)); + mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3)); + mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ | + LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) | + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1), LRADC_CTRL1); mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1); } /* touchscreen's state machine */ static void mxs_lradc_handle_touch(struct mxs_lradc *lradc) { - int val; - switch (lradc->cur_plate) { case LRADC_TOUCH: - /* - * start with the Y-pos, because it uses nearly the same plate - * settings like the touch detection - */ - if (mxs_lradc_check_touch_event(lradc)) { - mxs_lradc_reg_clear(lradc, - LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, - LRADC_CTRL1); - mxs_lradc_prepare_y_pos(lradc); - } + if (mxs_lradc_check_touch_event(lradc)) + mxs_lradc_start_touch_event(lradc); mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ, LRADC_CTRL1); return; case LRADC_SAMPLE_Y: - val = mxs_lradc_read_ts_channel(lradc); - if (val < 0) { - mxs_lradc_enable_touch_detection(lradc); /* re-start */ - return; - } - lradc->ts_y_pos = val; + lradc->ts_y_pos = mxs_lradc_read_raw_channel(lradc, + TOUCHSCREEN_VCHANNEL1); mxs_lradc_prepare_x_pos(lradc); return; case LRADC_SAMPLE_X: - val = mxs_lradc_read_ts_channel(lradc); - if (val < 0) { - mxs_lradc_enable_touch_detection(lradc); /* re-start */ - return; - } - lradc->ts_x_pos = val; + lradc->ts_x_pos = mxs_lradc_read_raw_channel(lradc, + TOUCHSCREEN_VCHANNEL1); mxs_lradc_prepare_pressure(lradc); return; case LRADC_SAMPLE_PRESSURE: - lradc->ts_pressure = - mxs_lradc_read_ts_pressure(lradc, TS_CH_XP, TS_CH_YM); + lradc->ts_pressure = mxs_lradc_read_ts_pressure(lradc, + TOUCHSCREEN_VCHANNEL2, + TOUCHSCREEN_VCHANNEL1); mxs_lradc_complete_touch_event(lradc); return; case LRADC_SAMPLE_VALID: - val = mxs_lradc_read_ts_channel(lradc); /* ignore the value */ mxs_lradc_finish_touch_event(lradc, 1); break; } @@ -844,9 +829,9 @@ static int mxs_lradc_read_single(struct iio_dev *iio_dev, int chan, int *val) * used if doing raw sampling. */ if (lradc->soc == IMX28_LRADC) - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK, + mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1); - mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0); + mxs_lradc_reg_clear(lradc, 0x1, LRADC_CTRL0); /* Enable / disable the divider per requirement */ if (test_bit(chan, &lradc->is_divided)) @@ -1090,9 +1075,8 @@ static void mxs_lradc_disable_ts(struct mxs_lradc *lradc) { /* stop all interrupts from firing */ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN | - LRADC_CTRL1_LRADC_IRQ_EN(2) | LRADC_CTRL1_LRADC_IRQ_EN(3) | - LRADC_CTRL1_LRADC_IRQ_EN(4) | LRADC_CTRL1_LRADC_IRQ_EN(5), - LRADC_CTRL1); + LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) | + LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1); /* Power-down touchscreen touch-detect circuitry. */ mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); @@ -1158,26 +1142,31 @@ static irqreturn_t mxs_lradc_handle_irq(int irq, void *data) struct iio_dev *iio = data; struct mxs_lradc *lradc = iio_priv(iio); unsigned long reg = readl(lradc->base + LRADC_CTRL1); + uint32_t clr_irq = mxs_lradc_irq_mask(lradc); const uint32_t ts_irq_mask = LRADC_CTRL1_TOUCH_DETECT_IRQ | - LRADC_CTRL1_LRADC_IRQ(2) | - LRADC_CTRL1_LRADC_IRQ(3) | - LRADC_CTRL1_LRADC_IRQ(4) | - LRADC_CTRL1_LRADC_IRQ(5); + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2); if (!(reg & mxs_lradc_irq_mask(lradc))) return IRQ_NONE; - if (lradc->use_touchscreen && (reg & ts_irq_mask)) + if (lradc->use_touchscreen && (reg & ts_irq_mask)) { mxs_lradc_handle_touch(lradc); - if (iio_buffer_enabled(iio)) - iio_trigger_poll(iio->trig); - else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) + /* Make sure we don't clear the next conversion's interrupt. */ + clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | + LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2)); + } + + if (iio_buffer_enabled(iio)) { + if (reg & lradc->buffer_vchans) + iio_trigger_poll(iio->trig); + } else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) { complete(&lradc->completion); + } - mxs_lradc_reg_clear(lradc, reg & mxs_lradc_irq_mask(lradc), - LRADC_CTRL1); + mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1); return IRQ_HANDLED; } @@ -1289,9 +1278,10 @@ static int mxs_lradc_buffer_preenable(struct iio_dev *iio) } if (lradc->soc == IMX28_LRADC) - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK, - LRADC_CTRL1); - mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0); + mxs_lradc_reg_clear(lradc, + lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, + LRADC_CTRL1); + mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0); for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) { ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs); @@ -1324,10 +1314,11 @@ static int mxs_lradc_buffer_postdisable(struct iio_dev *iio) mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK, LRADC_DELAY(0)); - mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0); + mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0); if (lradc->soc == IMX28_LRADC) - mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK, - LRADC_CTRL1); + mxs_lradc_reg_clear(lradc, + lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, + LRADC_CTRL1); kfree(lradc->buffer); mutex_unlock(&lradc->lock); @@ -1353,7 +1344,7 @@ static bool mxs_lradc_validate_scan_mask(struct iio_dev *iio, if (lradc->use_touchbutton) rsvd_chans++; if (lradc->use_touchscreen) - rsvd_chans++; + rsvd_chans += 2; /* Test for attempts to map channels with special mode of operation. */ if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS)) @@ -1413,6 +1404,13 @@ static const struct iio_chan_spec mxs_lradc_chan_spec[] = { .channel = 8, .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,}, }, + /* Hidden channel to keep indexes */ + { + .type = IIO_TEMP, + .indexed = 1, + .scan_index = -1, + .channel = 9, + }, MXS_ADC_CHAN(10, IIO_VOLTAGE), /* VDDIO */ MXS_ADC_CHAN(11, IIO_VOLTAGE), /* VTH */ MXS_ADC_CHAN(12, IIO_VOLTAGE), /* VDDA */ @@ -1583,6 +1581,11 @@ static int mxs_lradc_probe(struct platform_device *pdev) touch_ret = mxs_lradc_probe_touchscreen(lradc, node); + if (touch_ret == 0) + lradc->buffer_vchans = BUFFER_VCHANS_LIMITED; + else + lradc->buffer_vchans = BUFFER_VCHANS_ALL; + /* Grab all IRQ sources */ for (i = 0; i < of_cfg->irq_count; i++) { lradc->irq[i] = platform_get_irq(pdev, i); diff --git a/drivers/staging/iio/magnetometer/hmc5843_core.c b/drivers/staging/iio/magnetometer/hmc5843_core.c index fd171d8..90cc18b 100644 --- a/drivers/staging/iio/magnetometer/hmc5843_core.c +++ b/drivers/staging/iio/magnetometer/hmc5843_core.c @@ -592,6 +592,7 @@ int hmc5843_common_probe(struct device *dev, struct regmap *regmap, mutex_init(&data->lock); indio_dev->dev.parent = dev; + indio_dev->name = dev->driver->name; indio_dev->info = &hmc5843_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = data->variant->channels; diff --git a/drivers/staging/iio/resolver/ad2s1200.c b/drivers/staging/iio/resolver/ad2s1200.c index 017d2f8..c17893b 100644 --- a/drivers/staging/iio/resolver/ad2s1200.c +++ b/drivers/staging/iio/resolver/ad2s1200.c @@ -18,6 +18,7 @@ #include <linux/delay.h> #include <linux/gpio.h> #include <linux/module.h> +#include <linux/bitops.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -68,7 +69,7 @@ static int ad2s1200_read_raw(struct iio_dev *indio_dev, break; case IIO_ANGL_VEL: vel = (((s16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4); - vel = (vel << 4) >> 4; + vel = sign_extend32(vel, 11); *val = vel; break; default: diff --git a/drivers/staging/nvec/nvec_power.c b/drivers/staging/nvec/nvec_power.c index 6a1459d..30b66c3 100644 --- a/drivers/staging/nvec/nvec_power.c +++ b/drivers/staging/nvec/nvec_power.c @@ -82,8 +82,8 @@ struct bat_response { }; }; -static struct power_supply nvec_bat_psy; -static struct power_supply nvec_psy; +static struct power_supply *nvec_bat_psy; +static struct power_supply *nvec_psy; static int nvec_power_notifier(struct notifier_block *nb, unsigned long event_type, void *data) @@ -98,7 +98,7 @@ static int nvec_power_notifier(struct notifier_block *nb, if (res->sub_type == 0) { if (power->on != res->plu) { power->on = res->plu; - power_supply_changed(&nvec_psy); + power_supply_changed(nvec_psy); } return NOTIFY_STOP; } @@ -167,7 +167,7 @@ static int nvec_power_bat_notifier(struct notifier_block *nb, } power->bat_cap = res->plc[1]; if (status_changed) - power_supply_changed(&nvec_bat_psy); + power_supply_changed(nvec_bat_psy); break; case VOLTAGE: power->bat_voltage_now = res->plu * 1000; @@ -225,7 +225,7 @@ static int nvec_power_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct nvec_power *power = dev_get_drvdata(psy->dev->parent); + struct nvec_power *power = dev_get_drvdata(psy->dev.parent); switch (psp) { case POWER_SUPPLY_PROP_ONLINE: @@ -241,7 +241,7 @@ static int nvec_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct nvec_power *power = dev_get_drvdata(psy->dev->parent); + struct nvec_power *power = dev_get_drvdata(psy->dev.parent); switch (psp) { case POWER_SUPPLY_PROP_STATUS: @@ -323,7 +323,7 @@ static char *nvec_power_supplied_to[] = { "battery", }; -static struct power_supply nvec_bat_psy = { +static const struct power_supply_desc nvec_bat_psy_desc = { .name = "battery", .type = POWER_SUPPLY_TYPE_BATTERY, .properties = nvec_battery_props, @@ -331,11 +331,9 @@ static struct power_supply nvec_bat_psy = { .get_property = nvec_battery_get_property, }; -static struct power_supply nvec_psy = { +static const struct power_supply_desc nvec_psy_desc = { .name = "ac", .type = POWER_SUPPLY_TYPE_MAINS, - .supplied_to = nvec_power_supplied_to, - .num_supplicants = ARRAY_SIZE(nvec_power_supplied_to), .properties = nvec_power_props, .num_properties = ARRAY_SIZE(nvec_power_props), .get_property = nvec_power_get_property, @@ -373,9 +371,11 @@ static void nvec_power_poll(struct work_struct *work) static int nvec_power_probe(struct platform_device *pdev) { - struct power_supply *psy; + struct power_supply **psy; + const struct power_supply_desc *psy_desc; struct nvec_power *power; struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent); + struct power_supply_config psy_cfg = {}; power = devm_kzalloc(&pdev->dev, sizeof(struct nvec_power), GFP_NOWAIT); if (power == NULL) @@ -387,6 +387,9 @@ static int nvec_power_probe(struct platform_device *pdev) switch (pdev->id) { case AC: psy = &nvec_psy; + psy_desc = &nvec_psy_desc; + psy_cfg.supplied_to = nvec_power_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(nvec_power_supplied_to); power->notifier.notifier_call = nvec_power_notifier; @@ -395,6 +398,7 @@ static int nvec_power_probe(struct platform_device *pdev) break; case BAT: psy = &nvec_bat_psy; + psy_desc = &nvec_bat_psy_desc; power->notifier.notifier_call = nvec_power_bat_notifier; break; @@ -407,7 +411,9 @@ static int nvec_power_probe(struct platform_device *pdev) if (pdev->id == BAT) get_bat_mfg_data(power); - return power_supply_register(&pdev->dev, psy); + *psy = power_supply_register(&pdev->dev, psy_desc, &psy_cfg); + + return PTR_ERR_OR_ZERO(*psy); } static int nvec_power_remove(struct platform_device *pdev) @@ -418,10 +424,10 @@ static int nvec_power_remove(struct platform_device *pdev) nvec_unregister_notifier(power->nvec, &power->notifier); switch (pdev->id) { case AC: - power_supply_unregister(&nvec_psy); + power_supply_unregister(nvec_psy); break; case BAT: - power_supply_unregister(&nvec_bat_psy); + power_supply_unregister(nvec_bat_psy); } return 0; diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c index 4324282a..03b2a90 100644 --- a/drivers/staging/vt6655/device_main.c +++ b/drivers/staging/vt6655/device_main.c @@ -330,16 +330,6 @@ static void device_init_registers(struct vnt_private *pDevice) /* zonetype initial */ pDevice->byOriginalZonetype = pDevice->abyEEPROM[EEP_OFS_ZONETYPE]; - /* Get RFType */ - pDevice->byRFType = SROMbyReadEmbedded(pDevice->PortOffset, EEP_OFS_RFTYPE); - - /* force change RevID for VT3253 emu */ - if ((pDevice->byRFType & RF_EMU) != 0) - pDevice->byRevId = 0x80; - - pDevice->byRFType &= RF_MASK; - pr_debug("pDevice->byRFType = %x\n", pDevice->byRFType); - if (!pDevice->bZoneRegExist) pDevice->byZoneType = pDevice->abyEEPROM[EEP_OFS_ZONETYPE]; @@ -1187,12 +1177,14 @@ static int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; PSTxDesc head_td; - u32 dma_idx = TYPE_AC0DMA; + u32 dma_idx; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); - if (!ieee80211_is_data(hdr->frame_control)) + if (ieee80211_is_data(hdr->frame_control)) + dma_idx = TYPE_AC0DMA; + else dma_idx = TYPE_TXDMA0; if (AVAIL_TD(priv, dma_idx) < 1) { @@ -1206,6 +1198,9 @@ static int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb) head_td->pTDInfo->skb = skb; + if (dma_idx == TYPE_AC0DMA) + head_td->pTDInfo->byFlags = TD_FLAGS_NETIF_SKB; + priv->iTDUsed[dma_idx]++; /* Take ownership */ @@ -1234,13 +1229,10 @@ static int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb) head_td->buff_addr = cpu_to_le32(head_td->pTDInfo->skb_dma); - if (dma_idx == TYPE_AC0DMA) { - head_td->pTDInfo->byFlags = TD_FLAGS_NETIF_SKB; - + if (head_td->pTDInfo->byFlags & TD_FLAGS_NETIF_SKB) MACvTransmitAC0(priv->PortOffset); - } else { + else MACvTransmit0(priv->PortOffset); - } spin_unlock_irqrestore(&priv->lock, flags); @@ -1778,6 +1770,12 @@ vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent) MACvInitialize(priv->PortOffset); MACvReadEtherAddress(priv->PortOffset, priv->abyCurrentNetAddr); + /* Get RFType */ + priv->byRFType = SROMbyReadEmbedded(priv->PortOffset, EEP_OFS_RFTYPE); + priv->byRFType &= RF_MASK; + + dev_dbg(&pcid->dev, "RF Type = %x\n", priv->byRFType); + device_get_options(priv); device_set_options(priv); /* Mask out the options cannot be set to the chip */ diff --git a/drivers/staging/vt6655/rf.c b/drivers/staging/vt6655/rf.c index 941b2ad..7626f63 100644 --- a/drivers/staging/vt6655/rf.c +++ b/drivers/staging/vt6655/rf.c @@ -794,6 +794,7 @@ bool RFbSetPower( break; case RATE_6M: case RATE_9M: + case RATE_12M: case RATE_18M: byPwr = priv->abyOFDMPwrTbl[uCH]; if (priv->byRFType == RF_UW2452) diff --git a/drivers/staging/vt6656/rf.c b/drivers/staging/vt6656/rf.c index c42cde5..c4286cc 100644 --- a/drivers/staging/vt6656/rf.c +++ b/drivers/staging/vt6656/rf.c @@ -640,6 +640,7 @@ int vnt_rf_setpower(struct vnt_private *priv, u32 rate, u32 channel) break; case RATE_6M: case RATE_9M: + case RATE_12M: case RATE_18M: case RATE_24M: case RATE_36M: diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 50bad55..77d6425 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1181,7 +1181,7 @@ iscsit_handle_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, * traditional iSCSI block I/O. */ if (iscsit_allocate_iovecs(cmd) < 0) { - return iscsit_add_reject_cmd(cmd, + return iscsit_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); } immed_data = cmd->immediate_data; @@ -3468,6 +3468,7 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd, tpg_np_list) { struct iscsi_np *np = tpg_np->tpg_np; bool inaddr_any = iscsit_check_inaddr_any(np); + char *fmt_str; if (np->np_network_transport != network_transport) continue; @@ -3495,8 +3496,12 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd, } } - len = sprintf(buf, "TargetAddress=" - "%s:%hu,%hu", + if (np->np_sockaddr.ss_family == AF_INET6) + fmt_str = "TargetAddress=[%s]:%hu,%hu"; + else + fmt_str = "TargetAddress=%s:%hu,%hu"; + + len = sprintf(buf, fmt_str, inaddr_any ? conn->local_ip : np->np_ip, np->np_port, tpg->tpgt); @@ -4256,11 +4261,17 @@ int iscsit_close_connection( pr_debug("Closing iSCSI connection CID %hu on SID:" " %u\n", conn->cid, sess->sid); /* - * Always up conn_logout_comp just in case the RX Thread is sleeping - * and the logout response never got sent because the connection - * failed. + * Always up conn_logout_comp for the traditional TCP case just in case + * the RX Thread in iscsi_target_rx_opcode() is sleeping and the logout + * response never got sent because the connection failed. + * + * However for iser-target, isert_wait4logout() is using conn_logout_comp + * to signal logout response TX interrupt completion. Go ahead and skip + * this for iser since isert_rx_opcode() does not wait on logout failure, + * and to avoid iscsi_conn pointer dereference in iser-target code. */ - complete(&conn->conn_logout_comp); + if (conn->conn_transport->transport_type == ISCSI_TCP) + complete(&conn->conn_logout_comp); iscsi_release_thread_set(conn); diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c index 1c197ba..bdd8731 100644 --- a/drivers/target/iscsi/iscsi_target_erl0.c +++ b/drivers/target/iscsi/iscsi_target_erl0.c @@ -22,7 +22,6 @@ #include <target/target_core_fabric.h> #include <target/iscsi/iscsi_target_core.h> -#include <target/iscsi/iscsi_transport.h> #include "iscsi_target_seq_pdu_list.h" #include "iscsi_target_tq.h" #include "iscsi_target_erl0.h" @@ -940,8 +939,7 @@ void iscsit_take_action_for_connection_exit(struct iscsi_conn *conn) if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT) { spin_unlock_bh(&conn->state_lock); - if (conn->conn_transport->transport_type == ISCSI_TCP) - iscsit_close_connection(conn); + iscsit_close_connection(conn); return; } diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 6b3c329..c36bd7c 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -953,11 +953,8 @@ static int tcm_loop_make_nexus( transport_free_session(tl_nexus->se_sess); goto out; } - /* - * Now, register the SAS I_T Nexus as active with the call to - * transport_register_session() - */ - __transport_register_session(se_tpg, tl_nexus->se_sess->se_node_acl, + /* Now, register the SAS I_T Nexus as active. */ + transport_register_session(se_tpg, tl_nexus->se_sess->se_node_acl, tl_nexus->se_sess, tl_nexus); tl_tpg->tl_nexus = tl_nexus; pr_debug("TCM_Loop_ConfigFS: Established I_T Nexus to emulated" diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 58f49ff..7faa6ae 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -650,6 +650,18 @@ static u32 se_dev_align_max_sectors(u32 max_sectors, u32 block_size) return aligned_max_sectors; } +bool se_dev_check_wce(struct se_device *dev) +{ + bool wce = false; + + if (dev->transport->get_write_cache) + wce = dev->transport->get_write_cache(dev); + else if (dev->dev_attrib.emulate_write_cache > 0) + wce = true; + + return wce; +} + int se_dev_set_max_unmap_lba_count( struct se_device *dev, u32 max_unmap_lba_count) @@ -767,6 +779,16 @@ int se_dev_set_emulate_fua_write(struct se_device *dev, int flag) pr_err("Illegal value %d\n", flag); return -EINVAL; } + if (flag && + dev->transport->get_write_cache) { + pr_warn("emulate_fua_write not supported for this device, ignoring\n"); + return 0; + } + if (dev->export_count) { + pr_err("emulate_fua_write cannot be changed with active" + " exports: %d\n", dev->export_count); + return -EINVAL; + } dev->dev_attrib.emulate_fua_write = flag; pr_debug("dev[%p]: SE Device Forced Unit Access WRITEs: %d\n", dev, dev->dev_attrib.emulate_fua_write); @@ -801,7 +823,11 @@ int se_dev_set_emulate_write_cache(struct se_device *dev, int flag) pr_err("emulate_write_cache not supported for this device\n"); return -EINVAL; } - + if (dev->export_count) { + pr_err("emulate_write_cache cannot be changed with active" + " exports: %d\n", dev->export_count); + return -EINVAL; + } dev->dev_attrib.emulate_write_cache = flag; pr_debug("dev[%p]: SE Device WRITE_CACHE_EMULATION flag: %d\n", dev, dev->dev_attrib.emulate_write_cache); @@ -1534,8 +1560,6 @@ int target_configure_device(struct se_device *dev) ret = dev->transport->configure_device(dev); if (ret) goto out; - dev->dev_flags |= DF_CONFIGURED; - /* * XXX: there is not much point to have two different values here.. */ @@ -1597,6 +1621,8 @@ int target_configure_device(struct se_device *dev) list_add_tail(&dev->g_dev_node, &g_device_list); mutex_unlock(&g_device_mutex); + dev->dev_flags |= DF_CONFIGURED; + return 0; out_free_alua: diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 1045dcd..f6c954c 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -1121,7 +1121,7 @@ static u32 pscsi_get_device_type(struct se_device *dev) struct pscsi_dev_virt *pdv = PSCSI_DEV(dev); struct scsi_device *sd = pdv->pdv_sd; - return sd->type; + return (sd) ? sd->type : TYPE_NO_LUN; } static sector_t pscsi_get_blocks(struct se_device *dev) diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 9a2f9d3..3e72974 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -708,8 +708,7 @@ sbc_check_dpofua(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb) } } if (cdb[1] & 0x8) { - if (!dev->dev_attrib.emulate_fua_write || - !dev->dev_attrib.emulate_write_cache) { + if (!dev->dev_attrib.emulate_fua_write || !se_dev_check_wce(dev)) { pr_err("Got CDB: 0x%02x with FUA bit set, but device" " does not advertise support for FUA write\n", cdb[0]); diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 460e931..6c8bd6b 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -454,19 +454,6 @@ check_scsi_name: } EXPORT_SYMBOL(spc_emulate_evpd_83); -static bool -spc_check_dev_wce(struct se_device *dev) -{ - bool wce = false; - - if (dev->transport->get_write_cache) - wce = dev->transport->get_write_cache(dev); - else if (dev->dev_attrib.emulate_write_cache > 0) - wce = true; - - return wce; -} - /* Extended INQUIRY Data VPD Page */ static sense_reason_t spc_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf) @@ -490,7 +477,7 @@ spc_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf) buf[5] = 0x07; /* If WriteCache emulation is enabled, set V_SUP */ - if (spc_check_dev_wce(dev)) + if (se_dev_check_wce(dev)) buf[6] = 0x01; /* If an LBA map is present set R_SUP */ spin_lock(&cmd->se_dev->t10_alua.lba_map_lock); @@ -897,7 +884,7 @@ static int spc_modesense_caching(struct se_cmd *cmd, u8 pc, u8 *p) if (pc == 1) goto out; - if (spc_check_dev_wce(dev)) + if (se_dev_check_wce(dev)) p[2] = 0x04; /* Write Cache Enable */ p[12] = 0x20; /* Disabled Read Ahead */ @@ -1009,7 +996,7 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) (cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY))) spc_modesense_write_protect(&buf[length], type); - if ((spc_check_dev_wce(dev)) && + if ((se_dev_check_wce(dev)) && (dev->dev_attrib.emulate_fua_write > 0)) spc_modesense_dpofua(&buf[length], type); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 0adc0f6..ac3cbab 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -2389,6 +2389,10 @@ int target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd, list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list); out: spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); + + if (ret && ack_kref) + target_put_sess_cmd(se_sess, se_cmd); + return ret; } EXPORT_SYMBOL(target_get_sess_cmd); diff --git a/drivers/target/tcm_fc/tfc_io.c b/drivers/target/tcm_fc/tfc_io.c index 97b486c..583e755 100644 --- a/drivers/target/tcm_fc/tfc_io.c +++ b/drivers/target/tcm_fc/tfc_io.c @@ -359,7 +359,7 @@ void ft_invl_hw_context(struct ft_cmd *cmd) ep = fc_seq_exch(seq); if (ep) { lport = ep->lp; - if (lport && (ep->xid <= lport->lro_xid)) + if (lport && (ep->xid <= lport->lro_xid)) { /* * "ddp_done" trigger invalidation of HW * specific DDP context @@ -374,6 +374,7 @@ void ft_invl_hw_context(struct ft_cmd *cmd) * identified using ep->xid) */ cmd->was_ddp_setup = 0; + } } } } diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c index 25d244c..031018e 100644 --- a/drivers/thermal/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/int340x_thermal/int3400_thermal.c @@ -262,13 +262,12 @@ static int int3400_thermal_probe(struct platform_device *pdev) result = acpi_parse_art(priv->adev->handle, &priv->art_count, &priv->arts, true); if (result) - goto free_priv; - + dev_dbg(&pdev->dev, "_ART table parsing error\n"); result = acpi_parse_trt(priv->adev->handle, &priv->trt_count, &priv->trts, true); if (result) - goto free_art; + dev_dbg(&pdev->dev, "_TRT table parsing error\n"); platform_set_drvdata(pdev, priv); @@ -281,7 +280,7 @@ static int int3400_thermal_probe(struct platform_device *pdev) &int3400_thermal_params, 0, 0); if (IS_ERR(priv->thermal)) { result = PTR_ERR(priv->thermal); - goto free_trt; + goto free_art_trt; } priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add( @@ -295,9 +294,8 @@ static int int3400_thermal_probe(struct platform_device *pdev) free_zone: thermal_zone_device_unregister(priv->thermal); -free_trt: +free_art_trt: kfree(priv->trts); -free_art: kfree(priv->arts); free_priv: kfree(priv); diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c index f88b088..1e25133 100644 --- a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c +++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c @@ -208,7 +208,7 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev, trip_cnt, GFP_KERNEL); if (!int34x_thermal_zone->aux_trips) { ret = -ENOMEM; - goto free_mem; + goto err_trip_alloc; } trip_mask = BIT(trip_cnt) - 1; int34x_thermal_zone->aux_trip_nr = trip_cnt; @@ -248,14 +248,15 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev, 0, 0); if (IS_ERR(int34x_thermal_zone->zone)) { ret = PTR_ERR(int34x_thermal_zone->zone); - goto free_lpat; + goto err_thermal_zone; } return int34x_thermal_zone; -free_lpat: +err_thermal_zone: acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table); -free_mem: + kfree(int34x_thermal_zone->aux_trips); +err_trip_alloc: kfree(int34x_thermal_zone); return ERR_PTR(ret); } @@ -266,6 +267,7 @@ void int340x_thermal_zone_remove(struct int34x_thermal_zone { thermal_zone_device_unregister(int34x_thermal_zone->zone); acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table); + kfree(int34x_thermal_zone->aux_trips); kfree(int34x_thermal_zone); } EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove); diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c index 6ceebd6..12623bc 100644 --- a/drivers/thermal/intel_powerclamp.c +++ b/drivers/thermal/intel_powerclamp.c @@ -688,6 +688,7 @@ static const struct x86_cpu_id intel_powerclamp_ids[] = { { X86_VENDOR_INTEL, 6, 0x45}, { X86_VENDOR_INTEL, 6, 0x46}, { X86_VENDOR_INTEL, 6, 0x4c}, + { X86_VENDOR_INTEL, 6, 0x4d}, { X86_VENDOR_INTEL, 6, 0x56}, {} }; diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 2580a48..fe4e767 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -387,21 +387,9 @@ static int rcar_thermal_probe(struct platform_device *pdev) irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (irq) { - int ret; - /* * platform has IRQ support. * Then, driver uses common registers - */ - - ret = devm_request_irq(dev, irq->start, rcar_thermal_irq, 0, - dev_name(dev), common); - if (ret) { - dev_err(dev, "irq request failed\n "); - return ret; - } - - /* * rcar_has_irq_support() will be enabled */ res = platform_get_resource(pdev, IORESOURCE_MEM, mres++); @@ -456,8 +444,16 @@ static int rcar_thermal_probe(struct platform_device *pdev) } /* enable temperature comparation */ - if (irq) + if (irq) { + ret = devm_request_irq(dev, irq->start, rcar_thermal_irq, 0, + dev_name(dev), common); + if (ret) { + dev_err(dev, "irq request failed\n "); + goto error_unregister; + } + rcar_thermal_common_write(common, ENR, enr_bits); + } platform_set_drvdata(pdev, common); @@ -467,9 +463,9 @@ static int rcar_thermal_probe(struct platform_device *pdev) error_unregister: rcar_thermal_for_each_priv(priv, common) { - thermal_zone_device_unregister(priv->zone); if (rcar_has_irq_support(priv)) rcar_thermal_irq_disable(priv); + thermal_zone_device_unregister(priv->zone); } pm_runtime_put(dev); @@ -485,9 +481,9 @@ static int rcar_thermal_remove(struct platform_device *pdev) struct rcar_thermal_priv *priv; rcar_thermal_for_each_priv(priv, common) { - thermal_zone_device_unregister(priv->zone); if (rcar_has_irq_support(priv)) rcar_thermal_irq_disable(priv); + thermal_zone_device_unregister(priv->zone); } pm_runtime_put(dev); diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 933cd80..1d30b09 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -682,6 +682,7 @@ static void exynos7_tmu_control(struct platform_device *pdev, bool on) if (on) { con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); + con |= (1 << EXYNOS7_PD_DET_EN_SHIFT); interrupt_en = (of_thermal_is_trip_valid(tz, 7) << EXYNOS7_TMU_INTEN_RISE7_SHIFT) | @@ -704,9 +705,9 @@ static void exynos7_tmu_control(struct platform_device *pdev, bool on) interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; } else { con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); + con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT); interrupt_en = 0; /* Disable all interrupts */ } - con |= 1 << EXYNOS7_PD_DET_EN_SHIFT; writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); writel(con, data->base + EXYNOS_TMU_REG_CONTROL); @@ -918,34 +919,16 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id) } static const struct of_device_id exynos_tmu_match[] = { - { - .compatible = "samsung,exynos3250-tmu", - }, - { - .compatible = "samsung,exynos4210-tmu", - }, - { - .compatible = "samsung,exynos4412-tmu", - }, - { - .compatible = "samsung,exynos5250-tmu", - }, - { - .compatible = "samsung,exynos5260-tmu", - }, - { - .compatible = "samsung,exynos5420-tmu", - }, - { - .compatible = "samsung,exynos5420-tmu-ext-triminfo", - }, - { - .compatible = "samsung,exynos5440-tmu", - }, - { - .compatible = "samsung,exynos7-tmu", - }, - {}, + { .compatible = "samsung,exynos3250-tmu", }, + { .compatible = "samsung,exynos4210-tmu", }, + { .compatible = "samsung,exynos4412-tmu", }, + { .compatible = "samsung,exynos5250-tmu", }, + { .compatible = "samsung,exynos5260-tmu", }, + { .compatible = "samsung,exynos5420-tmu", }, + { .compatible = "samsung,exynos5420-tmu-ext-triminfo", }, + { .compatible = "samsung,exynos5440-tmu", }, + { .compatible = "samsung,exynos7-tmu", }, + { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, exynos_tmu_match); diff --git a/drivers/thermal/st/st_thermal.c b/drivers/thermal/st/st_thermal.c index d1ec580..76c515d 100644 --- a/drivers/thermal/st/st_thermal.c +++ b/drivers/thermal/st/st_thermal.c @@ -25,7 +25,7 @@ * Function to allocate regfields which are common * between syscfg and memory mapped based sensors */ -int st_thermal_alloc_regfields(struct st_thermal_sensor *sensor) +static int st_thermal_alloc_regfields(struct st_thermal_sensor *sensor) { struct device *dev = sensor->dev; struct regmap *regmap = sensor->regmap; diff --git a/drivers/thermal/st/st_thermal_memmap.c b/drivers/thermal/st/st_thermal_memmap.c index 067bfcd..fc0c9e1 100644 --- a/drivers/thermal/st/st_thermal_memmap.c +++ b/drivers/thermal/st/st_thermal_memmap.c @@ -157,7 +157,7 @@ static const struct st_thermal_sensor_ops st_mmap_sensor_ops = { }; /* Compatible device data stih416 mpe thermal sensor */ -const struct st_thermal_compat_data st_416mpe_cdata = { +static const struct st_thermal_compat_data st_416mpe_cdata = { .reg_fields = st_mmap_thermal_regfields, .ops = &st_mmap_sensor_ops, .calibration_val = 14, @@ -166,7 +166,7 @@ const struct st_thermal_compat_data st_416mpe_cdata = { }; /* Compatible device data stih407 thermal sensor */ -const struct st_thermal_compat_data st_407_cdata = { +static const struct st_thermal_compat_data st_407_cdata = { .reg_fields = st_mmap_thermal_regfields, .ops = &st_mmap_sensor_ops, .calibration_val = 16, @@ -174,19 +174,19 @@ const struct st_thermal_compat_data st_407_cdata = { .crit_temp = 120, }; -static struct of_device_id st_mmap_thermal_of_match[] = { +static const struct of_device_id st_mmap_thermal_of_match[] = { { .compatible = "st,stih416-mpe-thermal", .data = &st_416mpe_cdata }, { .compatible = "st,stih407-thermal", .data = &st_407_cdata }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, st_mmap_thermal_of_match); -int st_mmap_probe(struct platform_device *pdev) +static int st_mmap_probe(struct platform_device *pdev) { return st_thermal_register(pdev, st_mmap_thermal_of_match); } -int st_mmap_remove(struct platform_device *pdev) +static int st_mmap_remove(struct platform_device *pdev) { return st_thermal_unregister(pdev); } diff --git a/drivers/thermal/st/st_thermal_syscfg.c b/drivers/thermal/st/st_thermal_syscfg.c index 26d36a2..3df5b789 100644 --- a/drivers/thermal/st/st_thermal_syscfg.c +++ b/drivers/thermal/st/st_thermal_syscfg.c @@ -104,7 +104,7 @@ static const struct st_thermal_sensor_ops st_syscfg_sensor_ops = { }; /* Compatible device data for stih415 sas thermal sensor */ -const struct st_thermal_compat_data st_415sas_cdata = { +static const struct st_thermal_compat_data st_415sas_cdata = { .sys_compat = "st,stih415-front-syscfg", .reg_fields = st_415sas_regfields, .ops = &st_syscfg_sensor_ops, @@ -114,7 +114,7 @@ const struct st_thermal_compat_data st_415sas_cdata = { }; /* Compatible device data for stih415 mpe thermal sensor */ -const struct st_thermal_compat_data st_415mpe_cdata = { +static const struct st_thermal_compat_data st_415mpe_cdata = { .sys_compat = "st,stih415-system-syscfg", .reg_fields = st_415mpe_regfields, .ops = &st_syscfg_sensor_ops, @@ -124,7 +124,7 @@ const struct st_thermal_compat_data st_415mpe_cdata = { }; /* Compatible device data for stih416 sas thermal sensor */ -const struct st_thermal_compat_data st_416sas_cdata = { +static const struct st_thermal_compat_data st_416sas_cdata = { .sys_compat = "st,stih416-front-syscfg", .reg_fields = st_416sas_regfields, .ops = &st_syscfg_sensor_ops, @@ -134,7 +134,7 @@ const struct st_thermal_compat_data st_416sas_cdata = { }; /* Compatible device data for stid127 thermal sensor */ -const struct st_thermal_compat_data st_127_cdata = { +static const struct st_thermal_compat_data st_127_cdata = { .sys_compat = "st,stid127-cpu-syscfg", .reg_fields = st_127_regfields, .ops = &st_syscfg_sensor_ops, @@ -143,7 +143,7 @@ const struct st_thermal_compat_data st_127_cdata = { .crit_temp = 120, }; -static struct of_device_id st_syscfg_thermal_of_match[] = { +static const struct of_device_id st_syscfg_thermal_of_match[] = { { .compatible = "st,stih415-sas-thermal", .data = &st_415sas_cdata }, { .compatible = "st,stih415-mpe-thermal", .data = &st_415mpe_cdata }, { .compatible = "st,stih416-sas-thermal", .data = &st_416sas_cdata }, @@ -152,12 +152,12 @@ static struct of_device_id st_syscfg_thermal_of_match[] = { }; MODULE_DEVICE_TABLE(of, st_syscfg_thermal_of_match); -int st_syscfg_probe(struct platform_device *pdev) +static int st_syscfg_probe(struct platform_device *pdev) { return st_thermal_register(pdev, st_syscfg_thermal_of_match); } -int st_syscfg_remove(struct platform_device *pdev) +static int st_syscfg_remove(struct platform_device *pdev) { return st_thermal_unregister(pdev); } diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 48491d1..4108db7 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -458,8 +458,10 @@ static void update_temperature(struct thermal_zone_device *tz) ret = thermal_zone_get_temp(tz, &temp); if (ret) { - dev_warn(&tz->device, "failed to read out thermal zone %d\n", - tz->id); + if (ret != -EAGAIN) + dev_warn(&tz->device, + "failed to read out thermal zone (%d)\n", + ret); return; } @@ -899,6 +901,22 @@ thermal_cooling_device_trip_point_show(struct device *dev, return sprintf(buf, "%d\n", instance->trip); } +static struct attribute *cooling_device_attrs[] = { + &dev_attr_cdev_type.attr, + &dev_attr_max_state.attr, + &dev_attr_cur_state.attr, + NULL, +}; + +static const struct attribute_group cooling_device_attr_group = { + .attrs = cooling_device_attrs, +}; + +static const struct attribute_group *cooling_device_attr_groups[] = { + &cooling_device_attr_group, + NULL, +}; + /* Device management */ /** @@ -1130,6 +1148,7 @@ __thermal_cooling_device_register(struct device_node *np, cdev->ops = ops; cdev->updated = false; cdev->device.class = &thermal_class; + cdev->device.groups = cooling_device_attr_groups; cdev->devdata = devdata; dev_set_name(&cdev->device, "cooling_device%d", cdev->id); result = device_register(&cdev->device); @@ -1139,21 +1158,6 @@ __thermal_cooling_device_register(struct device_node *np, return ERR_PTR(result); } - /* sys I/F */ - if (type) { - result = device_create_file(&cdev->device, &dev_attr_cdev_type); - if (result) - goto unregister; - } - - result = device_create_file(&cdev->device, &dev_attr_max_state); - if (result) - goto unregister; - - result = device_create_file(&cdev->device, &dev_attr_cur_state); - if (result) - goto unregister; - /* Add 'this' new cdev to the global cdev list */ mutex_lock(&thermal_list_lock); list_add(&cdev->node, &thermal_cdev_list); @@ -1163,11 +1167,6 @@ __thermal_cooling_device_register(struct device_node *np, bind_cdev(cdev); return cdev; - -unregister: - release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); - device_unregister(&cdev->device); - return ERR_PTR(result); } /** diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.c b/drivers/thermal/ti-soc-thermal/ti-bandgap.c index 634b6ce..62a5d44 100644 --- a/drivers/thermal/ti-soc-thermal/ti-bandgap.c +++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.c @@ -1402,7 +1402,7 @@ int ti_bandgap_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ti_bandgap_save_ctxt(struct ti_bandgap *bgp) { int i; diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c index 3fb054a..a38c175 100644 --- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c @@ -429,7 +429,7 @@ int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id) data = ti_bandgap_get_sensor_data(bgp, id); - if (data && data->cool_dev) + if (data) cpufreq_cooling_unregister(data->cool_dev); return 0; diff --git a/drivers/tty/bfin_jtag_comm.c b/drivers/tty/bfin_jtag_comm.c index d7b198c..ce24182 100644 --- a/drivers/tty/bfin_jtag_comm.c +++ b/drivers/tty/bfin_jtag_comm.c @@ -210,18 +210,6 @@ bfin_jc_chars_in_buffer(struct tty_struct *tty) return circ_cnt(&bfin_jc_write_buf); } -static void -bfin_jc_wait_until_sent(struct tty_struct *tty, int timeout) -{ - unsigned long expire = jiffies + timeout; - while (!circ_empty(&bfin_jc_write_buf)) { - if (signal_pending(current)) - break; - if (time_after(jiffies, expire)) - break; - } -} - static const struct tty_operations bfin_jc_ops = { .open = bfin_jc_open, .close = bfin_jc_close, @@ -230,7 +218,6 @@ static const struct tty_operations bfin_jc_ops = { .flush_chars = bfin_jc_flush_chars, .write_room = bfin_jc_write_room, .chars_in_buffer = bfin_jc_chars_in_buffer, - .wait_until_sent = bfin_jc_wait_until_sent, }; static int __init bfin_jc_init(void) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index e3b9570a..deae122 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2138,8 +2138,8 @@ int serial8250_do_startup(struct uart_port *port) /* * Clear the interrupt registers. */ - if (serial_port_in(port, UART_LSR) & UART_LSR_DR) - serial_port_in(port, UART_RX); + serial_port_in(port, UART_LSR); + serial_port_in(port, UART_RX); serial_port_in(port, UART_IIR); serial_port_in(port, UART_MSR); @@ -2300,8 +2300,8 @@ dont_test_tx_en: * saved flags to avoid getting false values from polling * routines or the previous session. */ - if (serial_port_in(port, UART_LSR) & UART_LSR_DR) - serial_port_in(port, UART_RX); + serial_port_in(port, UART_LSR); + serial_port_in(port, UART_RX); serial_port_in(port, UART_IIR); serial_port_in(port, UART_MSR); up->lsr_saved_flags = 0; @@ -2394,8 +2394,7 @@ void serial8250_do_shutdown(struct uart_port *port) * Read data port to reset things, and then unlink from * the IRQ chain. */ - if (serial_port_in(port, UART_LSR) & UART_LSR_DR) - serial_port_in(port, UART_RX); + serial_port_in(port, UART_RX); serial8250_rpm_put(up); del_timer_sync(&up->timer); diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index e601162..6ae5b85 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -59,6 +59,8 @@ struct dw8250_data { u8 usr_reg; int last_mcr; int line; + int msr_mask_on; + int msr_mask_off; struct clk *clk; struct clk *pclk; struct reset_control *rst; @@ -81,6 +83,12 @@ static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value) value &= ~UART_MSR_DCTS; } + /* Override any modem control signals if needed */ + if (offset == UART_MSR) { + value |= d->msr_mask_on; + value &= ~d->msr_mask_off; + } + return value; } @@ -111,7 +119,10 @@ static void dw8250_serial_out(struct uart_port *p, int offset, int value) dw8250_force_idle(p); writeb(value, p->membase + (UART_LCR << p->regshift)); } - dev_err(p->dev, "Couldn't set LCR to %d\n", value); + /* + * FIXME: this deadlocks if port->lock is already held + * dev_err(p->dev, "Couldn't set LCR to %d\n", value); + */ } } @@ -155,7 +166,10 @@ static void dw8250_serial_outq(struct uart_port *p, int offset, int value) __raw_writeq(value & 0xff, p->membase + (UART_LCR << p->regshift)); } - dev_err(p->dev, "Couldn't set LCR to %d\n", value); + /* + * FIXME: this deadlocks if port->lock is already held + * dev_err(p->dev, "Couldn't set LCR to %d\n", value); + */ } } #endif /* CONFIG_64BIT */ @@ -179,7 +193,10 @@ static void dw8250_serial_out32(struct uart_port *p, int offset, int value) dw8250_force_idle(p); writel(value, p->membase + (UART_LCR << p->regshift)); } - dev_err(p->dev, "Couldn't set LCR to %d\n", value); + /* + * FIXME: this deadlocks if port->lock is already held + * dev_err(p->dev, "Couldn't set LCR to %d\n", value); + */ } } @@ -334,6 +351,30 @@ static int dw8250_probe_of(struct uart_port *p, if (id >= 0) p->line = id; + if (of_property_read_bool(np, "dcd-override")) { + /* Always report DCD as active */ + data->msr_mask_on |= UART_MSR_DCD; + data->msr_mask_off |= UART_MSR_DDCD; + } + + if (of_property_read_bool(np, "dsr-override")) { + /* Always report DSR as active */ + data->msr_mask_on |= UART_MSR_DSR; + data->msr_mask_off |= UART_MSR_DDSR; + } + + if (of_property_read_bool(np, "cts-override")) { + /* Always report DSR as active */ + data->msr_mask_on |= UART_MSR_DSR; + data->msr_mask_off |= UART_MSR_DDSR; + } + + if (of_property_read_bool(np, "ri-override")) { + /* Always report Ring indicator as inactive */ + data->msr_mask_off |= UART_MSR_RI; + data->msr_mask_off |= UART_MSR_TERI; + } + /* clock got configured through clk api, all done */ if (p->uartclk) return 0; diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index daf2c82..892eb32 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -69,7 +69,7 @@ static void moan_device(const char *str, struct pci_dev *dev) "Please send the output of lspci -vv, this\n" "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n" "manufacturer and name of serial board or\n" - "modem board to rmk+serial@arm.linux.org.uk.\n", + "modem board to <linux-serial@vger.kernel.org>.\n", pci_name(dev), str, dev->vendor, dev->device, dev->subsystem_vendor, dev->subsystem_device); } @@ -1989,13 +1989,6 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { }, { .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_QRK_UART, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_default_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, .device = PCI_DEVICE_ID_INTEL_BSW_UART1, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, @@ -2201,13 +2194,6 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { */ { .vendor = PCI_VENDOR_ID_PLX, - .device = PCI_DEVICE_ID_PLX_9030, - .subvendor = PCI_SUBVENDOR_ID_PERLE, - .subdevice = PCI_ANY_ID, - .setup = pci_default_setup, - }, - { - .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_9050, .subvendor = PCI_SUBVENDOR_ID_EXSYS, .subdevice = PCI_SUBDEVICE_ID_EXSYS_4055, @@ -5415,10 +5401,6 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH352_2S, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, pbn_b0_bt_2_115200 }, - { PCIE_VENDOR_ID_WCH, PCIE_DEVICE_ID_WCH_CH384_4S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_wch384_4 }, diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 846552b..4e959c4 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -47,6 +47,7 @@ #include <linux/gpio/consumer.h> #include <linux/err.h> #include <linux/irq.h> +#include <linux/suspend.h> #include <asm/io.h> #include <asm/ioctls.h> @@ -173,6 +174,12 @@ struct atmel_uart_port { bool ms_irq_enabled; bool is_usart; /* usart or uart */ struct timer_list uart_timer; /* uart timer */ + + bool suspended; + unsigned int pending; + unsigned int pending_status; + spinlock_t lock_suspended; + int (*prepare_rx)(struct uart_port *port); int (*prepare_tx)(struct uart_port *port); void (*schedule_rx)(struct uart_port *port); @@ -1179,12 +1186,15 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id) { struct uart_port *port = dev_id; struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - unsigned int status, pending, pass_counter = 0; + unsigned int status, pending, mask, pass_counter = 0; bool gpio_handled = false; + spin_lock(&atmel_port->lock_suspended); + do { status = atmel_get_lines_status(port); - pending = status & UART_GET_IMR(port); + mask = UART_GET_IMR(port); + pending = status & mask; if (!gpio_handled) { /* * Dealing with GPIO interrupt @@ -1206,11 +1216,21 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id) if (!pending) break; + if (atmel_port->suspended) { + atmel_port->pending |= pending; + atmel_port->pending_status = status; + UART_PUT_IDR(port, mask); + pm_system_wakeup(); + break; + } + atmel_handle_receive(port, pending); atmel_handle_status(port, pending, status); atmel_handle_transmit(port, pending); } while (pass_counter++ < ATMEL_ISR_PASS_LIMIT); + spin_unlock(&atmel_port->lock_suspended); + return pass_counter ? IRQ_HANDLED : IRQ_NONE; } @@ -1742,7 +1762,8 @@ static int atmel_startup(struct uart_port *port) /* * Allocate the IRQ */ - retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED, + retval = request_irq(port->irq, atmel_interrupt, + IRQF_SHARED | IRQF_COND_SUSPEND, tty ? tty->name : "atmel_serial", port); if (retval) { dev_err(port->dev, "atmel_startup - Can't get irq\n"); @@ -2513,8 +2534,14 @@ static int atmel_serial_suspend(struct platform_device *pdev, /* we can not wake up if we're running on slow clock */ atmel_port->may_wakeup = device_may_wakeup(&pdev->dev); - if (atmel_serial_clk_will_stop()) + if (atmel_serial_clk_will_stop()) { + unsigned long flags; + + spin_lock_irqsave(&atmel_port->lock_suspended, flags); + atmel_port->suspended = true; + spin_unlock_irqrestore(&atmel_port->lock_suspended, flags); device_set_wakeup_enable(&pdev->dev, 0); + } uart_suspend_port(&atmel_uart, port); @@ -2525,6 +2552,18 @@ static int atmel_serial_resume(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned long flags; + + spin_lock_irqsave(&atmel_port->lock_suspended, flags); + if (atmel_port->pending) { + atmel_handle_receive(port, atmel_port->pending); + atmel_handle_status(port, atmel_port->pending, + atmel_port->pending_status); + atmel_handle_transmit(port, atmel_port->pending); + atmel_port->pending = 0; + } + atmel_port->suspended = false; + spin_unlock_irqrestore(&atmel_port->lock_suspended, flags); uart_resume_port(&atmel_uart, port); device_set_wakeup_enable(&pdev->dev, atmel_port->may_wakeup); @@ -2593,6 +2632,8 @@ static int atmel_serial_probe(struct platform_device *pdev) port->backup_imr = 0; port->uart.line = ret; + spin_lock_init(&port->lock_suspended); + ret = atmel_init_gpios(port, &pdev->dev); if (ret < 0) dev_err(&pdev->dev, "%s", diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index b1893f3..3ad1458 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -921,6 +921,9 @@ static void lpuart_setup_watermark(struct lpuart_port *sport) writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE, sport->port.membase + UARTPFIFO); + /* explicitly clear RDRF */ + readb(sport->port.membase + UARTSR1); + /* flush Tx and Rx FIFO */ writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH, sport->port.membase + UARTCFIFO); @@ -1076,6 +1079,8 @@ static int lpuart_startup(struct uart_port *port) sport->txfifo_size = 0x1 << (((temp >> UARTPFIFO_TXSIZE_OFF) & UARTPFIFO_FIFOSIZE_MASK) + 1); + sport->port.fifosize = sport->txfifo_size; + sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) & UARTPFIFO_FIFOSIZE_MASK) + 1); diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 7ff61e2..33fb94f 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -133,10 +133,6 @@ static int of_platform_serial_setup(struct platform_device *ofdev, if (of_find_property(np, "no-loopback-test", NULL)) port->flags |= UPF_SKIP_TEST; - ret = of_alias_get_id(np, "serial"); - if (ret >= 0) - port->line = ret; - port->dev = &ofdev->dev; switch (type) { diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index af821a9..cf08876 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -963,6 +963,7 @@ static void s3c24xx_serial_shutdown(struct uart_port *port) free_irq(ourport->tx_irq, ourport); tx_enabled(port) = 0; ourport->tx_claimed = 0; + ourport->tx_mode = 0; } if (ourport->rx_claimed) { diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 594b633..bca975f 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -293,8 +293,10 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id) ims = serial_in(port, SPRD_IMSR); - if (!ims) + if (!ims) { + spin_unlock(&port->lock); return IRQ_NONE; + } serial_out(port, SPRD_ICLR, ~0); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 51f066a..2bb4dfc 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1028,8 +1028,8 @@ EXPORT_SYMBOL(start_tty); /* We limit tty time update visibility to every 8 seconds or so. */ static void tty_update_time(struct timespec *time) { - unsigned long sec = get_seconds() & ~7; - if ((long)(sec - time->tv_sec) > 0) + unsigned long sec = get_seconds(); + if (abs(sec - time->tv_sec) & ~7) time->tv_sec = sec; } diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index a5cf253..632fc81 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -217,11 +217,17 @@ void tty_wait_until_sent(struct tty_struct *tty, long timeout) #endif if (!timeout) timeout = MAX_SCHEDULE_TIMEOUT; - if (wait_event_interruptible_timeout(tty->write_wait, - !tty_chars_in_buffer(tty), timeout) >= 0) { - if (tty->ops->wait_until_sent) - tty->ops->wait_until_sent(tty, timeout); - } + + timeout = wait_event_interruptible_timeout(tty->write_wait, + !tty_chars_in_buffer(tty), timeout); + if (timeout <= 0) + return; + + if (timeout == MAX_SCHEDULE_TIMEOUT) + timeout = 0; + + if (tty->ops->wait_until_sent) + tty->ops->wait_until_sent(tty, timeout); } EXPORT_SYMBOL(tty_wait_until_sent); diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index ff45104..4bfb7ac 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -929,6 +929,13 @@ __acquires(hwep->lock) return retval; } +static int otg_a_alt_hnp_support(struct ci_hdrc *ci) +{ + dev_warn(&ci->gadget.dev, + "connect the device to an alternate port if you want HNP\n"); + return isr_setup_status_phase(ci); +} + /** * isr_setup_packet_handler: setup packet handler * @ci: UDC descriptor @@ -1061,6 +1068,10 @@ __acquires(ci->lock) ci); } break; + case USB_DEVICE_A_ALT_HNP_SUPPORT: + if (ci_otg_is_fsm_mode(ci)) + err = otg_a_alt_hnp_support(ci); + break; default: goto delegate; } diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index e78720b..6836177 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1650,6 +1650,8 @@ static int acm_reset_resume(struct usb_interface *intf) static const struct usb_device_id acm_ids[] = { /* quirky and broken devices */ + { USB_DEVICE(0x076d, 0x0006), /* Denso Cradle CU-321 */ + .driver_info = NO_UNION_NORMAL, },/* has no union descriptor */ { USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */ .driver_info = NO_UNION_NORMAL, },/* has no union descriptor */ { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */ diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c index c6b35b7..61d538a 100644 --- a/drivers/usb/common/usb-otg-fsm.c +++ b/drivers/usb/common/usb-otg-fsm.c @@ -150,9 +150,9 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) break; case OTG_STATE_B_PERIPHERAL: otg_chrg_vbus(fsm, 0); - otg_loc_conn(fsm, 1); otg_loc_sof(fsm, 0); otg_set_protocol(fsm, PROTO_GADGET); + otg_loc_conn(fsm, 1); break; case OTG_STATE_B_WAIT_ACON: otg_chrg_vbus(fsm, 0); @@ -213,10 +213,10 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) break; case OTG_STATE_A_PERIPHERAL: - otg_loc_conn(fsm, 1); otg_loc_sof(fsm, 0); otg_set_protocol(fsm, PROTO_GADGET); otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 1); otg_add_timer(fsm, A_BIDL_ADIS); break; case OTG_STATE_A_WAIT_VFALL: diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 66abdbc..1163553 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -501,6 +501,7 @@ static void async_completed(struct urb *urb) as->status = urb->status; signr = as->signr; if (signr) { + memset(&sinfo, 0, sizeof(sinfo)); sinfo.si_signo = as->signr; sinfo.si_errno = as->status; sinfo.si_code = SI_ASYNCIO; @@ -2382,6 +2383,7 @@ static void usbdev_remove(struct usb_device *udev) wake_up_all(&ps->wait); list_del_init(&ps->list); if (ps->discsignr) { + memset(&sinfo, 0, sizeof(sinfo)); sinfo.si_signo = ps->discsignr; sinfo.si_errno = EPIPE; sinfo.si_code = SI_ASYNCIO; diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 02e3e2d..6cf0478 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -377,6 +377,9 @@ static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg) dwc2_is_host_mode(hsotg) ? "Host" : "Device", dwc2_op_state_str(hsotg)); + if (hsotg->op_state == OTG_STATE_A_HOST) + dwc2_hcd_disconnect(hsotg); + /* Change to L3 (OFF) state */ hsotg->lx_state = DWC2_L3; diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 172d64e..52e0c4e 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -205,6 +205,18 @@ static void dwc3_omap_write_irq0_set(struct dwc3_omap *omap, u32 value) omap->irq0_offset, value); } +static void dwc3_omap_write_irqmisc_clr(struct dwc3_omap *omap, u32 value) +{ + dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_CLR_MISC + + omap->irqmisc_offset, value); +} + +static void dwc3_omap_write_irq0_clr(struct dwc3_omap *omap, u32 value) +{ + dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_CLR_0 - + omap->irq0_offset, value); +} + static void dwc3_omap_set_mailbox(struct dwc3_omap *omap, enum omap_dwc3_vbus_id_status status) { @@ -345,9 +357,23 @@ static void dwc3_omap_enable_irqs(struct dwc3_omap *omap) static void dwc3_omap_disable_irqs(struct dwc3_omap *omap) { + u32 reg; + /* disable all IRQs */ - dwc3_omap_write_irqmisc_set(omap, 0x00); - dwc3_omap_write_irq0_set(omap, 0x00); + reg = USBOTGSS_IRQO_COREIRQ_ST; + dwc3_omap_write_irq0_clr(omap, reg); + + reg = (USBOTGSS_IRQMISC_OEVT | + USBOTGSS_IRQMISC_DRVVBUS_RISE | + USBOTGSS_IRQMISC_CHRGVBUS_RISE | + USBOTGSS_IRQMISC_DISCHRGVBUS_RISE | + USBOTGSS_IRQMISC_IDPULLUP_RISE | + USBOTGSS_IRQMISC_DRVVBUS_FALL | + USBOTGSS_IRQMISC_CHRGVBUS_FALL | + USBOTGSS_IRQMISC_DISCHRGVBUS_FALL | + USBOTGSS_IRQMISC_IDPULLUP_FALL); + + dwc3_omap_write_irqmisc_clr(omap, reg); } static u64 dwc3_omap_dma_mask = DMA_BIT_MASK(32); diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 7564814..c42765b 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1161,7 +1161,6 @@ static ssize_t interf_grp_compatible_id_store(struct usb_os_desc *desc, if (desc->opts_mutex) mutex_lock(desc->opts_mutex); memcpy(desc->ext_compat_id, page, l); - desc->ext_compat_id[l] = '\0'; if (desc->opts_mutex) mutex_unlock(desc->opts_mutex); @@ -1192,7 +1191,6 @@ static ssize_t interf_grp_sub_compatible_id_store(struct usb_os_desc *desc, if (desc->opts_mutex) mutex_lock(desc->opts_mutex); memcpy(desc->ext_compat_id + 8, page, l); - desc->ext_compat_id[l + 8] = '\0'; if (desc->opts_mutex) mutex_unlock(desc->opts_mutex); diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index af98b09..175c995 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -144,10 +144,9 @@ struct ffs_io_data { bool read; struct kiocb *kiocb; - const struct iovec *iovec; - unsigned long nr_segs; - char __user *buf; - size_t len; + struct iov_iter data; + const void *to_free; + char *buf; struct mm_struct *mm; struct work_struct work; @@ -649,29 +648,10 @@ static void ffs_user_copy_worker(struct work_struct *work) io_data->req->actual; if (io_data->read && ret > 0) { - int i; - size_t pos = 0; - - /* - * Since req->length may be bigger than io_data->len (after - * being rounded up to maxpacketsize), we may end up with more - * data then user space has space for. - */ - ret = min_t(int, ret, io_data->len); - use_mm(io_data->mm); - for (i = 0; i < io_data->nr_segs; i++) { - size_t len = min_t(size_t, ret - pos, - io_data->iovec[i].iov_len); - if (!len) - break; - if (unlikely(copy_to_user(io_data->iovec[i].iov_base, - &io_data->buf[pos], len))) { - ret = -EFAULT; - break; - } - pos += len; - } + ret = copy_to_iter(io_data->buf, ret, &io_data->data); + if (iov_iter_count(&io_data->data)) + ret = -EFAULT; unuse_mm(io_data->mm); } @@ -684,7 +664,7 @@ static void ffs_user_copy_worker(struct work_struct *work) io_data->kiocb->private = NULL; if (io_data->read) - kfree(io_data->iovec); + kfree(io_data->to_free); kfree(io_data->buf); kfree(io_data); } @@ -743,6 +723,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) * before the waiting completes, so do not assign to 'gadget' earlier */ struct usb_gadget *gadget = epfile->ffs->gadget; + size_t copied; spin_lock_irq(&epfile->ffs->eps_lock); /* In the meantime, endpoint got disabled or changed. */ @@ -750,34 +731,21 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) spin_unlock_irq(&epfile->ffs->eps_lock); return -ESHUTDOWN; } + data_len = iov_iter_count(&io_data->data); /* * Controller may require buffer size to be aligned to * maxpacketsize of an out endpoint. */ - data_len = io_data->read ? - usb_ep_align_maybe(gadget, ep->ep, io_data->len) : - io_data->len; + if (io_data->read) + data_len = usb_ep_align_maybe(gadget, ep->ep, data_len); spin_unlock_irq(&epfile->ffs->eps_lock); data = kmalloc(data_len, GFP_KERNEL); if (unlikely(!data)) return -ENOMEM; - if (io_data->aio && !io_data->read) { - int i; - size_t pos = 0; - for (i = 0; i < io_data->nr_segs; i++) { - if (unlikely(copy_from_user(&data[pos], - io_data->iovec[i].iov_base, - io_data->iovec[i].iov_len))) { - ret = -EFAULT; - goto error; - } - pos += io_data->iovec[i].iov_len; - } - } else { - if (!io_data->read && - unlikely(__copy_from_user(data, io_data->buf, - io_data->len))) { + if (!io_data->read) { + copied = copy_from_iter(data, data_len, &io_data->data); + if (copied != data_len) { ret = -EFAULT; goto error; } @@ -876,10 +844,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) */ ret = ep->status; if (io_data->read && ret > 0) { - ret = min_t(size_t, ret, io_data->len); - - if (unlikely(copy_to_user(io_data->buf, - data, ret))) + ret = copy_to_iter(data, ret, &io_data->data); + if (unlikely(iov_iter_count(&io_data->data))) ret = -EFAULT; } } @@ -898,37 +864,6 @@ error: return ret; } -static ssize_t -ffs_epfile_write(struct file *file, const char __user *buf, size_t len, - loff_t *ptr) -{ - struct ffs_io_data io_data; - - ENTER(); - - io_data.aio = false; - io_data.read = false; - io_data.buf = (char * __user)buf; - io_data.len = len; - - return ffs_epfile_io(file, &io_data); -} - -static ssize_t -ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr) -{ - struct ffs_io_data io_data; - - ENTER(); - - io_data.aio = false; - io_data.read = true; - io_data.buf = buf; - io_data.len = len; - - return ffs_epfile_io(file, &io_data); -} - static int ffs_epfile_open(struct inode *inode, struct file *file) { @@ -965,67 +900,86 @@ static int ffs_aio_cancel(struct kiocb *kiocb) return value; } -static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb, - const struct iovec *iovec, - unsigned long nr_segs, loff_t loff) +static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from) { - struct ffs_io_data *io_data; + struct ffs_io_data io_data, *p = &io_data; + ssize_t res; ENTER(); - io_data = kmalloc(sizeof(*io_data), GFP_KERNEL); - if (unlikely(!io_data)) - return -ENOMEM; + if (!is_sync_kiocb(kiocb)) { + p = kmalloc(sizeof(io_data), GFP_KERNEL); + if (unlikely(!p)) + return -ENOMEM; + p->aio = true; + } else { + p->aio = false; + } - io_data->aio = true; - io_data->read = false; - io_data->kiocb = kiocb; - io_data->iovec = iovec; - io_data->nr_segs = nr_segs; - io_data->len = kiocb->ki_nbytes; - io_data->mm = current->mm; + p->read = false; + p->kiocb = kiocb; + p->data = *from; + p->mm = current->mm; - kiocb->private = io_data; + kiocb->private = p; kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); - return ffs_epfile_io(kiocb->ki_filp, io_data); + res = ffs_epfile_io(kiocb->ki_filp, p); + if (res == -EIOCBQUEUED) + return res; + if (p->aio) + kfree(p); + else + *from = p->data; + return res; } -static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb, - const struct iovec *iovec, - unsigned long nr_segs, loff_t loff) +static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) { - struct ffs_io_data *io_data; - struct iovec *iovec_copy; + struct ffs_io_data io_data, *p = &io_data; + ssize_t res; ENTER(); - iovec_copy = kmalloc_array(nr_segs, sizeof(*iovec_copy), GFP_KERNEL); - if (unlikely(!iovec_copy)) - return -ENOMEM; - - memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs); - - io_data = kmalloc(sizeof(*io_data), GFP_KERNEL); - if (unlikely(!io_data)) { - kfree(iovec_copy); - return -ENOMEM; + if (!is_sync_kiocb(kiocb)) { + p = kmalloc(sizeof(io_data), GFP_KERNEL); + if (unlikely(!p)) + return -ENOMEM; + p->aio = true; + } else { + p->aio = false; } - io_data->aio = true; - io_data->read = true; - io_data->kiocb = kiocb; - io_data->iovec = iovec_copy; - io_data->nr_segs = nr_segs; - io_data->len = kiocb->ki_nbytes; - io_data->mm = current->mm; + p->read = true; + p->kiocb = kiocb; + if (p->aio) { + p->to_free = dup_iter(&p->data, to, GFP_KERNEL); + if (!p->to_free) { + kfree(p); + return -ENOMEM; + } + } else { + p->data = *to; + p->to_free = NULL; + } + p->mm = current->mm; - kiocb->private = io_data; + kiocb->private = p; kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); - return ffs_epfile_io(kiocb->ki_filp, io_data); + res = ffs_epfile_io(kiocb->ki_filp, p); + if (res == -EIOCBQUEUED) + return res; + + if (p->aio) { + kfree(p->to_free); + kfree(p); + } else { + *to = p->data; + } + return res; } static int @@ -1105,10 +1059,10 @@ static const struct file_operations ffs_epfile_operations = { .llseek = no_llseek, .open = ffs_epfile_open, - .write = ffs_epfile_write, - .read = ffs_epfile_read, - .aio_write = ffs_epfile_aio_write, - .aio_read = ffs_epfile_aio_read, + .write = new_sync_write, + .read = new_sync_read, + .write_iter = ffs_epfile_write_iter, + .read_iter = ffs_epfile_read_iter, .release = ffs_epfile_release, .unlocked_ioctl = ffs_epfile_ioctl, }; diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 426d69a..a2612fb 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -569,7 +569,7 @@ fail: return status; } -const struct file_operations f_hidg_fops = { +static const struct file_operations f_hidg_fops = { .owner = THIS_MODULE, .open = f_hidg_open, .release = f_hidg_release, diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index 298b461..39f49f1 100644 --- a/drivers/usb/gadget/function/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c @@ -289,8 +289,7 @@ static void disable_loopback(struct f_loopback *loop) struct usb_composite_dev *cdev; cdev = loop->function.config->cdev; - disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL, NULL, - NULL); + disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL); VDBG(cdev, "%s disabled\n", loop->function.name); } diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c index c89e96c..c0c3ef2 100644 --- a/drivers/usb/gadget/function/f_phonet.c +++ b/drivers/usb/gadget/function/f_phonet.c @@ -417,7 +417,10 @@ static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt) return -EINVAL; spin_lock(&port->lock); - __pn_reset(f); + + if (fp->in_ep->driver_data) + __pn_reset(f); + if (alt == 1) { int i; diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index e07c50c..3a5ae99 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -23,15 +23,6 @@ #include "gadget_chips.h" #include "u_f.h" -#define USB_MS_TO_SS_INTERVAL(x) USB_MS_TO_HS_INTERVAL(x) - -enum eptype { - EP_CONTROL = 0, - EP_BULK, - EP_ISOC, - EP_INTERRUPT, -}; - /* * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral * controller drivers. @@ -64,8 +55,6 @@ struct f_sourcesink { struct usb_ep *out_ep; struct usb_ep *iso_in_ep; struct usb_ep *iso_out_ep; - struct usb_ep *int_in_ep; - struct usb_ep *int_out_ep; int cur_alt; }; @@ -79,10 +68,6 @@ static unsigned isoc_interval; static unsigned isoc_maxpacket; static unsigned isoc_mult; static unsigned isoc_maxburst; -static unsigned int_interval; /* In ms */ -static unsigned int_maxpacket; -static unsigned int_mult; -static unsigned int_maxburst; static unsigned buflen; /*-------------------------------------------------------------------------*/ @@ -107,16 +92,6 @@ static struct usb_interface_descriptor source_sink_intf_alt1 = { /* .iInterface = DYNAMIC */ }; -static struct usb_interface_descriptor source_sink_intf_alt2 = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - - .bAlternateSetting = 2, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - /* .iInterface = DYNAMIC */ -}; - /* full speed support: */ static struct usb_endpoint_descriptor fs_source_desc = { @@ -155,26 +130,6 @@ static struct usb_endpoint_descriptor fs_iso_sink_desc = { .bInterval = 4, }; -static struct usb_endpoint_descriptor fs_int_source_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(64), - .bInterval = GZERO_INT_INTERVAL, -}; - -static struct usb_endpoint_descriptor fs_int_sink_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(64), - .bInterval = GZERO_INT_INTERVAL, -}; - static struct usb_descriptor_header *fs_source_sink_descs[] = { (struct usb_descriptor_header *) &source_sink_intf_alt0, (struct usb_descriptor_header *) &fs_sink_desc, @@ -185,10 +140,6 @@ static struct usb_descriptor_header *fs_source_sink_descs[] = { (struct usb_descriptor_header *) &fs_source_desc, (struct usb_descriptor_header *) &fs_iso_sink_desc, (struct usb_descriptor_header *) &fs_iso_source_desc, - (struct usb_descriptor_header *) &source_sink_intf_alt2, -#define FS_ALT_IFC_2_OFFSET 8 - (struct usb_descriptor_header *) &fs_int_sink_desc, - (struct usb_descriptor_header *) &fs_int_source_desc, NULL, }; @@ -228,24 +179,6 @@ static struct usb_endpoint_descriptor hs_iso_sink_desc = { .bInterval = 4, }; -static struct usb_endpoint_descriptor hs_int_source_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(1024), - .bInterval = USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL), -}; - -static struct usb_endpoint_descriptor hs_int_sink_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(1024), - .bInterval = USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL), -}; - static struct usb_descriptor_header *hs_source_sink_descs[] = { (struct usb_descriptor_header *) &source_sink_intf_alt0, (struct usb_descriptor_header *) &hs_source_desc, @@ -256,10 +189,6 @@ static struct usb_descriptor_header *hs_source_sink_descs[] = { (struct usb_descriptor_header *) &hs_sink_desc, (struct usb_descriptor_header *) &hs_iso_source_desc, (struct usb_descriptor_header *) &hs_iso_sink_desc, - (struct usb_descriptor_header *) &source_sink_intf_alt2, -#define HS_ALT_IFC_2_OFFSET 8 - (struct usb_descriptor_header *) &hs_int_source_desc, - (struct usb_descriptor_header *) &hs_int_sink_desc, NULL, }; @@ -335,42 +264,6 @@ static struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = { .wBytesPerInterval = cpu_to_le16(1024), }; -static struct usb_endpoint_descriptor ss_int_source_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(1024), - .bInterval = USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL), -}; - -struct usb_ss_ep_comp_descriptor ss_int_source_comp_desc = { - .bLength = USB_DT_SS_EP_COMP_SIZE, - .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, - - .bMaxBurst = 0, - .bmAttributes = 0, - .wBytesPerInterval = cpu_to_le16(1024), -}; - -static struct usb_endpoint_descriptor ss_int_sink_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(1024), - .bInterval = USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL), -}; - -struct usb_ss_ep_comp_descriptor ss_int_sink_comp_desc = { - .bLength = USB_DT_SS_EP_COMP_SIZE, - .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, - - .bMaxBurst = 0, - .bmAttributes = 0, - .wBytesPerInterval = cpu_to_le16(1024), -}; - static struct usb_descriptor_header *ss_source_sink_descs[] = { (struct usb_descriptor_header *) &source_sink_intf_alt0, (struct usb_descriptor_header *) &ss_source_desc, @@ -387,12 +280,6 @@ static struct usb_descriptor_header *ss_source_sink_descs[] = { (struct usb_descriptor_header *) &ss_iso_source_comp_desc, (struct usb_descriptor_header *) &ss_iso_sink_desc, (struct usb_descriptor_header *) &ss_iso_sink_comp_desc, - (struct usb_descriptor_header *) &source_sink_intf_alt2, -#define SS_ALT_IFC_2_OFFSET 14 - (struct usb_descriptor_header *) &ss_int_source_desc, - (struct usb_descriptor_header *) &ss_int_source_comp_desc, - (struct usb_descriptor_header *) &ss_int_sink_desc, - (struct usb_descriptor_header *) &ss_int_sink_comp_desc, NULL, }; @@ -414,21 +301,6 @@ static struct usb_gadget_strings *sourcesink_strings[] = { }; /*-------------------------------------------------------------------------*/ -static const char *get_ep_string(enum eptype ep_type) -{ - switch (ep_type) { - case EP_ISOC: - return "ISOC-"; - case EP_INTERRUPT: - return "INTERRUPT-"; - case EP_CONTROL: - return "CTRL-"; - case EP_BULK: - return "BULK-"; - default: - return "UNKNOWN-"; - } -} static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len) { @@ -456,8 +328,7 @@ static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) void disable_endpoints(struct usb_composite_dev *cdev, struct usb_ep *in, struct usb_ep *out, - struct usb_ep *iso_in, struct usb_ep *iso_out, - struct usb_ep *int_in, struct usb_ep *int_out) + struct usb_ep *iso_in, struct usb_ep *iso_out) { disable_ep(cdev, in); disable_ep(cdev, out); @@ -465,10 +336,6 @@ void disable_endpoints(struct usb_composite_dev *cdev, disable_ep(cdev, iso_in); if (iso_out) disable_ep(cdev, iso_out); - if (int_in) - disable_ep(cdev, int_in); - if (int_out) - disable_ep(cdev, int_out); } static int @@ -485,7 +352,6 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f) return id; source_sink_intf_alt0.bInterfaceNumber = id; source_sink_intf_alt1.bInterfaceNumber = id; - source_sink_intf_alt2.bInterfaceNumber = id; /* allocate bulk endpoints */ ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); @@ -546,55 +412,14 @@ no_iso: if (isoc_maxpacket > 1024) isoc_maxpacket = 1024; - /* sanity check the interrupt module parameters */ - if (int_interval < 1) - int_interval = 1; - if (int_interval > 4096) - int_interval = 4096; - if (int_mult > 2) - int_mult = 2; - if (int_maxburst > 15) - int_maxburst = 15; - - /* fill in the FS interrupt descriptors from the module parameters */ - fs_int_source_desc.wMaxPacketSize = int_maxpacket > 64 ? - 64 : int_maxpacket; - fs_int_source_desc.bInterval = int_interval > 255 ? - 255 : int_interval; - fs_int_sink_desc.wMaxPacketSize = int_maxpacket > 64 ? - 64 : int_maxpacket; - fs_int_sink_desc.bInterval = int_interval > 255 ? - 255 : int_interval; - - /* allocate int endpoints */ - ss->int_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_source_desc); - if (!ss->int_in_ep) - goto no_int; - ss->int_in_ep->driver_data = cdev; /* claim */ - - ss->int_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_sink_desc); - if (ss->int_out_ep) { - ss->int_out_ep->driver_data = cdev; /* claim */ - } else { - ss->int_in_ep->driver_data = NULL; - ss->int_in_ep = NULL; -no_int: - fs_source_sink_descs[FS_ALT_IFC_2_OFFSET] = NULL; - hs_source_sink_descs[HS_ALT_IFC_2_OFFSET] = NULL; - ss_source_sink_descs[SS_ALT_IFC_2_OFFSET] = NULL; - } - - if (int_maxpacket > 1024) - int_maxpacket = 1024; - /* support high speed hardware */ hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; /* - * Fill in the HS isoc and interrupt descriptors from the module - * parameters. We assume that the user knows what they are doing and - * won't give parameters that their UDC doesn't support. + * Fill in the HS isoc descriptors from the module parameters. + * We assume that the user knows what they are doing and won't + * give parameters that their UDC doesn't support. */ hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket; hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11; @@ -607,17 +432,6 @@ no_int: hs_iso_sink_desc.bInterval = isoc_interval; hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; - hs_int_source_desc.wMaxPacketSize = int_maxpacket; - hs_int_source_desc.wMaxPacketSize |= int_mult << 11; - hs_int_source_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval); - hs_int_source_desc.bEndpointAddress = - fs_int_source_desc.bEndpointAddress; - - hs_int_sink_desc.wMaxPacketSize = int_maxpacket; - hs_int_sink_desc.wMaxPacketSize |= int_mult << 11; - hs_int_sink_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval); - hs_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress; - /* support super speed hardware */ ss_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; @@ -625,9 +439,9 @@ no_int: fs_sink_desc.bEndpointAddress; /* - * Fill in the SS isoc and interrupt descriptors from the module - * parameters. We assume that the user knows what they are doing and - * won't give parameters that their UDC doesn't support. + * Fill in the SS isoc descriptors from the module parameters. + * We assume that the user knows what they are doing and won't + * give parameters that their UDC doesn't support. */ ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket; ss_iso_source_desc.bInterval = isoc_interval; @@ -646,37 +460,17 @@ no_int: isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1); ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; - ss_int_source_desc.wMaxPacketSize = int_maxpacket; - ss_int_source_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval); - ss_int_source_comp_desc.bmAttributes = int_mult; - ss_int_source_comp_desc.bMaxBurst = int_maxburst; - ss_int_source_comp_desc.wBytesPerInterval = - int_maxpacket * (int_mult + 1) * (int_maxburst + 1); - ss_int_source_desc.bEndpointAddress = - fs_int_source_desc.bEndpointAddress; - - ss_int_sink_desc.wMaxPacketSize = int_maxpacket; - ss_int_sink_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval); - ss_int_sink_comp_desc.bmAttributes = int_mult; - ss_int_sink_comp_desc.bMaxBurst = int_maxburst; - ss_int_sink_comp_desc.wBytesPerInterval = - int_maxpacket * (int_mult + 1) * (int_maxburst + 1); - ss_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress; - ret = usb_assign_descriptors(f, fs_source_sink_descs, hs_source_sink_descs, ss_source_sink_descs); if (ret) return ret; - DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s, " - "INT-IN/%s, INT-OUT/%s\n", + DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n", (gadget_is_superspeed(c->cdev->gadget) ? "super" : (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), f->name, ss->in_ep->name, ss->out_ep->name, ss->iso_in_ep ? ss->iso_in_ep->name : "<none>", - ss->iso_out_ep ? ss->iso_out_ep->name : "<none>", - ss->int_in_ep ? ss->int_in_ep->name : "<none>", - ss->int_out_ep ? ss->int_out_ep->name : "<none>"); + ss->iso_out_ep ? ss->iso_out_ep->name : "<none>"); return 0; } @@ -807,15 +601,14 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) } static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, - enum eptype ep_type, int speed) + bool is_iso, int speed) { struct usb_ep *ep; struct usb_request *req; int i, size, status; for (i = 0; i < 8; i++) { - switch (ep_type) { - case EP_ISOC: + if (is_iso) { switch (speed) { case USB_SPEED_SUPER: size = isoc_maxpacket * (isoc_mult + 1) * @@ -831,28 +624,9 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, } ep = is_in ? ss->iso_in_ep : ss->iso_out_ep; req = ss_alloc_ep_req(ep, size); - break; - case EP_INTERRUPT: - switch (speed) { - case USB_SPEED_SUPER: - size = int_maxpacket * (int_mult + 1) * - (int_maxburst + 1); - break; - case USB_SPEED_HIGH: - size = int_maxpacket * (int_mult + 1); - break; - default: - size = int_maxpacket > 1023 ? - 1023 : int_maxpacket; - break; - } - ep = is_in ? ss->int_in_ep : ss->int_out_ep; - req = ss_alloc_ep_req(ep, size); - break; - default: + } else { ep = is_in ? ss->in_ep : ss->out_ep; req = ss_alloc_ep_req(ep, 0); - break; } if (!req) @@ -870,12 +644,12 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, cdev = ss->function.config->cdev; ERROR(cdev, "start %s%s %s --> %d\n", - get_ep_string(ep_type), is_in ? "IN" : "OUT", - ep->name, status); + is_iso ? "ISO-" : "", is_in ? "IN" : "OUT", + ep->name, status); free_ep_req(ep, req); } - if (!(ep_type == EP_ISOC)) + if (!is_iso) break; } @@ -888,7 +662,7 @@ static void disable_source_sink(struct f_sourcesink *ss) cdev = ss->function.config->cdev; disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep, - ss->iso_out_ep, ss->int_in_ep, ss->int_out_ep); + ss->iso_out_ep); VDBG(cdev, "%s disabled\n", ss->function.name); } @@ -900,62 +674,6 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss, int speed = cdev->gadget->speed; struct usb_ep *ep; - if (alt == 2) { - /* Configure for periodic interrupt endpoint */ - ep = ss->int_in_ep; - if (ep) { - result = config_ep_by_speed(cdev->gadget, - &(ss->function), ep); - if (result) - return result; - - result = usb_ep_enable(ep); - if (result < 0) - return result; - - ep->driver_data = ss; - result = source_sink_start_ep(ss, true, EP_INTERRUPT, - speed); - if (result < 0) { -fail1: - ep = ss->int_in_ep; - if (ep) { - usb_ep_disable(ep); - ep->driver_data = NULL; - } - return result; - } - } - - /* - * one interrupt endpoint reads (sinks) anything OUT (from the - * host) - */ - ep = ss->int_out_ep; - if (ep) { - result = config_ep_by_speed(cdev->gadget, - &(ss->function), ep); - if (result) - goto fail1; - - result = usb_ep_enable(ep); - if (result < 0) - goto fail1; - - ep->driver_data = ss; - result = source_sink_start_ep(ss, false, EP_INTERRUPT, - speed); - if (result < 0) { - ep = ss->int_out_ep; - usb_ep_disable(ep); - ep->driver_data = NULL; - goto fail1; - } - } - - goto out; - } - /* one bulk endpoint writes (sources) zeroes IN (to the host) */ ep = ss->in_ep; result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); @@ -966,7 +684,7 @@ fail1: return result; ep->driver_data = ss; - result = source_sink_start_ep(ss, true, EP_BULK, speed); + result = source_sink_start_ep(ss, true, false, speed); if (result < 0) { fail: ep = ss->in_ep; @@ -985,7 +703,7 @@ fail: goto fail; ep->driver_data = ss; - result = source_sink_start_ep(ss, false, EP_BULK, speed); + result = source_sink_start_ep(ss, false, false, speed); if (result < 0) { fail2: ep = ss->out_ep; @@ -1008,7 +726,7 @@ fail2: goto fail2; ep->driver_data = ss; - result = source_sink_start_ep(ss, true, EP_ISOC, speed); + result = source_sink_start_ep(ss, true, true, speed); if (result < 0) { fail3: ep = ss->iso_in_ep; @@ -1031,14 +749,13 @@ fail3: goto fail3; ep->driver_data = ss; - result = source_sink_start_ep(ss, false, EP_ISOC, speed); + result = source_sink_start_ep(ss, false, true, speed); if (result < 0) { usb_ep_disable(ep); ep->driver_data = NULL; goto fail3; } } - out: ss->cur_alt = alt; @@ -1054,8 +771,6 @@ static int sourcesink_set_alt(struct usb_function *f, if (ss->in_ep->driver_data) disable_source_sink(ss); - else if (alt == 2 && ss->int_in_ep->driver_data) - disable_source_sink(ss); return enable_source_sink(cdev, ss, alt); } @@ -1168,10 +883,6 @@ static struct usb_function *source_sink_alloc_func( isoc_maxpacket = ss_opts->isoc_maxpacket; isoc_mult = ss_opts->isoc_mult; isoc_maxburst = ss_opts->isoc_maxburst; - int_interval = ss_opts->int_interval; - int_maxpacket = ss_opts->int_maxpacket; - int_mult = ss_opts->int_mult; - int_maxburst = ss_opts->int_maxburst; buflen = ss_opts->bulk_buflen; ss->function.name = "source/sink"; @@ -1468,182 +1179,6 @@ static struct f_ss_opts_attribute f_ss_opts_bulk_buflen = f_ss_opts_bulk_buflen_show, f_ss_opts_bulk_buflen_store); -static ssize_t f_ss_opts_int_interval_show(struct f_ss_opts *opts, char *page) -{ - int result; - - mutex_lock(&opts->lock); - result = sprintf(page, "%u", opts->int_interval); - mutex_unlock(&opts->lock); - - return result; -} - -static ssize_t f_ss_opts_int_interval_store(struct f_ss_opts *opts, - const char *page, size_t len) -{ - int ret; - u32 num; - - mutex_lock(&opts->lock); - if (opts->refcnt) { - ret = -EBUSY; - goto end; - } - - ret = kstrtou32(page, 0, &num); - if (ret) - goto end; - - if (num > 4096) { - ret = -EINVAL; - goto end; - } - - opts->int_interval = num; - ret = len; -end: - mutex_unlock(&opts->lock); - return ret; -} - -static struct f_ss_opts_attribute f_ss_opts_int_interval = - __CONFIGFS_ATTR(int_interval, S_IRUGO | S_IWUSR, - f_ss_opts_int_interval_show, - f_ss_opts_int_interval_store); - -static ssize_t f_ss_opts_int_maxpacket_show(struct f_ss_opts *opts, char *page) -{ - int result; - - mutex_lock(&opts->lock); - result = sprintf(page, "%u", opts->int_maxpacket); - mutex_unlock(&opts->lock); - - return result; -} - -static ssize_t f_ss_opts_int_maxpacket_store(struct f_ss_opts *opts, - const char *page, size_t len) -{ - int ret; - u16 num; - - mutex_lock(&opts->lock); - if (opts->refcnt) { - ret = -EBUSY; - goto end; - } - - ret = kstrtou16(page, 0, &num); - if (ret) - goto end; - - if (num > 1024) { - ret = -EINVAL; - goto end; - } - - opts->int_maxpacket = num; - ret = len; -end: - mutex_unlock(&opts->lock); - return ret; -} - -static struct f_ss_opts_attribute f_ss_opts_int_maxpacket = - __CONFIGFS_ATTR(int_maxpacket, S_IRUGO | S_IWUSR, - f_ss_opts_int_maxpacket_show, - f_ss_opts_int_maxpacket_store); - -static ssize_t f_ss_opts_int_mult_show(struct f_ss_opts *opts, char *page) -{ - int result; - - mutex_lock(&opts->lock); - result = sprintf(page, "%u", opts->int_mult); - mutex_unlock(&opts->lock); - - return result; -} - -static ssize_t f_ss_opts_int_mult_store(struct f_ss_opts *opts, - const char *page, size_t len) -{ - int ret; - u8 num; - - mutex_lock(&opts->lock); - if (opts->refcnt) { - ret = -EBUSY; - goto end; - } - - ret = kstrtou8(page, 0, &num); - if (ret) - goto end; - - if (num > 2) { - ret = -EINVAL; - goto end; - } - - opts->int_mult = num; - ret = len; -end: - mutex_unlock(&opts->lock); - return ret; -} - -static struct f_ss_opts_attribute f_ss_opts_int_mult = - __CONFIGFS_ATTR(int_mult, S_IRUGO | S_IWUSR, - f_ss_opts_int_mult_show, - f_ss_opts_int_mult_store); - -static ssize_t f_ss_opts_int_maxburst_show(struct f_ss_opts *opts, char *page) -{ - int result; - - mutex_lock(&opts->lock); - result = sprintf(page, "%u", opts->int_maxburst); - mutex_unlock(&opts->lock); - - return result; -} - -static ssize_t f_ss_opts_int_maxburst_store(struct f_ss_opts *opts, - const char *page, size_t len) -{ - int ret; - u8 num; - - mutex_lock(&opts->lock); - if (opts->refcnt) { - ret = -EBUSY; - goto end; - } - - ret = kstrtou8(page, 0, &num); - if (ret) - goto end; - - if (num > 15) { - ret = -EINVAL; - goto end; - } - - opts->int_maxburst = num; - ret = len; -end: - mutex_unlock(&opts->lock); - return ret; -} - -static struct f_ss_opts_attribute f_ss_opts_int_maxburst = - __CONFIGFS_ATTR(int_maxburst, S_IRUGO | S_IWUSR, - f_ss_opts_int_maxburst_show, - f_ss_opts_int_maxburst_store); - static struct configfs_attribute *ss_attrs[] = { &f_ss_opts_pattern.attr, &f_ss_opts_isoc_interval.attr, @@ -1651,10 +1186,6 @@ static struct configfs_attribute *ss_attrs[] = { &f_ss_opts_isoc_mult.attr, &f_ss_opts_isoc_maxburst.attr, &f_ss_opts_bulk_buflen.attr, - &f_ss_opts_int_interval.attr, - &f_ss_opts_int_maxpacket.attr, - &f_ss_opts_int_mult.attr, - &f_ss_opts_int_maxburst.attr, NULL, }; @@ -1684,8 +1215,6 @@ static struct usb_function_instance *source_sink_alloc_inst(void) ss_opts->isoc_interval = GZERO_ISOC_INTERVAL; ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET; ss_opts->bulk_buflen = GZERO_BULK_BUFLEN; - ss_opts->int_interval = GZERO_INT_INTERVAL; - ss_opts->int_maxpacket = GZERO_INT_MAXPACKET; config_group_init_type_name(&ss_opts->func_inst.group, "", &ss_func_type); diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 33e1665..6d3eb8b 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -54,7 +54,7 @@ #define UNFLW_CTRL 8 #define OVFLW_CTRL 10 -const char *uac2_name = "snd_uac2"; +static const char *uac2_name = "snd_uac2"; struct uac2_req { struct uac2_rtd_params *pp; /* parent param */ @@ -634,7 +634,7 @@ static struct usb_interface_descriptor std_ac_if_desc = { }; /* Clock source for IN traffic */ -struct uac_clock_source_descriptor in_clk_src_desc = { +static struct uac_clock_source_descriptor in_clk_src_desc = { .bLength = sizeof in_clk_src_desc, .bDescriptorType = USB_DT_CS_INTERFACE, @@ -646,7 +646,7 @@ struct uac_clock_source_descriptor in_clk_src_desc = { }; /* Clock source for OUT traffic */ -struct uac_clock_source_descriptor out_clk_src_desc = { +static struct uac_clock_source_descriptor out_clk_src_desc = { .bLength = sizeof out_clk_src_desc, .bDescriptorType = USB_DT_CS_INTERFACE, @@ -658,7 +658,7 @@ struct uac_clock_source_descriptor out_clk_src_desc = { }; /* Input Terminal for USB_OUT */ -struct uac2_input_terminal_descriptor usb_out_it_desc = { +static struct uac2_input_terminal_descriptor usb_out_it_desc = { .bLength = sizeof usb_out_it_desc, .bDescriptorType = USB_DT_CS_INTERFACE, @@ -672,7 +672,7 @@ struct uac2_input_terminal_descriptor usb_out_it_desc = { }; /* Input Terminal for I/O-In */ -struct uac2_input_terminal_descriptor io_in_it_desc = { +static struct uac2_input_terminal_descriptor io_in_it_desc = { .bLength = sizeof io_in_it_desc, .bDescriptorType = USB_DT_CS_INTERFACE, @@ -686,7 +686,7 @@ struct uac2_input_terminal_descriptor io_in_it_desc = { }; /* Ouput Terminal for USB_IN */ -struct uac2_output_terminal_descriptor usb_in_ot_desc = { +static struct uac2_output_terminal_descriptor usb_in_ot_desc = { .bLength = sizeof usb_in_ot_desc, .bDescriptorType = USB_DT_CS_INTERFACE, @@ -700,7 +700,7 @@ struct uac2_output_terminal_descriptor usb_in_ot_desc = { }; /* Ouput Terminal for I/O-Out */ -struct uac2_output_terminal_descriptor io_out_ot_desc = { +static struct uac2_output_terminal_descriptor io_out_ot_desc = { .bLength = sizeof io_out_ot_desc, .bDescriptorType = USB_DT_CS_INTERFACE, @@ -713,7 +713,7 @@ struct uac2_output_terminal_descriptor io_out_ot_desc = { .bmControls = (CONTROL_RDWR << COPY_CTRL), }; -struct uac2_ac_header_descriptor ac_hdr_desc = { +static struct uac2_ac_header_descriptor ac_hdr_desc = { .bLength = sizeof ac_hdr_desc, .bDescriptorType = USB_DT_CS_INTERFACE, @@ -751,7 +751,7 @@ static struct usb_interface_descriptor std_as_out_if1_desc = { }; /* Audio Stream OUT Intface Desc */ -struct uac2_as_header_descriptor as_out_hdr_desc = { +static struct uac2_as_header_descriptor as_out_hdr_desc = { .bLength = sizeof as_out_hdr_desc, .bDescriptorType = USB_DT_CS_INTERFACE, @@ -764,7 +764,7 @@ struct uac2_as_header_descriptor as_out_hdr_desc = { }; /* Audio USB_OUT Format */ -struct uac2_format_type_i_descriptor as_out_fmt1_desc = { +static struct uac2_format_type_i_descriptor as_out_fmt1_desc = { .bLength = sizeof as_out_fmt1_desc, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_FORMAT_TYPE, @@ -772,7 +772,7 @@ struct uac2_format_type_i_descriptor as_out_fmt1_desc = { }; /* STD AS ISO OUT Endpoint */ -struct usb_endpoint_descriptor fs_epout_desc = { +static struct usb_endpoint_descriptor fs_epout_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -782,7 +782,7 @@ struct usb_endpoint_descriptor fs_epout_desc = { .bInterval = 1, }; -struct usb_endpoint_descriptor hs_epout_desc = { +static struct usb_endpoint_descriptor hs_epout_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -828,7 +828,7 @@ static struct usb_interface_descriptor std_as_in_if1_desc = { }; /* Audio Stream IN Intface Desc */ -struct uac2_as_header_descriptor as_in_hdr_desc = { +static struct uac2_as_header_descriptor as_in_hdr_desc = { .bLength = sizeof as_in_hdr_desc, .bDescriptorType = USB_DT_CS_INTERFACE, @@ -841,7 +841,7 @@ struct uac2_as_header_descriptor as_in_hdr_desc = { }; /* Audio USB_IN Format */ -struct uac2_format_type_i_descriptor as_in_fmt1_desc = { +static struct uac2_format_type_i_descriptor as_in_fmt1_desc = { .bLength = sizeof as_in_fmt1_desc, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_FORMAT_TYPE, @@ -849,7 +849,7 @@ struct uac2_format_type_i_descriptor as_in_fmt1_desc = { }; /* STD AS ISO IN Endpoint */ -struct usb_endpoint_descriptor fs_epin_desc = { +static struct usb_endpoint_descriptor fs_epin_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -859,7 +859,7 @@ struct usb_endpoint_descriptor fs_epin_desc = { .bInterval = 1, }; -struct usb_endpoint_descriptor hs_epin_desc = { +static struct usb_endpoint_descriptor hs_epin_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -1563,7 +1563,7 @@ static void afunc_unbind(struct usb_configuration *c, struct usb_function *f) agdev->out_ep->driver_data = NULL; } -struct usb_function *afunc_alloc(struct usb_function_instance *fi) +static struct usb_function *afunc_alloc(struct usb_function_instance *fi) { struct audio_dev *agdev; struct f_uac2_opts *opts; diff --git a/drivers/usb/gadget/function/g_zero.h b/drivers/usb/gadget/function/g_zero.h index 2ce28b9d..15f1809 100644 --- a/drivers/usb/gadget/function/g_zero.h +++ b/drivers/usb/gadget/function/g_zero.h @@ -10,8 +10,6 @@ #define GZERO_QLEN 32 #define GZERO_ISOC_INTERVAL 4 #define GZERO_ISOC_MAXPACKET 1024 -#define GZERO_INT_INTERVAL 1 /* Default interrupt interval = 1 ms */ -#define GZERO_INT_MAXPACKET 1024 struct usb_zero_options { unsigned pattern; @@ -19,10 +17,6 @@ struct usb_zero_options { unsigned isoc_maxpacket; unsigned isoc_mult; unsigned isoc_maxburst; - unsigned int_interval; /* In ms */ - unsigned int_maxpacket; - unsigned int_mult; - unsigned int_maxburst; unsigned bulk_buflen; unsigned qlen; }; @@ -34,10 +28,6 @@ struct f_ss_opts { unsigned isoc_maxpacket; unsigned isoc_mult; unsigned isoc_maxburst; - unsigned int_interval; /* In ms */ - unsigned int_maxpacket; - unsigned int_mult; - unsigned int_maxburst; unsigned bulk_buflen; /* @@ -72,7 +62,6 @@ int lb_modinit(void); void free_ep_req(struct usb_ep *ep, struct usb_request *req); void disable_endpoints(struct usb_composite_dev *cdev, struct usb_ep *in, struct usb_ep *out, - struct usb_ep *iso_in, struct usb_ep *iso_out, - struct usb_ep *int_in, struct usb_ep *int_out); + struct usb_ep *iso_in, struct usb_ep *iso_out); #endif /* __G_ZERO_H */ diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 5aad7fe..8b818fd 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -27,6 +27,7 @@ #include "uvc.h" #include "uvc_queue.h" #include "uvc_video.h" +#include "uvc_v4l2.h" /* -------------------------------------------------------------------------- * Requests handling diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 9cb86bc..50a5e63 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -21,6 +21,7 @@ #include "uvc.h" #include "uvc_queue.h" +#include "uvc_video.h" /* -------------------------------------------------------------------------- * Video codecs diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c index 06acfa5..b01b88e 100644 --- a/drivers/usb/gadget/legacy/g_ffs.c +++ b/drivers/usb/gadget/legacy/g_ffs.c @@ -133,7 +133,9 @@ struct gfs_configuration { struct usb_configuration c; int (*eth)(struct usb_configuration *c); int num; -} gfs_configurations[] = { +}; + +static struct gfs_configuration gfs_configurations[] = { #ifdef CONFIG_USB_FUNCTIONFS_RNDIS { .eth = bind_rndis_config, @@ -278,7 +280,7 @@ static void *functionfs_acquire_dev(struct ffs_dev *dev) if (!try_module_get(THIS_MODULE)) return ERR_PTR(-ENOENT); - return 0; + return NULL; } static void functionfs_release_dev(struct ffs_dev *dev) diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index db49ec4..200f9a5 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -74,6 +74,8 @@ MODULE_DESCRIPTION (DRIVER_DESC); MODULE_AUTHOR ("David Brownell"); MODULE_LICENSE ("GPL"); +static int ep_open(struct inode *, struct file *); + /*----------------------------------------------------------------------*/ @@ -283,14 +285,15 @@ static void epio_complete (struct usb_ep *ep, struct usb_request *req) * still need dev->lock to use epdata->ep. */ static int -get_ready_ep (unsigned f_flags, struct ep_data *epdata) +get_ready_ep (unsigned f_flags, struct ep_data *epdata, bool is_write) { int val; if (f_flags & O_NONBLOCK) { if (!mutex_trylock(&epdata->lock)) goto nonblock; - if (epdata->state != STATE_EP_ENABLED) { + if (epdata->state != STATE_EP_ENABLED && + (!is_write || epdata->state != STATE_EP_READY)) { mutex_unlock(&epdata->lock); nonblock: val = -EAGAIN; @@ -305,18 +308,20 @@ nonblock: switch (epdata->state) { case STATE_EP_ENABLED: + return 0; + case STATE_EP_READY: /* not configured yet */ + if (is_write) + return 0; + // FALLTHRU + case STATE_EP_UNBOUND: /* clean disconnect */ break; // case STATE_EP_DISABLED: /* "can't happen" */ - // case STATE_EP_READY: /* "can't happen" */ default: /* error! */ pr_debug ("%s: ep %p not available, state %d\n", shortname, epdata, epdata->state); - // FALLTHROUGH - case STATE_EP_UNBOUND: /* clean disconnect */ - val = -ENODEV; - mutex_unlock(&epdata->lock); } - return val; + mutex_unlock(&epdata->lock); + return -ENODEV; } static ssize_t @@ -363,97 +368,6 @@ ep_io (struct ep_data *epdata, void *buf, unsigned len) return value; } - -/* handle a synchronous OUT bulk/intr/iso transfer */ -static ssize_t -ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr) -{ - struct ep_data *data = fd->private_data; - void *kbuf; - ssize_t value; - - if ((value = get_ready_ep (fd->f_flags, data)) < 0) - return value; - - /* halt any endpoint by doing a "wrong direction" i/o call */ - if (usb_endpoint_dir_in(&data->desc)) { - if (usb_endpoint_xfer_isoc(&data->desc)) { - mutex_unlock(&data->lock); - return -EINVAL; - } - DBG (data->dev, "%s halt\n", data->name); - spin_lock_irq (&data->dev->lock); - if (likely (data->ep != NULL)) - usb_ep_set_halt (data->ep); - spin_unlock_irq (&data->dev->lock); - mutex_unlock(&data->lock); - return -EBADMSG; - } - - /* FIXME readahead for O_NONBLOCK and poll(); careful with ZLPs */ - - value = -ENOMEM; - kbuf = kmalloc (len, GFP_KERNEL); - if (unlikely (!kbuf)) - goto free1; - - value = ep_io (data, kbuf, len); - VDEBUG (data->dev, "%s read %zu OUT, status %d\n", - data->name, len, (int) value); - if (value >= 0 && copy_to_user (buf, kbuf, value)) - value = -EFAULT; - -free1: - mutex_unlock(&data->lock); - kfree (kbuf); - return value; -} - -/* handle a synchronous IN bulk/intr/iso transfer */ -static ssize_t -ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) -{ - struct ep_data *data = fd->private_data; - void *kbuf; - ssize_t value; - - if ((value = get_ready_ep (fd->f_flags, data)) < 0) - return value; - - /* halt any endpoint by doing a "wrong direction" i/o call */ - if (!usb_endpoint_dir_in(&data->desc)) { - if (usb_endpoint_xfer_isoc(&data->desc)) { - mutex_unlock(&data->lock); - return -EINVAL; - } - DBG (data->dev, "%s halt\n", data->name); - spin_lock_irq (&data->dev->lock); - if (likely (data->ep != NULL)) - usb_ep_set_halt (data->ep); - spin_unlock_irq (&data->dev->lock); - mutex_unlock(&data->lock); - return -EBADMSG; - } - - /* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */ - - value = -ENOMEM; - kbuf = memdup_user(buf, len); - if (IS_ERR(kbuf)) { - value = PTR_ERR(kbuf); - kbuf = NULL; - goto free1; - } - - value = ep_io (data, kbuf, len); - VDEBUG (data->dev, "%s write %zu IN, status %d\n", - data->name, len, (int) value); -free1: - mutex_unlock(&data->lock); - kfree (kbuf); - return value; -} - static int ep_release (struct inode *inode, struct file *fd) { @@ -481,7 +395,7 @@ static long ep_ioctl(struct file *fd, unsigned code, unsigned long value) struct ep_data *data = fd->private_data; int status; - if ((status = get_ready_ep (fd->f_flags, data)) < 0) + if ((status = get_ready_ep (fd->f_flags, data, false)) < 0) return status; spin_lock_irq (&data->dev->lock); @@ -517,8 +431,8 @@ struct kiocb_priv { struct mm_struct *mm; struct work_struct work; void *buf; - const struct iovec *iv; - unsigned long nr_segs; + struct iov_iter to; + const void *to_free; unsigned actual; }; @@ -541,35 +455,6 @@ static int ep_aio_cancel(struct kiocb *iocb) return value; } -static ssize_t ep_copy_to_user(struct kiocb_priv *priv) -{ - ssize_t len, total; - void *to_copy; - int i; - - /* copy stuff into user buffers */ - total = priv->actual; - len = 0; - to_copy = priv->buf; - for (i=0; i < priv->nr_segs; i++) { - ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total); - - if (copy_to_user(priv->iv[i].iov_base, to_copy, this)) { - if (len == 0) - len = -EFAULT; - break; - } - - total -= this; - len += this; - to_copy += this; - if (total == 0) - break; - } - - return len; -} - static void ep_user_copy_worker(struct work_struct *work) { struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work); @@ -578,13 +463,16 @@ static void ep_user_copy_worker(struct work_struct *work) size_t ret; use_mm(mm); - ret = ep_copy_to_user(priv); + ret = copy_to_iter(priv->buf, priv->actual, &priv->to); unuse_mm(mm); + if (!ret) + ret = -EFAULT; /* completing the iocb can drop the ctx and mm, don't touch mm after */ aio_complete(iocb, ret, ret); kfree(priv->buf); + kfree(priv->to_free); kfree(priv); } @@ -603,8 +491,9 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) * don't need to copy anything to userspace, so we can * complete the aio request immediately. */ - if (priv->iv == NULL || unlikely(req->actual == 0)) { + if (priv->to_free == NULL || unlikely(req->actual == 0)) { kfree(req->buf); + kfree(priv->to_free); kfree(priv); iocb->private = NULL; /* aio_complete() reports bytes-transferred _and_ faults */ @@ -618,6 +507,7 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) priv->buf = req->buf; priv->actual = req->actual; + INIT_WORK(&priv->work, ep_user_copy_worker); schedule_work(&priv->work); } spin_unlock(&epdata->dev->lock); @@ -626,38 +516,17 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) put_ep(epdata); } -static ssize_t -ep_aio_rwtail( - struct kiocb *iocb, - char *buf, - size_t len, - struct ep_data *epdata, - const struct iovec *iv, - unsigned long nr_segs -) +static ssize_t ep_aio(struct kiocb *iocb, + struct kiocb_priv *priv, + struct ep_data *epdata, + char *buf, + size_t len) { - struct kiocb_priv *priv; - struct usb_request *req; - ssize_t value; + struct usb_request *req; + ssize_t value; - priv = kmalloc(sizeof *priv, GFP_KERNEL); - if (!priv) { - value = -ENOMEM; -fail: - kfree(buf); - return value; - } iocb->private = priv; priv->iocb = iocb; - priv->iv = iv; - priv->nr_segs = nr_segs; - INIT_WORK(&priv->work, ep_user_copy_worker); - - value = get_ready_ep(iocb->ki_filp->f_flags, epdata); - if (unlikely(value < 0)) { - kfree(priv); - goto fail; - } kiocb_set_cancel_fn(iocb, ep_aio_cancel); get_ep(epdata); @@ -669,75 +538,154 @@ fail: * allocate or submit those if the host disconnected. */ spin_lock_irq(&epdata->dev->lock); - if (likely(epdata->ep)) { - req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC); - if (likely(req)) { - priv->req = req; - req->buf = buf; - req->length = len; - req->complete = ep_aio_complete; - req->context = iocb; - value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC); - if (unlikely(0 != value)) - usb_ep_free_request(epdata->ep, req); - } else - value = -EAGAIN; - } else - value = -ENODEV; - spin_unlock_irq(&epdata->dev->lock); + value = -ENODEV; + if (unlikely(epdata->ep)) + goto fail; - mutex_unlock(&epdata->lock); + req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC); + value = -ENOMEM; + if (unlikely(!req)) + goto fail; - if (unlikely(value)) { - kfree(priv); - put_ep(epdata); - } else - value = -EIOCBQUEUED; + priv->req = req; + req->buf = buf; + req->length = len; + req->complete = ep_aio_complete; + req->context = iocb; + value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC); + if (unlikely(0 != value)) { + usb_ep_free_request(epdata->ep, req); + goto fail; + } + spin_unlock_irq(&epdata->dev->lock); + return -EIOCBQUEUED; + +fail: + spin_unlock_irq(&epdata->dev->lock); + kfree(priv->to_free); + kfree(priv); + put_ep(epdata); return value; } static ssize_t -ep_aio_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t o) +ep_read_iter(struct kiocb *iocb, struct iov_iter *to) { - struct ep_data *epdata = iocb->ki_filp->private_data; - char *buf; + struct file *file = iocb->ki_filp; + struct ep_data *epdata = file->private_data; + size_t len = iov_iter_count(to); + ssize_t value; + char *buf; - if (unlikely(usb_endpoint_dir_in(&epdata->desc))) - return -EINVAL; + if ((value = get_ready_ep(file->f_flags, epdata, false)) < 0) + return value; - buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL); - if (unlikely(!buf)) - return -ENOMEM; + /* halt any endpoint by doing a "wrong direction" i/o call */ + if (usb_endpoint_dir_in(&epdata->desc)) { + if (usb_endpoint_xfer_isoc(&epdata->desc) || + !is_sync_kiocb(iocb)) { + mutex_unlock(&epdata->lock); + return -EINVAL; + } + DBG (epdata->dev, "%s halt\n", epdata->name); + spin_lock_irq(&epdata->dev->lock); + if (likely(epdata->ep != NULL)) + usb_ep_set_halt(epdata->ep); + spin_unlock_irq(&epdata->dev->lock); + mutex_unlock(&epdata->lock); + return -EBADMSG; + } - return ep_aio_rwtail(iocb, buf, iocb->ki_nbytes, epdata, iov, nr_segs); + buf = kmalloc(len, GFP_KERNEL); + if (unlikely(!buf)) { + mutex_unlock(&epdata->lock); + return -ENOMEM; + } + if (is_sync_kiocb(iocb)) { + value = ep_io(epdata, buf, len); + if (value >= 0 && copy_to_iter(buf, value, to)) + value = -EFAULT; + } else { + struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL); + value = -ENOMEM; + if (!priv) + goto fail; + priv->to_free = dup_iter(&priv->to, to, GFP_KERNEL); + if (!priv->to_free) { + kfree(priv); + goto fail; + } + value = ep_aio(iocb, priv, epdata, buf, len); + if (value == -EIOCBQUEUED) + buf = NULL; + } +fail: + kfree(buf); + mutex_unlock(&epdata->lock); + return value; } +static ssize_t ep_config(struct ep_data *, const char *, size_t); + static ssize_t -ep_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t o) +ep_write_iter(struct kiocb *iocb, struct iov_iter *from) { - struct ep_data *epdata = iocb->ki_filp->private_data; - char *buf; - size_t len = 0; - int i = 0; + struct file *file = iocb->ki_filp; + struct ep_data *epdata = file->private_data; + size_t len = iov_iter_count(from); + bool configured; + ssize_t value; + char *buf; + + if ((value = get_ready_ep(file->f_flags, epdata, true)) < 0) + return value; - if (unlikely(!usb_endpoint_dir_in(&epdata->desc))) - return -EINVAL; + configured = epdata->state == STATE_EP_ENABLED; - buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL); - if (unlikely(!buf)) + /* halt any endpoint by doing a "wrong direction" i/o call */ + if (configured && !usb_endpoint_dir_in(&epdata->desc)) { + if (usb_endpoint_xfer_isoc(&epdata->desc) || + !is_sync_kiocb(iocb)) { + mutex_unlock(&epdata->lock); + return -EINVAL; + } + DBG (epdata->dev, "%s halt\n", epdata->name); + spin_lock_irq(&epdata->dev->lock); + if (likely(epdata->ep != NULL)) + usb_ep_set_halt(epdata->ep); + spin_unlock_irq(&epdata->dev->lock); + mutex_unlock(&epdata->lock); + return -EBADMSG; + } + + buf = kmalloc(len, GFP_KERNEL); + if (unlikely(!buf)) { + mutex_unlock(&epdata->lock); return -ENOMEM; + } - for (i=0; i < nr_segs; i++) { - if (unlikely(copy_from_user(&buf[len], iov[i].iov_base, - iov[i].iov_len) != 0)) { - kfree(buf); - return -EFAULT; + if (unlikely(copy_from_iter(buf, len, from) != len)) { + value = -EFAULT; + goto out; + } + + if (unlikely(!configured)) { + value = ep_config(epdata, buf, len); + } else if (is_sync_kiocb(iocb)) { + value = ep_io(epdata, buf, len); + } else { + struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL); + value = -ENOMEM; + if (priv) { + value = ep_aio(iocb, priv, epdata, buf, len); + if (value == -EIOCBQUEUED) + buf = NULL; } - len += iov[i].iov_len; } - return ep_aio_rwtail(iocb, buf, len, epdata, NULL, 0); +out: + kfree(buf); + mutex_unlock(&epdata->lock); + return value; } /*----------------------------------------------------------------------*/ @@ -745,15 +693,15 @@ ep_aio_write(struct kiocb *iocb, const struct iovec *iov, /* used after endpoint configuration */ static const struct file_operations ep_io_operations = { .owner = THIS_MODULE, - .llseek = no_llseek, - .read = ep_read, - .write = ep_write, - .unlocked_ioctl = ep_ioctl, + .open = ep_open, .release = ep_release, - - .aio_read = ep_aio_read, - .aio_write = ep_aio_write, + .llseek = no_llseek, + .read = new_sync_read, + .write = new_sync_write, + .unlocked_ioctl = ep_ioctl, + .read_iter = ep_read_iter, + .write_iter = ep_write_iter, }; /* ENDPOINT INITIALIZATION @@ -770,17 +718,12 @@ static const struct file_operations ep_io_operations = { * speed descriptor, then optional high speed descriptor. */ static ssize_t -ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) +ep_config (struct ep_data *data, const char *buf, size_t len) { - struct ep_data *data = fd->private_data; struct usb_ep *ep; u32 tag; int value, length = len; - value = mutex_lock_interruptible(&data->lock); - if (value < 0) - return value; - if (data->state != STATE_EP_READY) { value = -EL2HLT; goto fail; @@ -791,9 +734,7 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) goto fail0; /* we might need to change message format someday */ - if (copy_from_user (&tag, buf, 4)) { - goto fail1; - } + memcpy(&tag, buf, 4); if (tag != 1) { DBG(data->dev, "config %s, bad tag %d\n", data->name, tag); goto fail0; @@ -806,19 +747,15 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) */ /* full/low speed descriptor, then high speed */ - if (copy_from_user (&data->desc, buf, USB_DT_ENDPOINT_SIZE)) { - goto fail1; - } + memcpy(&data->desc, buf, USB_DT_ENDPOINT_SIZE); if (data->desc.bLength != USB_DT_ENDPOINT_SIZE || data->desc.bDescriptorType != USB_DT_ENDPOINT) goto fail0; if (len != USB_DT_ENDPOINT_SIZE) { if (len != 2 * USB_DT_ENDPOINT_SIZE) goto fail0; - if (copy_from_user (&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE, - USB_DT_ENDPOINT_SIZE)) { - goto fail1; - } + memcpy(&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE, + USB_DT_ENDPOINT_SIZE); if (data->hs_desc.bLength != USB_DT_ENDPOINT_SIZE || data->hs_desc.bDescriptorType != USB_DT_ENDPOINT) { @@ -840,24 +777,20 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) case USB_SPEED_LOW: case USB_SPEED_FULL: ep->desc = &data->desc; - value = usb_ep_enable(ep); - if (value == 0) - data->state = STATE_EP_ENABLED; break; case USB_SPEED_HIGH: /* fails if caller didn't provide that descriptor... */ ep->desc = &data->hs_desc; - value = usb_ep_enable(ep); - if (value == 0) - data->state = STATE_EP_ENABLED; break; default: DBG(data->dev, "unconnected, %s init abandoned\n", data->name); value = -EINVAL; + goto gone; } + value = usb_ep_enable(ep); if (value == 0) { - fd->f_op = &ep_io_operations; + data->state = STATE_EP_ENABLED; value = length; } gone: @@ -867,14 +800,10 @@ fail: data->desc.bDescriptorType = 0; data->hs_desc.bDescriptorType = 0; } - mutex_unlock(&data->lock); return value; fail0: value = -EINVAL; goto fail; -fail1: - value = -EFAULT; - goto fail; } static int @@ -902,15 +831,6 @@ ep_open (struct inode *inode, struct file *fd) return value; } -/* used before endpoint configuration */ -static const struct file_operations ep_config_operations = { - .llseek = no_llseek, - - .open = ep_open, - .write = ep_config, - .release = ep_release, -}; - /*----------------------------------------------------------------------*/ /* EP0 IMPLEMENTATION can be partly in userspace. @@ -989,6 +909,10 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr) enum ep0_state state; spin_lock_irq (&dev->lock); + if (dev->state <= STATE_DEV_OPENED) { + retval = -EINVAL; + goto done; + } /* report fd mode change before acting on it */ if (dev->setup_abort) { @@ -1187,8 +1111,6 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) struct dev_data *dev = fd->private_data; ssize_t retval = -ESRCH; - spin_lock_irq (&dev->lock); - /* report fd mode change before acting on it */ if (dev->setup_abort) { dev->setup_abort = 0; @@ -1234,7 +1156,6 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) } else DBG (dev, "fail %s, state %d\n", __func__, dev->state); - spin_unlock_irq (&dev->lock); return retval; } @@ -1281,6 +1202,9 @@ ep0_poll (struct file *fd, poll_table *wait) struct dev_data *dev = fd->private_data; int mask = 0; + if (dev->state <= STATE_DEV_OPENED) + return DEFAULT_POLLMASK; + poll_wait(fd, &dev->wait, wait); spin_lock_irq (&dev->lock); @@ -1316,19 +1240,6 @@ static long dev_ioctl (struct file *fd, unsigned code, unsigned long value) return ret; } -/* used after device configuration */ -static const struct file_operations ep0_io_operations = { - .owner = THIS_MODULE, - .llseek = no_llseek, - - .read = ep0_read, - .write = ep0_write, - .fasync = ep0_fasync, - .poll = ep0_poll, - .unlocked_ioctl = dev_ioctl, - .release = dev_release, -}; - /*----------------------------------------------------------------------*/ /* The in-kernel gadget driver handles most ep0 issues, in particular @@ -1650,7 +1561,7 @@ static int activate_ep_files (struct dev_data *dev) goto enomem1; data->dentry = gadgetfs_create_file (dev->sb, data->name, - data, &ep_config_operations); + data, &ep_io_operations); if (!data->dentry) goto enomem2; list_add_tail (&data->epfiles, &dev->epfiles); @@ -1852,6 +1763,14 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) u32 tag; char *kbuf; + spin_lock_irq(&dev->lock); + if (dev->state > STATE_DEV_OPENED) { + value = ep0_write(fd, buf, len, ptr); + spin_unlock_irq(&dev->lock); + return value; + } + spin_unlock_irq(&dev->lock); + if (len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) return -EINVAL; @@ -1925,7 +1844,6 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) * on, they can work ... except in cleanup paths that * kick in after the ep0 descriptor is closed. */ - fd->f_op = &ep0_io_operations; value = len; } return value; @@ -1956,12 +1874,14 @@ dev_open (struct inode *inode, struct file *fd) return value; } -static const struct file_operations dev_init_operations = { +static const struct file_operations ep0_operations = { .llseek = no_llseek, .open = dev_open, + .read = ep0_read, .write = dev_config, .fasync = ep0_fasync, + .poll = ep0_poll, .unlocked_ioctl = dev_ioctl, .release = dev_release, }; @@ -2077,7 +1997,7 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent) goto Enomem; dev->sb = sb; - dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &dev_init_operations); + dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations); if (!dev->dentry) { put_dev(dev); goto Enomem; diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c index 3a49416..6e0a019 100644 --- a/drivers/usb/gadget/legacy/tcm_usb_gadget.c +++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c @@ -1740,10 +1740,9 @@ static int tcm_usbg_make_nexus(struct usbg_tpg *tpg, char *name) goto err_session; } /* - * Now register the TCM vHost virtual I_T Nexus as active with the - * call to __transport_register_session() + * Now register the TCM vHost virtual I_T Nexus as active. */ - __transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl, + transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl, tv_nexus->tvn_se_sess, tv_nexus); tpg->tpg_nexus = tv_nexus; mutex_unlock(&tpg->tpg_mutex); diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c index ff97ac93..5ee9515 100644 --- a/drivers/usb/gadget/legacy/zero.c +++ b/drivers/usb/gadget/legacy/zero.c @@ -68,8 +68,6 @@ static struct usb_zero_options gzero_options = { .isoc_maxpacket = GZERO_ISOC_MAXPACKET, .bulk_buflen = GZERO_BULK_BUFLEN, .qlen = GZERO_QLEN, - .int_interval = GZERO_INT_INTERVAL, - .int_maxpacket = GZERO_INT_MAXPACKET, }; /*-------------------------------------------------------------------------*/ @@ -268,21 +266,6 @@ module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)"); -module_param_named(int_interval, gzero_options.int_interval, uint, - S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(int_interval, "1 - 16"); - -module_param_named(int_maxpacket, gzero_options.int_maxpacket, uint, - S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(int_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)"); - -module_param_named(int_mult, gzero_options.int_mult, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(int_mult, "0 - 2 (hs/ss only)"); - -module_param_named(int_maxburst, gzero_options.int_maxburst, uint, - S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(int_maxburst, "0 - 15 (ss only)"); - static struct usb_function *func_lb; static struct usb_function_instance *func_inst_lb; @@ -318,10 +301,6 @@ static int __init zero_bind(struct usb_composite_dev *cdev) ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket; ss_opts->isoc_mult = gzero_options.isoc_mult; ss_opts->isoc_maxburst = gzero_options.isoc_maxburst; - ss_opts->int_interval = gzero_options.int_interval; - ss_opts->int_maxpacket = gzero_options.int_maxpacket; - ss_opts->int_mult = gzero_options.int_mult; - ss_opts->int_maxburst = gzero_options.int_maxburst; ss_opts->bulk_buflen = gzero_options.bulk_buflen; func_ss = usb_get_function(func_inst_ss); diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 663f790..be0964a 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -34,7 +34,6 @@ static const char hcd_name[] = "ehci-atmel"; struct atmel_ehci_priv { struct clk *iclk; - struct clk *fclk; struct clk *uclk; bool clocked; }; @@ -51,12 +50,9 @@ static void atmel_start_clock(struct atmel_ehci_priv *atmel_ehci) { if (atmel_ehci->clocked) return; - if (IS_ENABLED(CONFIG_COMMON_CLK)) { - clk_set_rate(atmel_ehci->uclk, 48000000); - clk_prepare_enable(atmel_ehci->uclk); - } + + clk_prepare_enable(atmel_ehci->uclk); clk_prepare_enable(atmel_ehci->iclk); - clk_prepare_enable(atmel_ehci->fclk); atmel_ehci->clocked = true; } @@ -64,10 +60,9 @@ static void atmel_stop_clock(struct atmel_ehci_priv *atmel_ehci) { if (!atmel_ehci->clocked) return; - clk_disable_unprepare(atmel_ehci->fclk); + clk_disable_unprepare(atmel_ehci->iclk); - if (IS_ENABLED(CONFIG_COMMON_CLK)) - clk_disable_unprepare(atmel_ehci->uclk); + clk_disable_unprepare(atmel_ehci->uclk); atmel_ehci->clocked = false; } @@ -146,20 +141,13 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev) retval = -ENOENT; goto fail_request_resource; } - atmel_ehci->fclk = devm_clk_get(&pdev->dev, "uhpck"); - if (IS_ERR(atmel_ehci->fclk)) { - dev_err(&pdev->dev, "Error getting function clock\n"); - retval = -ENOENT; + + atmel_ehci->uclk = devm_clk_get(&pdev->dev, "usb_clk"); + if (IS_ERR(atmel_ehci->uclk)) { + dev_err(&pdev->dev, "failed to get uclk\n"); + retval = PTR_ERR(atmel_ehci->uclk); goto fail_request_resource; } - if (IS_ENABLED(CONFIG_COMMON_CLK)) { - atmel_ehci->uclk = devm_clk_get(&pdev->dev, "usb_clk"); - if (IS_ERR(atmel_ehci->uclk)) { - dev_err(&pdev->dev, "failed to get uclk\n"); - retval = PTR_ERR(atmel_ehci->uclk); - goto fail_request_resource; - } - } ehci = hcd_to_ehci(hcd); /* registers start at offset 0x0 */ diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index a7865c4..0827d7c 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -387,6 +387,10 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, status = PORT_PLC; port_change_bit = "link state"; break; + case USB_PORT_FEAT_C_PORT_CONFIG_ERROR: + status = PORT_CEC; + port_change_bit = "config error"; + break; default: /* Should never happen */ return; @@ -588,6 +592,8 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, status |= USB_PORT_STAT_C_LINK_STATE << 16; if ((raw_port_status & PORT_WRC)) status |= USB_PORT_STAT_C_BH_RESET << 16; + if ((raw_port_status & PORT_CEC)) + status |= USB_PORT_STAT_C_CONFIG_ERROR << 16; } if (hcd->speed != HCD_USB3) { @@ -1005,6 +1011,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_PORT_LINK_STATE: + case USB_PORT_FEAT_C_PORT_CONFIG_ERROR: xhci_clear_port_change_bit(xhci, wValue, wIndex, port_array[wIndex], temp); break; @@ -1069,7 +1076,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) */ status = bus_state->resuming_ports; - mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC; + mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC | PORT_CEC; spin_lock_irqsave(&xhci->lock, flags); /* For each port, did anything change? If so, set that bit in buf. */ diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 7f76c8a..2af32e2 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -37,6 +37,9 @@ #define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 +#define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5 +#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI 0xa12f +#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f static const char hcd_name[] = "xhci_hcd"; @@ -112,6 +115,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_INTEL) { xhci->quirks |= XHCI_LPM_SUPPORT; xhci->quirks |= XHCI_INTEL_HOST; + xhci->quirks |= XHCI_AVOID_BEI; } if (pdev->vendor == PCI_VENDOR_ID_INTEL && pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI) { @@ -127,12 +131,17 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) * PPT chipsets. */ xhci->quirks |= XHCI_SPURIOUS_REBOOT; - xhci->quirks |= XHCI_AVOID_BEI; } if (pdev->vendor == PCI_VENDOR_ID_INTEL && pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) { xhci->quirks |= XHCI_SPURIOUS_REBOOT; } + if (pdev->vendor == PCI_VENDOR_ID_INTEL && + (pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI)) { + xhci->quirks |= XHCI_PME_STUCK_QUIRK; + } if (pdev->vendor == PCI_VENDOR_ID_ETRON && pdev->device == PCI_DEVICE_ID_EJ168) { xhci->quirks |= XHCI_RESET_ON_RESUME; @@ -159,6 +168,21 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) "QUIRK: Resetting on resume"); } +/* + * Make sure PME works on some Intel xHCI controllers by writing 1 to clear + * the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4 + */ +static void xhci_pme_quirk(struct xhci_hcd *xhci) +{ + u32 val; + void __iomem *reg; + + reg = (void __iomem *) xhci->cap_regs + 0x80a4; + val = readl(reg); + writel(val | BIT(28), reg); + readl(reg); +} + /* called during probe() after chip reset completes */ static int xhci_pci_setup(struct usb_hcd *hcd) { @@ -283,6 +307,9 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) if (xhci->quirks & XHCI_COMP_MODE_QUIRK) pdev->no_d3cold = true; + if (xhci->quirks & XHCI_PME_STUCK_QUIRK) + xhci_pme_quirk(xhci); + return xhci_suspend(xhci, do_wakeup); } @@ -313,6 +340,9 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) if (pdev->vendor == PCI_VENDOR_ID_INTEL) usb_enable_intel_xhci_ports(pdev); + if (xhci->quirks & XHCI_PME_STUCK_QUIRK) + xhci_pme_quirk(xhci); + retval = xhci_resume(xhci, hibernated); return retval; } diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 08d402b..0e11d61 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -83,16 +83,6 @@ static int xhci_plat_probe(struct platform_device *pdev) if (irq < 0) return -ENODEV; - - if (of_device_is_compatible(pdev->dev.of_node, - "marvell,armada-375-xhci") || - of_device_is_compatible(pdev->dev.of_node, - "marvell,armada-380-xhci")) { - ret = xhci_mvebu_mbus_init_quirk(pdev); - if (ret) - return ret; - } - /* Initialize dma_mask and coherent_dma_mask to 32-bits */ ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); if (ret) @@ -127,6 +117,15 @@ static int xhci_plat_probe(struct platform_device *pdev) goto put_hcd; } + if (of_device_is_compatible(pdev->dev.of_node, + "marvell,armada-375-xhci") || + of_device_is_compatible(pdev->dev.of_node, + "marvell,armada-380-xhci")) { + ret = xhci_mvebu_mbus_init_quirk(pdev); + if (ret) + goto disable_clk; + } + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) goto disable_clk; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 88da8d6..73485fa 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1946,7 +1946,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, if (event_trb != ep_ring->dequeue) { /* The event was for the status stage */ if (event_trb == td->last_trb) { - if (td->urb->actual_length != 0) { + if (td->urb_length_set) { /* Don't overwrite a previously set error code */ if ((*status == -EINPROGRESS || *status == 0) && @@ -1960,7 +1960,13 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, td->urb->transfer_buffer_length; } } else { - /* Maybe the event was for the data stage? */ + /* + * Maybe the event was for the data stage? If so, update + * already the actual_length of the URB and flag it as + * set, so that it is not overwritten in the event for + * the last TRB. + */ + td->urb_length_set = true; td->urb->actual_length = td->urb->transfer_buffer_length - EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 9745147..8e421b8 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1,3 +1,4 @@ + /* * xHCI host controller driver * @@ -88,9 +89,10 @@ struct xhci_cap_regs { #define HCS_IST(p) (((p) >> 0) & 0xf) /* bits 4:7, max number of Event Ring segments */ #define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) +/* bits 21:25 Hi 5 bits of Scratchpad buffers SW must allocate for the HW */ /* bit 26 Scratchpad restore - for save/restore HW state - not used yet */ -/* bits 27:31 number of Scratchpad buffers SW must allocate for the HW */ -#define HCS_MAX_SCRATCHPAD(p) (((p) >> 27) & 0x1f) +/* bits 27:31 Lo 5 bits of Scratchpad buffers SW must allocate for the HW */ +#define HCS_MAX_SCRATCHPAD(p) ((((p) >> 16) & 0x3e0) | (((p) >> 27) & 0x1f)) /* HCSPARAMS3 - hcs_params3 - bitmasks */ /* bits 0:7, Max U1 to U0 latency for the roothub ports */ @@ -1288,6 +1290,8 @@ struct xhci_td { struct xhci_segment *start_seg; union xhci_trb *first_trb; union xhci_trb *last_trb; + /* actual_length of the URB has already been set */ + bool urb_length_set; }; /* xHCI command default timeout value */ @@ -1560,6 +1564,7 @@ struct xhci_hcd { #define XHCI_SPURIOUS_WAKEUP (1 << 18) /* For controllers with a broken beyond repair streams implementation */ #define XHCI_BROKEN_STREAMS (1 << 19) +#define XHCI_PME_STUCK_QUIRK (1 << 20) unsigned int num_active_eps; unsigned int limit_active_eps; /* There are two roothubs to keep track of bus suspend info for */ diff --git a/drivers/usb/isp1760/isp1760-core.c b/drivers/usb/isp1760/isp1760-core.c index b982755..bfa402c 100644 --- a/drivers/usb/isp1760/isp1760-core.c +++ b/drivers/usb/isp1760/isp1760-core.c @@ -151,8 +151,7 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, } if (IS_ENABLED(CONFIG_USB_ISP1761_UDC) && !udc_disabled) { - ret = isp1760_udc_register(isp, irq, irqflags | IRQF_SHARED | - IRQF_DISABLED); + ret = isp1760_udc_register(isp, irq, irqflags); if (ret < 0) { isp1760_hcd_unregister(&isp->hcd); return ret; diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c index eba9b82..3cb98b1 100644 --- a/drivers/usb/isp1760/isp1760-hcd.c +++ b/drivers/usb/isp1760/isp1760-hcd.c @@ -1274,7 +1274,7 @@ static void errata2_function(unsigned long data) for (slot = 0; slot < 32; slot++) if (priv->atl_slots[slot].qh && time_after(jiffies, priv->atl_slots[slot].timestamp + - SLOT_TIMEOUT * HZ / 1000)) { + msecs_to_jiffies(SLOT_TIMEOUT))) { ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); if (!FROM_DW0_VALID(ptd.dw0) && !FROM_DW3_ACTIVE(ptd.dw3)) @@ -1286,7 +1286,7 @@ static void errata2_function(unsigned long data) spin_unlock_irqrestore(&priv->lock, spinflags); - errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000; + errata2_timer.expires = jiffies + msecs_to_jiffies(SLOT_CHECK_PERIOD); add_timer(&errata2_timer); } @@ -1336,7 +1336,7 @@ static int isp1760_run(struct usb_hcd *hcd) return retval; setup_timer(&errata2_timer, errata2_function, (unsigned long)hcd); - errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000; + errata2_timer.expires = jiffies + msecs_to_jiffies(SLOT_CHECK_PERIOD); add_timer(&errata2_timer); chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG); diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c index 9612d79..3fc4fe7 100644 --- a/drivers/usb/isp1760/isp1760-udc.c +++ b/drivers/usb/isp1760/isp1760-udc.c @@ -1191,6 +1191,7 @@ static int isp1760_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { struct isp1760_udc *udc = gadget_to_udc(gadget); + unsigned long flags; /* The hardware doesn't support low speed. */ if (driver->max_speed < USB_SPEED_FULL) { @@ -1198,17 +1199,17 @@ static int isp1760_udc_start(struct usb_gadget *gadget, return -EINVAL; } - spin_lock(&udc->lock); + spin_lock_irqsave(&udc->lock, flags); if (udc->driver) { dev_err(udc->isp->dev, "UDC already has a gadget driver\n"); - spin_unlock(&udc->lock); + spin_unlock_irqrestore(&udc->lock, flags); return -EBUSY; } udc->driver = driver; - spin_unlock(&udc->lock); + spin_unlock_irqrestore(&udc->lock, flags); dev_dbg(udc->isp->dev, "starting UDC with driver %s\n", driver->function); @@ -1232,6 +1233,7 @@ static int isp1760_udc_start(struct usb_gadget *gadget, static int isp1760_udc_stop(struct usb_gadget *gadget) { struct isp1760_udc *udc = gadget_to_udc(gadget); + unsigned long flags; dev_dbg(udc->isp->dev, "%s\n", __func__); @@ -1239,9 +1241,9 @@ static int isp1760_udc_stop(struct usb_gadget *gadget) isp1760_udc_write(udc, DC_MODE, 0); - spin_lock(&udc->lock); + spin_lock_irqsave(&udc->lock, flags); udc->driver = NULL; - spin_unlock(&udc->lock); + spin_unlock_irqrestore(&udc->lock, flags); return 0; } @@ -1411,7 +1413,7 @@ static int isp1760_udc_init(struct isp1760_udc *udc) return -ENODEV; } - if (chipid != 0x00011582) { + if (chipid != 0x00011582 && chipid != 0x00158210) { dev_err(udc->isp->dev, "udc: invalid chip ID 0x%08x\n", chipid); return -ENODEV; } @@ -1451,8 +1453,8 @@ int isp1760_udc_register(struct isp1760_device *isp, int irq, sprintf(udc->irqname, "%s (udc)", devname); - ret = request_irq(irq, isp1760_udc_irq, IRQF_SHARED | IRQF_DISABLED | - irqflags, udc->irqname, udc); + ret = request_irq(irq, isp1760_udc_irq, IRQF_SHARED | irqflags, + udc->irqname, udc); if (ret < 0) goto error; diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 14e1628..39db8b6 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -79,7 +79,8 @@ config USB_MUSB_TUSB6010 config USB_MUSB_OMAP2PLUS tristate "OMAP2430 and onwards" - depends on ARCH_OMAP2PLUS && USB && OMAP_CONTROL_PHY + depends on ARCH_OMAP2PLUS && USB + depends on OMAP_CONTROL_PHY || !OMAP_CONTROL_PHY select GENERIC_PHY config USB_MUSB_AM35X diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index e6f4cbf..067920f 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1969,10 +1969,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) goto fail0; } - pm_runtime_use_autosuspend(musb->controller); - pm_runtime_set_autosuspend_delay(musb->controller, 200); - pm_runtime_enable(musb->controller); - spin_lock_init(&musb->lock); musb->board_set_power = plat->set_power; musb->min_power = plat->min_power; @@ -1991,6 +1987,12 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb_readl = musb_default_readl; musb_writel = musb_default_writel; + /* We need musb_read/write functions initialized for PM */ + pm_runtime_use_autosuspend(musb->controller); + pm_runtime_set_autosuspend_delay(musb->controller, 200); + pm_runtime_irq_safe(musb->controller); + pm_runtime_enable(musb->controller); + /* The musb_platform_init() call: * - adjusts musb->mregs * - sets the musb->isr diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 53bd0e7..a900c98 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -457,12 +457,27 @@ static int dsps_musb_init(struct musb *musb) if (IS_ERR(musb->xceiv)) return PTR_ERR(musb->xceiv); + musb->phy = devm_phy_get(dev->parent, "usb2-phy"); + /* Returns zero if e.g. not clocked */ rev = dsps_readl(reg_base, wrp->revision); if (!rev) return -ENODEV; usb_phy_init(musb->xceiv); + if (IS_ERR(musb->phy)) { + musb->phy = NULL; + } else { + ret = phy_init(musb->phy); + if (ret < 0) + return ret; + ret = phy_power_on(musb->phy); + if (ret) { + phy_exit(musb->phy); + return ret; + } + } + setup_timer(&glue->timer, otg_timer, (unsigned long) musb); /* Reset the musb */ @@ -502,6 +517,8 @@ static int dsps_musb_exit(struct musb *musb) del_timer_sync(&glue->timer); usb_phy_shutdown(musb->xceiv); + phy_power_off(musb->phy); + phy_exit(musb->phy); debugfs_remove_recursive(glue->dbgfs_root); return 0; @@ -610,7 +627,7 @@ static int dsps_musb_reset(struct musb *musb) struct device *dev = musb->controller; struct dsps_glue *glue = dev_get_drvdata(dev->parent); const struct dsps_musb_wrapper *wrp = glue->wrp; - int session_restart = 0; + int session_restart = 0, error; if (glue->sw_babble_enabled) session_restart = sw_babble_control(musb); @@ -624,8 +641,14 @@ static int dsps_musb_reset(struct musb *musb) dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset)); usleep_range(100, 200); usb_phy_shutdown(musb->xceiv); + error = phy_power_off(musb->phy); + if (error) + dev_err(dev, "phy shutdown failed: %i\n", error); usleep_range(100, 200); usb_phy_init(musb->xceiv); + error = phy_power_on(musb->phy); + if (error) + dev_err(dev, "phy powerup failed: %i\n", error); session_restart = 1; } @@ -687,7 +710,7 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, struct musb_hdrc_config *config; struct platform_device *musb; struct device_node *dn = parent->dev.of_node; - int ret; + int ret, val; memset(resources, 0, sizeof(resources)); res = platform_get_resource_byname(parent, IORESOURCE_MEM, "mc"); @@ -739,7 +762,10 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, pdata.mode = get_musb_port_mode(dev); /* DT keeps this entry in mA, musb expects it as per USB spec */ pdata.power = get_int_prop(dn, "mentor,power") / 2; - config->multipoint = of_property_read_bool(dn, "mentor,multipoint"); + + ret = of_property_read_u32(dn, "mentor,multipoint", &val); + if (!ret && val) + config->multipoint = true; ret = platform_device_add_data(musb, &pdata, sizeof(pdata)); if (ret) { diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 883a9ad..c3d5fc9 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2613,7 +2613,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, + .flags = HCD_USB2 | HCD_MEMORY | HCD_BH, /* 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/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 763649e..cc752d8 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -516,7 +516,7 @@ static int omap2430_probe(struct platform_device *pdev) struct omap2430_glue *glue; struct device_node *np = pdev->dev.of_node; struct musb_hdrc_config *config; - int ret = -ENOMEM; + int ret = -ENOMEM, val; glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) @@ -559,7 +559,10 @@ static int omap2430_probe(struct platform_device *pdev) of_property_read_u32(np, "num-eps", (u32 *)&config->num_eps); of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits); of_property_read_u32(np, "power", (u32 *)&pdata->power); - config->multipoint = of_property_read_bool(np, "multipoint"); + + ret = of_property_read_u32(np, "multipoint", &val); + if (!ret && val) + config->multipoint = true; pdata->board_data = data; pdata->config = config; diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 0b1bd23..f5b3b92 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -277,7 +277,7 @@ static void ab8500_usb_regulator_enable(struct ab8500_usb *ab) dev_err(ab->dev, "Failed to set the Vintcore to 1.3V, ret=%d\n", ret); - ret = regulator_set_optimum_mode(ab->v_ulpi, 28000); + ret = regulator_set_load(ab->v_ulpi, 28000); if (ret < 0) dev_err(ab->dev, "Failed to set optimum mode (ret=%d)\n", ret); @@ -317,7 +317,7 @@ static void ab8500_usb_regulator_disable(struct ab8500_usb *ab) ab->saved_v_ulpi, ret); } - ret = regulator_set_optimum_mode(ab->v_ulpi, 0); + ret = regulator_set_load(ab->v_ulpi, 0); if (ret < 0) dev_err(ab->dev, "Failed to set optimum mode (ret=%d)\n", ret); diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c index 403fab7..7b3035f 100644 --- a/drivers/usb/phy/phy-am335x-control.c +++ b/drivers/usb/phy/phy-am335x-control.c @@ -126,6 +126,9 @@ struct phy_control *am335x_get_phy_control(struct device *dev) return NULL; dev = bus_find_device(&platform_bus_type, NULL, node, match); + if (!dev) + return NULL; + ctrl_usb = dev_get_drvdata(dev); if (!ctrl_usb) return NULL; diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 000fd89..6ed67ea 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -142,27 +142,22 @@ static int msm_hsusb_ldo_set_mode(struct msm_otg *motg, int on) int ret = 0; if (on) { - ret = regulator_set_optimum_mode(motg->v1p8, - USB_PHY_1P8_HPM_LOAD); + ret = regulator_set_load(motg->v1p8, USB_PHY_1P8_HPM_LOAD); if (ret < 0) { pr_err("Could not set HPM for v1p8\n"); return ret; } - ret = regulator_set_optimum_mode(motg->v3p3, - USB_PHY_3P3_HPM_LOAD); + ret = regulator_set_load(motg->v3p3, USB_PHY_3P3_HPM_LOAD); if (ret < 0) { pr_err("Could not set HPM for v3p3\n"); - regulator_set_optimum_mode(motg->v1p8, - USB_PHY_1P8_LPM_LOAD); + regulator_set_load(motg->v1p8, USB_PHY_1P8_LPM_LOAD); return ret; } } else { - ret = regulator_set_optimum_mode(motg->v1p8, - USB_PHY_1P8_LPM_LOAD); + ret = regulator_set_load(motg->v1p8, USB_PHY_1P8_LPM_LOAD); if (ret < 0) pr_err("Could not set LPM for v1p8\n"); - ret = regulator_set_optimum_mode(motg->v3p3, - USB_PHY_3P3_LPM_LOAD); + ret = regulator_set_load(motg->v3p3, USB_PHY_3P3_LPM_LOAD); if (ret < 0) pr_err("Could not set LPM for v3p3\n"); } diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig index de83b9d..ebc99ee 100644 --- a/drivers/usb/renesas_usbhs/Kconfig +++ b/drivers/usb/renesas_usbhs/Kconfig @@ -6,6 +6,7 @@ config USB_RENESAS_USBHS tristate 'Renesas USBHS controller' depends on USB_GADGET depends on ARCH_SHMOBILE || SUPERH || COMPILE_TEST + depends on EXTCON || !EXTCON # if EXTCON=m, USBHS cannot be built-in default n help Renesas USBHS is a discrete USB host and peripheral controller chip diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 9374bd2..8936a83 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -38,56 +38,51 @@ static int usb_serial_device_match(struct device *dev, return 0; } -static ssize_t port_number_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_serial_port *port = to_usb_serial_port(dev); - - return sprintf(buf, "%d\n", port->port_number); -} -static DEVICE_ATTR_RO(port_number); - static int usb_serial_device_probe(struct device *dev) { struct usb_serial_driver *driver; struct usb_serial_port *port; + struct device *tty_dev; int retval = 0; int minor; port = to_usb_serial_port(dev); - if (!port) { - retval = -ENODEV; - goto exit; - } + if (!port) + return -ENODEV; /* make sure suspend/resume doesn't race against port_probe */ retval = usb_autopm_get_interface(port->serial->interface); if (retval) - goto exit; + return retval; driver = port->serial->type; if (driver->port_probe) { retval = driver->port_probe(port); if (retval) - goto exit_with_autopm; + goto err_autopm_put; } - retval = device_create_file(dev, &dev_attr_port_number); - if (retval) { - if (driver->port_remove) - retval = driver->port_remove(port); - goto exit_with_autopm; + minor = port->minor; + tty_dev = tty_register_device(usb_serial_tty_driver, minor, dev); + if (IS_ERR(tty_dev)) { + retval = PTR_ERR(tty_dev); + goto err_port_remove; } - minor = port->minor; - tty_register_device(usb_serial_tty_driver, minor, dev); + usb_autopm_put_interface(port->serial->interface); + dev_info(&port->serial->dev->dev, "%s converter now attached to ttyUSB%d\n", driver->description, minor); -exit_with_autopm: + return 0; + +err_port_remove: + if (driver->port_remove) + driver->port_remove(port); +err_autopm_put: usb_autopm_put_interface(port->serial->interface); -exit: + return retval; } @@ -114,8 +109,6 @@ static int usb_serial_device_remove(struct device *dev) minor = port->minor; tty_unregister_device(usb_serial_tty_driver, minor); - device_remove_file(&port->dev, &dev_attr_port_number); - driver = port->serial->type; if (driver->port_remove) retval = driver->port_remove(port); diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 2d72aa3..ede4f5f 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -84,6 +84,10 @@ struct ch341_private { u8 line_status; /* active status of modem control inputs */ }; +static void ch341_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, + struct ktermios *old_termios); + static int ch341_control_out(struct usb_device *dev, u8 request, u16 value, u16 index) { @@ -309,19 +313,12 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port) struct ch341_private *priv = usb_get_serial_port_data(port); int r; - priv->baud_rate = DEFAULT_BAUD_RATE; - r = ch341_configure(serial->dev, priv); if (r) goto out; - r = ch341_set_handshake(serial->dev, priv->line_control); - if (r) - goto out; - - r = ch341_set_baudrate(serial->dev, priv); - if (r) - goto out; + if (tty) + ch341_set_termios(tty, port, NULL); dev_dbg(&port->dev, "%s - submitting interrupt urb\n", __func__); r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index 29fa1c3..3806e70 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -14,6 +14,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> +#include <linux/module.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/console.h> @@ -144,6 +145,7 @@ static int usb_console_setup(struct console *co, char *options) init_ldsem(&tty->ldisc_sem); INIT_LIST_HEAD(&tty->tty_files); kref_get(&tty->driver->kref); + __module_get(tty->driver->owner); tty->ops = &usb_console_fake_tty_ops; if (tty_init_termios(tty)) { retval = -ENOMEM; diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index f40c856..84ce2d7 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -147,6 +147,8 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x166A, 0x0305) }, /* Clipsal C-5000CT2 C-Bus Spectrum Colour Touchscreen */ { USB_DEVICE(0x166A, 0x0401) }, /* Clipsal L51xx C-Bus Architectural Dimmer */ { USB_DEVICE(0x166A, 0x0101) }, /* Clipsal 5560884 C-Bus Multi-room Audio Matrix Switcher */ + { USB_DEVICE(0x16C0, 0x09B0) }, /* Lunatico Seletek */ + { USB_DEVICE(0x16C0, 0x09B1) }, /* Lunatico Seletek */ { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */ { USB_DEVICE(0x16DC, 0x0010) }, /* W-IE-NE-R Plein & Baus GmbH PL512 Power Supply */ { USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */ diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 1ebb351..8eb68a3 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -604,6 +604,7 @@ static const struct usb_device_id id_table_combined[] = { .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLXM_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_SYNAPSE_SS200_PID) }, /* * ELV devices: */ @@ -799,6 +800,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_ELSTER_UNICOM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PROPOX_JTAGCABLEII_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) }, + { USB_DEVICE(FTDI_VID, CYBER_CORTEX_AV_PID), + .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID), @@ -978,6 +981,23 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_PID, 1) }, /* GE Healthcare devices */ { USB_DEVICE(GE_HEALTHCARE_VID, GE_HEALTHCARE_NEMO_TRACKER_PID) }, + /* Active Research (Actisense) devices */ + { USB_DEVICE(FTDI_VID, ACTISENSE_NDC_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_USG_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_NGT_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_NGW_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_D9AC_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_D9AD_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_D9AE_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_D9AF_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEAGAUGE_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEASWITCH_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_NMEA2000_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_ETHERNET_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_WIFI_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_DISPLAY_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_LITE_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_ANALOG_PID) }, { } /* Terminating entry */ }; @@ -1864,8 +1884,12 @@ static int ftdi_8u2232c_probe(struct usb_serial *serial) { struct usb_device *udev = serial->dev; - if ((udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems")) || - (udev->product && !strcmp(udev->product, "BeagleBone/XDS100V2"))) + if (udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems")) + return ftdi_jtag_probe(serial); + + if (udev->product && + (!strcmp(udev->product, "BeagleBone/XDS100V2") || + !strcmp(udev->product, "SNAP Connect E10"))) return ftdi_jtag_probe(serial); return 0; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index e52409c..4e4f46f 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -38,6 +38,9 @@ #define FTDI_LUMEL_PD12_PID 0x6002 +/* Cyber Cortex AV by Fabulous Silicon (http://fabuloussilicon.com) */ +#define CYBER_CORTEX_AV_PID 0x8698 + /* * Marvell OpenRD Base, Client * http://www.open-rd.org @@ -558,6 +561,12 @@ */ #define FTDI_NT_ORIONLXM_PID 0x7c90 /* OrionLXm Substation Automation Platform */ +/* + * Synapse Wireless product ids (FTDI_VID) + * http://www.synapse-wireless.com + */ +#define FTDI_SYNAPSE_SS200_PID 0x9090 /* SS200 - SNAP Stick 200 */ + /********************************/ /** third-party VID/PID combos **/ @@ -1438,3 +1447,23 @@ */ #define GE_HEALTHCARE_VID 0x1901 #define GE_HEALTHCARE_NEMO_TRACKER_PID 0x0015 + +/* + * Active Research (Actisense) devices + */ +#define ACTISENSE_NDC_PID 0xD9A8 /* NDC USB Serial Adapter */ +#define ACTISENSE_USG_PID 0xD9A9 /* USG USB Serial Adapter */ +#define ACTISENSE_NGT_PID 0xD9AA /* NGT NMEA2000 Interface */ +#define ACTISENSE_NGW_PID 0xD9AB /* NGW NMEA2000 Gateway */ +#define ACTISENSE_D9AC_PID 0xD9AC /* Actisense Reserved */ +#define ACTISENSE_D9AD_PID 0xD9AD /* Actisense Reserved */ +#define ACTISENSE_D9AE_PID 0xD9AE /* Actisense Reserved */ +#define ACTISENSE_D9AF_PID 0xD9AF /* Actisense Reserved */ +#define CHETCO_SEAGAUGE_PID 0xA548 /* SeaGauge USB Adapter */ +#define CHETCO_SEASWITCH_PID 0xA549 /* SeaSwitch USB Adapter */ +#define CHETCO_SEASMART_NMEA2000_PID 0xA54A /* SeaSmart NMEA2000 Gateway */ +#define CHETCO_SEASMART_ETHERNET_PID 0xA54B /* SeaSmart Ethernet Gateway */ +#define CHETCO_SEASMART_WIFI_PID 0xA5AC /* SeaSmart Wifi Gateway */ +#define CHETCO_SEASMART_DISPLAY_PID 0xA5AD /* SeaSmart NMEA2000 Display */ +#define CHETCO_SEASMART_LITE_PID 0xA5AE /* SeaSmart Lite USB Adapter */ +#define CHETCO_SEASMART_ANALOG_PID 0xA5AF /* SeaSmart Analog Adapter */ diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index ccf1df7..54e170d 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -258,7 +258,8 @@ void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout) * character or at least one jiffy. */ period = max_t(unsigned long, (10 * HZ / bps), 1); - period = min_t(unsigned long, period, timeout); + if (timeout) + period = min_t(unsigned long, period, timeout); dev_dbg(&port->dev, "%s - timeout = %u ms, period = %u ms\n", __func__, jiffies_to_msecs(timeout), @@ -268,7 +269,7 @@ void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout) schedule_timeout_interruptible(period); if (signal_pending(current)) break; - if (time_after(jiffies, expire)) + if (timeout && time_after(jiffies, expire)) break; } } diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index dd97d8b..4f7e072 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -61,6 +61,7 @@ struct keyspan_pda_private { /* For Xircom PGSDB9 and older Entrega version of the same device */ #define XIRCOM_VENDOR_ID 0x085a #define XIRCOM_FAKE_ID 0x8027 +#define XIRCOM_FAKE_ID_2 0x8025 /* "PGMFHUB" serial */ #define ENTREGA_VENDOR_ID 0x1645 #define ENTREGA_FAKE_ID 0x8093 @@ -70,6 +71,7 @@ static const struct usb_device_id id_table_combined[] = { #endif #ifdef XIRCOM { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) }, + { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) }, { USB_DEVICE(ENTREGA_VENDOR_ID, ENTREGA_FAKE_ID) }, #endif { USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_ID) }, @@ -93,6 +95,7 @@ static const struct usb_device_id id_table_fake[] = { #ifdef XIRCOM static const struct usb_device_id id_table_fake_xircom[] = { { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) }, + { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) }, { USB_DEVICE(ENTREGA_VENDOR_ID, ENTREGA_FAKE_ID) }, { } }; diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index ab1d690..460a406 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -1284,7 +1284,8 @@ static int mxuport_open(struct tty_struct *tty, struct usb_serial_port *port) } /* Initial port termios */ - mxuport_set_termios(tty, port, NULL); + if (tty) + mxuport_set_termios(tty, port, NULL); /* * TODO: use RQ_VENDOR_GET_MSR, once we know what it diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 0f872e6..829604d 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -132,6 +132,7 @@ MODULE_DEVICE_TABLE(usb, id_table); #define UART_OVERRUN_ERROR 0x40 #define UART_CTS 0x80 +static void pl2303_set_break(struct usb_serial_port *port, bool enable); enum pl2303_type { TYPE_01, /* Type 0 and 1 (difference unknown) */ @@ -615,6 +616,7 @@ static void pl2303_close(struct usb_serial_port *port) { usb_serial_generic_close(port); usb_kill_urb(port->interrupt_in_urb); + pl2303_set_break(port, false); } static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port) @@ -741,17 +743,16 @@ static int pl2303_ioctl(struct tty_struct *tty, return -ENOIOCTLCMD; } -static void pl2303_break_ctl(struct tty_struct *tty, int break_state) +static void pl2303_set_break(struct usb_serial_port *port, bool enable) { - struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; u16 state; int result; - if (break_state == 0) - state = BREAK_OFF; - else + if (enable) state = BREAK_ON; + else + state = BREAK_OFF; dev_dbg(&port->dev, "%s - turning break %s\n", __func__, state == BREAK_OFF ? "off" : "on"); @@ -763,6 +764,13 @@ static void pl2303_break_ctl(struct tty_struct *tty, int break_state) dev_err(&port->dev, "error sending break = %d\n", result); } +static void pl2303_break_ctl(struct tty_struct *tty, int state) +{ + struct usb_serial_port *port = tty->driver_data; + + pl2303_set_break(port, state); +} + static void pl2303_update_line_status(struct usb_serial_port *port, unsigned char *data, unsigned int actual_length) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 475723c..529066b 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -687,6 +687,21 @@ static void serial_port_dtr_rts(struct tty_port *port, int on) drv->dtr_rts(p, on); } +static ssize_t port_number_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_serial_port *port = to_usb_serial_port(dev); + + return sprintf(buf, "%u\n", port->port_number); +} +static DEVICE_ATTR_RO(port_number); + +static struct attribute *usb_serial_port_attrs[] = { + &dev_attr_port_number.attr, + NULL +}; +ATTRIBUTE_GROUPS(usb_serial_port); + static const struct tty_port_operations serial_port_ops = { .carrier_raised = serial_port_carrier_raised, .dtr_rts = serial_port_dtr_rts, @@ -902,6 +917,7 @@ static int usb_serial_probe(struct usb_interface *interface, port->dev.driver = NULL; port->dev.bus = &usb_serial_bus_type; port->dev.release = &usb_serial_port_release; + port->dev.groups = usb_serial_port_groups; device_initialize(&port->dev); } @@ -940,8 +956,9 @@ static int usb_serial_probe(struct usb_interface *interface, port = serial->port[i]; if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL)) goto probe_error; - buffer_size = max_t(int, serial->type->bulk_out_size, - usb_endpoint_maxp(endpoint)); + buffer_size = serial->type->bulk_out_size; + if (!buffer_size) + buffer_size = usb_endpoint_maxp(endpoint); port->bulk_out_size = buffer_size; port->bulk_out_endpointAddress = endpoint->bEndpointAddress; diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index dbc00e5..c85ea53 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -113,6 +113,20 @@ UNUSUAL_DEV(0x0bc2, 0xab2a, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_ATA_1X), +/* Reported-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> */ +UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x9999, + "Initio Corporation", + "", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X), + +/* Reported-by: Tom Arild Naess <tanaess@gmail.com> */ +UNUSUAL_DEV(0x152d, 0x0539, 0x0000, 0x9999, + "JMicron", + "JMS539", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES), + /* Reported-by: Claudio Bizzarri <claudio.bizzarri@gmail.com> */ UNUSUAL_DEV(0x152d, 0x0567, 0x0000, 0x9999, "JMicron", diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index d468d02..5600c33 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -889,6 +889,12 @@ static void usb_stor_scan_dwork(struct work_struct *work) !(us->fflags & US_FL_SCM_MULT_TARG)) { mutex_lock(&us->dev_mutex); us->max_lun = usb_stor_Bulk_max_lun(us); + /* + * Allow proper scanning of devices that present more than 8 LUNs + * While not affecting other devices that may need the previous behavior + */ + if (us->max_lun >= 8) + us_to_host(us)->max_lun = us->max_lun+1; mutex_unlock(&us->dev_mutex); } scsi_scan_host(us_to_host(us)); diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c index f88bfdf..2027a27 100644 --- a/drivers/vfio/pci/vfio_pci_intrs.c +++ b/drivers/vfio/pci/vfio_pci_intrs.c @@ -868,12 +868,14 @@ int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags, func = vfio_pci_set_err_trigger; break; } + break; case VFIO_PCI_REQ_IRQ_INDEX: switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { case VFIO_IRQ_SET_ACTION_TRIGGER: func = vfio_pci_set_req_trigger; break; } + break; } if (!func) diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index afa06d2..2bbfc25e 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -591,11 +591,6 @@ static void handle_rx(struct vhost_net *net) * TODO: support TSO. */ iov_iter_advance(&msg.msg_iter, vhost_hlen); - } else { - /* It'll come from socket; we'll need to patch - * ->num_buffers over if VIRTIO_NET_F_MRG_RXBUF - */ - iov_iter_advance(&fixup, sizeof(hdr)); } err = sock->ops->recvmsg(NULL, sock, &msg, sock_len, MSG_DONTWAIT | MSG_TRUNC); @@ -609,17 +604,25 @@ static void handle_rx(struct vhost_net *net) continue; } /* Supply virtio_net_hdr if VHOST_NET_F_VIRTIO_NET_HDR */ - if (unlikely(vhost_hlen) && - copy_to_iter(&hdr, sizeof(hdr), &fixup) != sizeof(hdr)) { - vq_err(vq, "Unable to write vnet_hdr at addr %p\n", - vq->iov->iov_base); - break; + if (unlikely(vhost_hlen)) { + if (copy_to_iter(&hdr, sizeof(hdr), + &fixup) != sizeof(hdr)) { + vq_err(vq, "Unable to write vnet_hdr " + "at addr %p\n", vq->iov->iov_base); + break; + } + } else { + /* Header came from socket; we'll need to patch + * ->num_buffers over if VIRTIO_NET_F_MRG_RXBUF + */ + iov_iter_advance(&fixup, sizeof(hdr)); } /* TODO: Should check and handle checksum. */ num_buffers = cpu_to_vhost16(vq, headcount); if (likely(mergeable) && - copy_to_iter(&num_buffers, 2, &fixup) != 2) { + copy_to_iter(&num_buffers, sizeof num_buffers, + &fixup) != sizeof num_buffers) { vq_err(vq, "Failed num_buffers write"); vhost_discard_vq_desc(vq, headcount); break; diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 8d4f3f1..71df240 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -1956,10 +1956,9 @@ static int vhost_scsi_make_nexus(struct vhost_scsi_tpg *tpg, goto out; } /* - * Now register the TCM vhost virtual I_T Nexus as active with the - * call to __transport_register_session() + * Now register the TCM vhost virtual I_T Nexus as active. */ - __transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl, + transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl, tv_nexus->tvn_se_sess, tv_nexus); tpg->tpg_nexus = tv_nexus; diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c index 32c0b6b..9362424 100644 --- a/drivers/video/fbdev/amba-clcd.c +++ b/drivers/video/fbdev/amba-clcd.c @@ -599,6 +599,9 @@ static int clcdfb_of_get_mode(struct device *dev, struct device_node *endpoint, len = clcdfb_snprintf_mode(NULL, 0, mode); name = devm_kzalloc(dev, len + 1, GFP_KERNEL); + if (!name) + return -ENOMEM; + clcdfb_snprintf_mode(name, len + 1, mode); mode->name = name; diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c index 9533859..868facd 100644 --- a/drivers/video/fbdev/core/fbmon.c +++ b/drivers/video/fbdev/core/fbmon.c @@ -624,9 +624,6 @@ static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize, int num = 0, i, first = 1; int ver, rev; - ver = edid[EDID_STRUCT_VERSION]; - rev = edid[EDID_STRUCT_REVISION]; - mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL); if (mode == NULL) return NULL; @@ -637,6 +634,9 @@ static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize, return NULL; } + ver = edid[EDID_STRUCT_VERSION]; + rev = edid[EDID_STRUCT_REVISION]; + *dbsize = 0; DPRINTK(" Detailed Timings\n"); diff --git a/drivers/video/fbdev/omap2/dss/display-sysfs.c b/drivers/video/fbdev/omap2/dss/display-sysfs.c index 5a2095a..1218655 100644 --- a/drivers/video/fbdev/omap2/dss/display-sysfs.c +++ b/drivers/video/fbdev/omap2/dss/display-sysfs.c @@ -28,44 +28,22 @@ #include <video/omapdss.h> #include "dss.h" -static struct omap_dss_device *to_dss_device_sysfs(struct device *dev) +static ssize_t display_name_show(struct omap_dss_device *dssdev, char *buf) { - struct omap_dss_device *dssdev = NULL; - - for_each_dss_dev(dssdev) { - if (dssdev->dev == dev) { - omap_dss_put_device(dssdev); - return dssdev; - } - } - - return NULL; -} - -static ssize_t display_name_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", dssdev->name ? dssdev->name : ""); } -static ssize_t display_enabled_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t display_enabled_show(struct omap_dss_device *dssdev, char *buf) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", omapdss_device_is_enabled(dssdev)); } -static ssize_t display_enabled_store(struct device *dev, - struct device_attribute *attr, +static ssize_t display_enabled_store(struct omap_dss_device *dssdev, const char *buf, size_t size) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); int r; bool enable; @@ -90,19 +68,16 @@ static ssize_t display_enabled_store(struct device *dev, return size; } -static ssize_t display_tear_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t display_tear_show(struct omap_dss_device *dssdev, char *buf) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); return snprintf(buf, PAGE_SIZE, "%d\n", dssdev->driver->get_te ? dssdev->driver->get_te(dssdev) : 0); } -static ssize_t display_tear_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) +static ssize_t display_tear_store(struct omap_dss_device *dssdev, + const char *buf, size_t size) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); int r; bool te; @@ -120,10 +95,8 @@ static ssize_t display_tear_store(struct device *dev, return size; } -static ssize_t display_timings_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t display_timings_show(struct omap_dss_device *dssdev, char *buf) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); struct omap_video_timings t; if (!dssdev->driver->get_timings) @@ -137,10 +110,9 @@ static ssize_t display_timings_show(struct device *dev, t.y_res, t.vfp, t.vbp, t.vsw); } -static ssize_t display_timings_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) +static ssize_t display_timings_store(struct omap_dss_device *dssdev, + const char *buf, size_t size) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); struct omap_video_timings t = dssdev->panel.timings; int r, found; @@ -176,10 +148,8 @@ static ssize_t display_timings_store(struct device *dev, return size; } -static ssize_t display_rotate_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t display_rotate_show(struct omap_dss_device *dssdev, char *buf) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); int rotate; if (!dssdev->driver->get_rotate) return -ENOENT; @@ -187,10 +157,9 @@ static ssize_t display_rotate_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%u\n", rotate); } -static ssize_t display_rotate_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) +static ssize_t display_rotate_store(struct omap_dss_device *dssdev, + const char *buf, size_t size) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); int rot, r; if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate) @@ -207,10 +176,8 @@ static ssize_t display_rotate_store(struct device *dev, return size; } -static ssize_t display_mirror_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t display_mirror_show(struct omap_dss_device *dssdev, char *buf) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); int mirror; if (!dssdev->driver->get_mirror) return -ENOENT; @@ -218,10 +185,9 @@ static ssize_t display_mirror_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%u\n", mirror); } -static ssize_t display_mirror_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) +static ssize_t display_mirror_store(struct omap_dss_device *dssdev, + const char *buf, size_t size) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); int r; bool mirror; @@ -239,10 +205,8 @@ static ssize_t display_mirror_store(struct device *dev, return size; } -static ssize_t display_wss_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t display_wss_show(struct omap_dss_device *dssdev, char *buf) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); unsigned int wss; if (!dssdev->driver->get_wss) @@ -253,10 +217,9 @@ static ssize_t display_wss_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss); } -static ssize_t display_wss_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) +static ssize_t display_wss_store(struct omap_dss_device *dssdev, + const char *buf, size_t size) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); u32 wss; int r; @@ -277,50 +240,94 @@ static ssize_t display_wss_store(struct device *dev, return size; } -static DEVICE_ATTR(display_name, S_IRUGO, display_name_show, NULL); -static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR, +struct display_attribute { + struct attribute attr; + ssize_t (*show)(struct omap_dss_device *, char *); + ssize_t (*store)(struct omap_dss_device *, const char *, size_t); +}; + +#define DISPLAY_ATTR(_name, _mode, _show, _store) \ + struct display_attribute display_attr_##_name = \ + __ATTR(_name, _mode, _show, _store) + +static DISPLAY_ATTR(name, S_IRUGO, display_name_show, NULL); +static DISPLAY_ATTR(display_name, S_IRUGO, display_name_show, NULL); +static DISPLAY_ATTR(enabled, S_IRUGO|S_IWUSR, display_enabled_show, display_enabled_store); -static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR, +static DISPLAY_ATTR(tear_elim, S_IRUGO|S_IWUSR, display_tear_show, display_tear_store); -static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR, +static DISPLAY_ATTR(timings, S_IRUGO|S_IWUSR, display_timings_show, display_timings_store); -static DEVICE_ATTR(rotate, S_IRUGO|S_IWUSR, +static DISPLAY_ATTR(rotate, S_IRUGO|S_IWUSR, display_rotate_show, display_rotate_store); -static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR, +static DISPLAY_ATTR(mirror, S_IRUGO|S_IWUSR, display_mirror_show, display_mirror_store); -static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR, +static DISPLAY_ATTR(wss, S_IRUGO|S_IWUSR, display_wss_show, display_wss_store); -static const struct attribute *display_sysfs_attrs[] = { - &dev_attr_display_name.attr, - &dev_attr_enabled.attr, - &dev_attr_tear_elim.attr, - &dev_attr_timings.attr, - &dev_attr_rotate.attr, - &dev_attr_mirror.attr, - &dev_attr_wss.attr, +static struct attribute *display_sysfs_attrs[] = { + &display_attr_name.attr, + &display_attr_display_name.attr, + &display_attr_enabled.attr, + &display_attr_tear_elim.attr, + &display_attr_timings.attr, + &display_attr_rotate.attr, + &display_attr_mirror.attr, + &display_attr_wss.attr, NULL }; +static ssize_t display_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct omap_dss_device *dssdev; + struct display_attribute *display_attr; + + dssdev = container_of(kobj, struct omap_dss_device, kobj); + display_attr = container_of(attr, struct display_attribute, attr); + + if (!display_attr->show) + return -ENOENT; + + return display_attr->show(dssdev, buf); +} + +static ssize_t display_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t size) +{ + struct omap_dss_device *dssdev; + struct display_attribute *display_attr; + + dssdev = container_of(kobj, struct omap_dss_device, kobj); + display_attr = container_of(attr, struct display_attribute, attr); + + if (!display_attr->store) + return -ENOENT; + + return display_attr->store(dssdev, buf, size); +} + +static const struct sysfs_ops display_sysfs_ops = { + .show = display_attr_show, + .store = display_attr_store, +}; + +static struct kobj_type display_ktype = { + .sysfs_ops = &display_sysfs_ops, + .default_attrs = display_sysfs_attrs, +}; + int display_init_sysfs(struct platform_device *pdev) { struct omap_dss_device *dssdev = NULL; int r; for_each_dss_dev(dssdev) { - struct kobject *kobj = &dssdev->dev->kobj; - - r = sysfs_create_files(kobj, display_sysfs_attrs); + r = kobject_init_and_add(&dssdev->kobj, &display_ktype, + &pdev->dev.kobj, dssdev->alias); if (r) { DSSERR("failed to create sysfs files\n"); - goto err; - } - - r = sysfs_create_link(&pdev->dev.kobj, kobj, dssdev->alias); - if (r) { - sysfs_remove_files(kobj, display_sysfs_attrs); - - DSSERR("failed to create sysfs display link\n"); + omap_dss_put_device(dssdev); goto err; } } @@ -338,8 +345,12 @@ void display_uninit_sysfs(struct platform_device *pdev) struct omap_dss_device *dssdev = NULL; for_each_dss_dev(dssdev) { - sysfs_remove_link(&pdev->dev.kobj, dssdev->alias); - sysfs_remove_files(&dssdev->dev->kobj, - display_sysfs_attrs); + if (kobject_name(&dssdev->kobj) == NULL) + continue; + + kobject_del(&dssdev->kobj); + kobject_put(&dssdev->kobj); + + memset(&dssdev->kobj, 0, sizeof(dssdev->kobj)); } } diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 0413157..6a356e3 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -29,6 +29,7 @@ #include <linux/module.h> #include <linux/balloon_compaction.h> #include <linux/oom.h> +#include <linux/wait.h> /* * Balloon device works in 4K page units. So each page is pointed to by @@ -334,17 +335,25 @@ static int virtballoon_oom_notify(struct notifier_block *self, static int balloon(void *_vballoon) { struct virtio_balloon *vb = _vballoon; + DEFINE_WAIT_FUNC(wait, woken_wake_function); set_freezable(); while (!kthread_should_stop()) { s64 diff; try_to_freeze(); - wait_event_interruptible(vb->config_change, - (diff = towards_target(vb)) != 0 - || vb->need_stats_update - || kthread_should_stop() - || freezing(current)); + + add_wait_queue(&vb->config_change, &wait); + for (;;) { + if ((diff = towards_target(vb)) != 0 || + vb->need_stats_update || + kthread_should_stop() || + freezing(current)) + break; + wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); + } + remove_wait_queue(&vb->config_change, &wait); + if (vb->need_stats_update) stats_handle_request(vb); if (diff > 0) @@ -499,6 +508,8 @@ static int virtballoon_probe(struct virtio_device *vdev) if (err < 0) goto out_oom_notify; + virtio_device_ready(vdev); + vb->thread = kthread_run(balloon, vb, "vballoon"); if (IS_ERR(vb->thread)) { err = PTR_ERR(vb->thread); diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index cad5698..6010d7e 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -156,22 +156,95 @@ static void vm_get(struct virtio_device *vdev, unsigned offset, void *buf, unsigned len) { struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); - u8 *ptr = buf; - int i; + void __iomem *base = vm_dev->base + VIRTIO_MMIO_CONFIG; + u8 b; + __le16 w; + __le32 l; - for (i = 0; i < len; i++) - ptr[i] = readb(vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i); + if (vm_dev->version == 1) { + u8 *ptr = buf; + int i; + + for (i = 0; i < len; i++) + ptr[i] = readb(base + offset + i); + return; + } + + switch (len) { + case 1: + b = readb(base + offset); + memcpy(buf, &b, sizeof b); + break; + case 2: + w = cpu_to_le16(readw(base + offset)); + memcpy(buf, &w, sizeof w); + break; + case 4: + l = cpu_to_le32(readl(base + offset)); + memcpy(buf, &l, sizeof l); + break; + case 8: + l = cpu_to_le32(readl(base + offset)); + memcpy(buf, &l, sizeof l); + l = cpu_to_le32(ioread32(base + offset + sizeof l)); + memcpy(buf + sizeof l, &l, sizeof l); + break; + default: + BUG(); + } } static void vm_set(struct virtio_device *vdev, unsigned offset, const void *buf, unsigned len) { struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); - const u8 *ptr = buf; - int i; + void __iomem *base = vm_dev->base + VIRTIO_MMIO_CONFIG; + u8 b; + __le16 w; + __le32 l; - for (i = 0; i < len; i++) - writeb(ptr[i], vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i); + if (vm_dev->version == 1) { + const u8 *ptr = buf; + int i; + + for (i = 0; i < len; i++) + writeb(ptr[i], base + offset + i); + + return; + } + + switch (len) { + case 1: + memcpy(&b, buf, sizeof b); + writeb(b, base + offset); + break; + case 2: + memcpy(&w, buf, sizeof w); + writew(le16_to_cpu(w), base + offset); + break; + case 4: + memcpy(&l, buf, sizeof l); + writel(le32_to_cpu(l), base + offset); + break; + case 8: + memcpy(&l, buf, sizeof l); + writel(le32_to_cpu(l), base + offset); + memcpy(&l, buf + sizeof l, sizeof l); + writel(le32_to_cpu(l), base + offset + sizeof l); + break; + default: + BUG(); + } +} + +static u32 vm_generation(struct virtio_device *vdev) +{ + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); + + if (vm_dev->version == 1) + return 0; + else + return readl(vm_dev->base + VIRTIO_MMIO_CONFIG_GENERATION); } static u8 vm_get_status(struct virtio_device *vdev) @@ -440,6 +513,7 @@ static const char *vm_bus_name(struct virtio_device *vdev) static const struct virtio_config_ops virtio_mmio_config_ops = { .get = vm_get, .set = vm_set, + .generation = vm_generation, .get_status = vm_get_status, .set_status = vm_set_status, .reset = vm_reset, diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index 6df9405..1443b3c 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c @@ -208,7 +208,8 @@ static int at91_wdt_init(struct platform_device *pdev, struct at91wdt *wdt) if ((tmp & AT91_WDT_WDFIEN) && wdt->irq) { err = request_irq(wdt->irq, wdt_interrupt, - IRQF_SHARED | IRQF_IRQPOLL, + IRQF_SHARED | IRQF_IRQPOLL | + IRQF_NO_SUSPEND, pdev->name, wdt); if (err) return err; diff --git a/drivers/watchdog/imgpdc_wdt.c b/drivers/watchdog/imgpdc_wdt.c index c8def68..0deaa4f 100644 --- a/drivers/watchdog/imgpdc_wdt.c +++ b/drivers/watchdog/imgpdc_wdt.c @@ -42,10 +42,10 @@ #define PDC_WDT_MIN_TIMEOUT 1 #define PDC_WDT_DEF_TIMEOUT 64 -static int heartbeat; +static int heartbeat = PDC_WDT_DEF_TIMEOUT; module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. " - "(default = " __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")"); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds " + "(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")"); static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); @@ -191,6 +191,7 @@ static int pdc_wdt_probe(struct platform_device *pdev) pdc_wdt->wdt_dev.ops = &pdc_wdt_ops; pdc_wdt->wdt_dev.max_timeout = 1 << PDC_WDT_CONFIG_DELAY_MASK; pdc_wdt->wdt_dev.parent = &pdev->dev; + watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt); ret = watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev); if (ret < 0) { @@ -232,7 +233,6 @@ static int pdc_wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(&pdc_wdt->wdt_dev, nowayout); platform_set_drvdata(pdev, pdc_wdt); - watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt); ret = watchdog_register_device(&pdc_wdt->wdt_dev); if (ret) diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index a87f6df..938b987 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -133,7 +133,7 @@ static int mtk_wdt_start(struct watchdog_device *wdt_dev) u32 reg; struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); void __iomem *wdt_base = mtk_wdt->wdt_base; - u32 ret; + int ret; ret = mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout); if (ret < 0) diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index b812462..94d9680 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -55,6 +55,23 @@ config XEN_BALLOON_MEMORY_HOTPLUG In that case step 3 should be omitted. +config XEN_BALLOON_MEMORY_HOTPLUG_LIMIT + int "Hotplugged memory limit (in GiB) for a PV guest" + default 512 if X86_64 + default 4 if X86_32 + range 0 64 if X86_32 + depends on XEN_HAVE_PVMMU + depends on XEN_BALLOON_MEMORY_HOTPLUG + help + Maxmium amount of memory (in GiB) that a PV guest can be + expanded to when using memory hotplug. + + A PV guest can have more memory than this limit if is + started with a larger maximum. + + This value is used to allocate enough space in internal + tables needed for physical memory administration. + config XEN_SCRUB_PAGES bool "Scrub pages before returning them to system" depends on XEN_BALLOON diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 2140398..2ccd359 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -2,7 +2,7 @@ ifeq ($(filter y, $(CONFIG_ARM) $(CONFIG_ARM64)),) obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o endif obj-$(CONFIG_X86) += fallback.o -obj-y += grant-table.o features.o balloon.o manage.o +obj-y += grant-table.o features.o balloon.o manage.o preempt.o obj-y += events/ obj-y += xenbus/ diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 0b52d92..fd93369 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -229,6 +229,29 @@ static enum bp_state reserve_additional_memory(long credit) balloon_hotplug = round_up(balloon_hotplug, PAGES_PER_SECTION); nid = memory_add_physaddr_to_nid(hotplug_start_paddr); +#ifdef CONFIG_XEN_HAVE_PVMMU + /* + * add_memory() will build page tables for the new memory so + * the p2m must contain invalid entries so the correct + * non-present PTEs will be written. + * + * If a failure occurs, the original (identity) p2m entries + * are not restored since this region is now known not to + * conflict with any devices. + */ + if (!xen_feature(XENFEAT_auto_translated_physmap)) { + unsigned long pfn, i; + + pfn = PFN_DOWN(hotplug_start_paddr); + for (i = 0; i < balloon_hotplug; i++) { + if (!set_phys_to_machine(pfn + i, INVALID_P2M_ENTRY)) { + pr_warn("set_phys_to_machine() failed, no memory added\n"); + return BP_ECANCELED; + } + } + } +#endif + rc = add_memory(nid, hotplug_start_paddr, balloon_hotplug << PAGE_SHIFT); if (rc) { diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c index b4bca2d..70fba97 100644 --- a/drivers/xen/events/events_base.c +++ b/drivers/xen/events/events_base.c @@ -526,20 +526,26 @@ static unsigned int __startup_pirq(unsigned int irq) pirq_query_unmask(irq); rc = set_evtchn_to_irq(evtchn, irq); - if (rc != 0) { - pr_err("irq%d: Failed to set port to irq mapping (%d)\n", - irq, rc); - xen_evtchn_close(evtchn); - return 0; - } + if (rc) + goto err; + bind_evtchn_to_cpu(evtchn, 0); info->evtchn = evtchn; + rc = xen_evtchn_port_setup(info); + if (rc) + goto err; + out: unmask_evtchn(evtchn); eoi_pirq(irq_get_irq_data(irq)); return 0; + +err: + pr_err("irq%d: Failed to set port to irq mapping (%d)\n", irq, rc); + xen_evtchn_close(evtchn); + return 0; } static unsigned int startup_pirq(struct irq_data *data) diff --git a/drivers/xen/preempt.c b/drivers/xen/preempt.c new file mode 100644 index 0000000..a1800c1 --- /dev/null +++ b/drivers/xen/preempt.c @@ -0,0 +1,44 @@ +/* + * Preemptible hypercalls + * + * Copyright (C) 2014 Citrix Systems R&D ltd. + * + * This source code is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + */ + +#include <linux/sched.h> +#include <xen/xen-ops.h> + +#ifndef CONFIG_PREEMPT + +/* + * Some hypercalls issued by the toolstack can take many 10s of + * seconds. Allow tasks running hypercalls via the privcmd driver to + * be voluntarily preempted even if full kernel preemption is + * disabled. + * + * Such preemptible hypercalls are bracketed by + * xen_preemptible_hcall_begin() and xen_preemptible_hcall_end() + * calls. + */ + +DEFINE_PER_CPU(bool, xen_in_preemptible_hcall); +EXPORT_SYMBOL_GPL(xen_in_preemptible_hcall); + +asmlinkage __visible void xen_maybe_preempt_hcall(void) +{ + if (unlikely(__this_cpu_read(xen_in_preemptible_hcall) + && should_resched())) { + /* + * Clear flag as we may be rescheduled on a different + * cpu. + */ + __this_cpu_write(xen_in_preemptible_hcall, false); + _cond_resched(); + __this_cpu_write(xen_in_preemptible_hcall, true); + } +} +#endif /* CONFIG_PREEMPT */ diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index 569a13b..59ac71c 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -56,10 +56,12 @@ static long privcmd_ioctl_hypercall(void __user *udata) if (copy_from_user(&hypercall, udata, sizeof(hypercall))) return -EFAULT; + xen_preemptible_hcall_begin(); ret = privcmd_call(hypercall.op, hypercall.arg[0], hypercall.arg[1], hypercall.arg[2], hypercall.arg[3], hypercall.arg[4]); + xen_preemptible_hcall_end(); return ret; } diff --git a/drivers/xen/xen-pciback/conf_space.c b/drivers/xen/xen-pciback/conf_space.c index 46ae0f9..75fe3d4 100644 --- a/drivers/xen/xen-pciback/conf_space.c +++ b/drivers/xen/xen-pciback/conf_space.c @@ -16,7 +16,7 @@ #include "conf_space.h" #include "conf_space_quirks.h" -static bool permissive; +bool permissive; module_param(permissive, bool, 0644); /* This is where xen_pcibk_read_config_byte, xen_pcibk_read_config_word, diff --git a/drivers/xen/xen-pciback/conf_space.h b/drivers/xen/xen-pciback/conf_space.h index e56c934..2e1d73d 100644 --- a/drivers/xen/xen-pciback/conf_space.h +++ b/drivers/xen/xen-pciback/conf_space.h @@ -64,6 +64,8 @@ struct config_field_entry { void *data; }; +extern bool permissive; + #define OFFSET(cfg_entry) ((cfg_entry)->base_offset+(cfg_entry)->field->offset) /* Add fields to a device - the add_fields macro expects to get a pointer to diff --git a/drivers/xen/xen-pciback/conf_space_header.c b/drivers/xen/xen-pciback/conf_space_header.c index c5ee825..2d73693 100644 --- a/drivers/xen/xen-pciback/conf_space_header.c +++ b/drivers/xen/xen-pciback/conf_space_header.c @@ -11,6 +11,10 @@ #include "pciback.h" #include "conf_space.h" +struct pci_cmd_info { + u16 val; +}; + struct pci_bar_info { u32 val; u32 len_val; @@ -20,22 +24,36 @@ struct pci_bar_info { #define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO)) #define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER) -static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data) +/* Bits guests are allowed to control in permissive mode. */ +#define PCI_COMMAND_GUEST (PCI_COMMAND_MASTER|PCI_COMMAND_SPECIAL| \ + PCI_COMMAND_INVALIDATE|PCI_COMMAND_VGA_PALETTE| \ + PCI_COMMAND_WAIT|PCI_COMMAND_FAST_BACK) + +static void *command_init(struct pci_dev *dev, int offset) { - int i; - int ret; - - ret = xen_pcibk_read_config_word(dev, offset, value, data); - if (!pci_is_enabled(dev)) - return ret; - - for (i = 0; i < PCI_ROM_RESOURCE; i++) { - if (dev->resource[i].flags & IORESOURCE_IO) - *value |= PCI_COMMAND_IO; - if (dev->resource[i].flags & IORESOURCE_MEM) - *value |= PCI_COMMAND_MEMORY; + struct pci_cmd_info *cmd = kmalloc(sizeof(*cmd), GFP_KERNEL); + int err; + + if (!cmd) + return ERR_PTR(-ENOMEM); + + err = pci_read_config_word(dev, PCI_COMMAND, &cmd->val); + if (err) { + kfree(cmd); + return ERR_PTR(err); } + return cmd; +} + +static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data) +{ + int ret = pci_read_config_word(dev, offset, value); + const struct pci_cmd_info *cmd = data; + + *value &= PCI_COMMAND_GUEST; + *value |= cmd->val & ~PCI_COMMAND_GUEST; + return ret; } @@ -43,6 +61,8 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data) { struct xen_pcibk_dev_data *dev_data; int err; + u16 val; + struct pci_cmd_info *cmd = data; dev_data = pci_get_drvdata(dev); if (!pci_is_enabled(dev) && is_enable_cmd(value)) { @@ -83,6 +103,19 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data) } } + cmd->val = value; + + if (!permissive && (!dev_data || !dev_data->permissive)) + return 0; + + /* Only allow the guest to control certain bits. */ + err = pci_read_config_word(dev, offset, &val); + if (err || val == value) + return err; + + value &= PCI_COMMAND_GUEST; + value |= val & ~PCI_COMMAND_GUEST; + return pci_write_config_word(dev, offset, value); } @@ -282,6 +315,8 @@ static const struct config_field header_common[] = { { .offset = PCI_COMMAND, .size = 2, + .init = command_init, + .release = bar_release, .u.w.read = command_read, .u.w.write = command_write, }, diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index 61653a0..42bd55a 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -709,12 +709,11 @@ static int prepare_pending_reqs(struct vscsibk_info *info, static int scsiback_do_cmd_fn(struct vscsibk_info *info) { struct vscsiif_back_ring *ring = &info->ring; - struct vscsiif_request *ring_req; + struct vscsiif_request ring_req; struct vscsibk_pend *pending_req; RING_IDX rc, rp; int err, more_to_do; uint32_t result; - uint8_t act; rc = ring->req_cons; rp = ring->sring->req_prod; @@ -735,11 +734,10 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info) if (!pending_req) return 1; - ring_req = RING_GET_REQUEST(ring, rc); + ring_req = *RING_GET_REQUEST(ring, rc); ring->req_cons = ++rc; - act = ring_req->act; - err = prepare_pending_reqs(info, ring_req, pending_req); + err = prepare_pending_reqs(info, &ring_req, pending_req); if (err) { switch (err) { case -ENODEV: @@ -755,9 +753,9 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info) return 1; } - switch (act) { + switch (ring_req.act) { case VSCSIIF_ACT_SCSI_CDB: - if (scsiback_gnttab_data_map(ring_req, pending_req)) { + if (scsiback_gnttab_data_map(&ring_req, pending_req)) { scsiback_fast_flush_area(pending_req); scsiback_do_resp_with_sense(NULL, DRIVER_ERROR << 24, 0, pending_req); @@ -768,7 +766,7 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info) break; case VSCSIIF_ACT_SCSI_ABORT: scsiback_device_action(pending_req, TMR_ABORT_TASK, - ring_req->ref_rqid); + ring_req.ref_rqid); break; case VSCSIIF_ACT_SCSI_RESET: scsiback_device_action(pending_req, TMR_LUN_RESET, 0); @@ -1661,11 +1659,8 @@ static int scsiback_make_nexus(struct scsiback_tpg *tpg, name); goto out; } - /* - * Now register the TCM pvscsi virtual I_T Nexus as active with the - * call to __transport_register_session() - */ - __transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl, + /* Now register the TCM pvscsi virtual I_T Nexus as active. */ + transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl, tv_nexus->tvn_se_sess, tv_nexus); tpg->tpg_nexus = tv_nexus; |