diff options
Diffstat (limited to 'drivers')
546 files changed, 15835 insertions, 9152 deletions
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 36b0e61f9c09..bbcc2b5a70d4 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_pad.c b/drivers/acpi/acpi_pad.c index c7b105c0e1d3..6bc9cbc01ad6 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 d98ba4355819..fdc16ce9d272 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 e7f718d6918a..b1def411c0b8 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/processor_idle.c b/drivers/acpi/processor_idle.c index c6bb9f1257c9..39e0c8e36244 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/sbs.c b/drivers/acpi/sbs.c index a7a3edd28beb..cd827625cf07 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/ata/libata-core.c b/drivers/ata/libata-core.c index 4c35f0822d06..23dac3babfe3 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/base/regmap/Makefile b/drivers/base/regmap/Makefile index 0a533653ef3b..609e4c84f485 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 beb8b27d4621..a13587b5c2be 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 d453a2c98ad0..81751a49d8bf 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 f373c35f9e1d..7eb7b3b98794 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 6299a50a5960..a6c3f75b4b01 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 f99b098ddabf..6273ff072f3e 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 000000000000..64586a1c5a42 --- /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/nbd.c b/drivers/block/nbd.c index 4bc2a5cb9935..a98c41f72c63 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 ceb32dd52a6c..e23be20a3417 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c @@ -3003,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/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c index 79524ed2a3cb..8753b0f6a317 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 f6646ed3047e..518585c1ce94 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 f6e378dac5f5..f40e3bd2c69c 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/virtio_console.c b/drivers/char/virtio_console.c index fae2dbbf5745..72d7028f779b 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/clocksource/Kconfig b/drivers/clocksource/Kconfig index 68161f7a07d6..a0b036ccb118 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -192,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 @@ -201,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 @@ -210,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 diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index a3025e7ae35f..266469691e58 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 d305fb089767..a19a3f619cc7 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 d0a7bd66b8b9..dc3c6ee04aaa 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/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 2bd13b53b727..b8ff3c64cc45 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 f150ca82bfaf..b6b8fa3cd211 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 f4a9c0058b4d..1928a8912584 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 d2616ef16770..5a112d72fc2d 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 bba62f9deefb..5b6e3d5644c9 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 b5b4d4585c9a..c0304ff608b0 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 02268448dc85..28aa4b7bb602 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 28e59a48b35f..8ae655c364f4 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/cpuidle/cpuidle-mvebu-v7.c b/drivers/cpuidle/cpuidle-mvebu-v7.c index 38e68618513a..980151f34707 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 080bd2dbde4b..7a73a279e179 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -330,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 2697e87d5b34..5db147859b90 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 97c5903b4606..832a2c3f01ff 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/Kconfig b/drivers/dma/Kconfig index a874b6ec6650..942ca541dcbd 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 f915f61ec574..539d4825bd76 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 4a5fd245014e..83aa55d6fa5d 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 1e1a4c567542..0b4fc6fb48ce 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 d6bba6c636c2..2727ca560572 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/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index 0723096fb50a..c92d6a70ccf3 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 512cb8e2805e..ceedafbd23e0 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 4527a3ebeac4..84884418fd30 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 f15712f2fec6..ac336a961dea 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/platform.c b/drivers/dma/dw/platform.c index 6565a361e7e5..b2c3ae071429 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 276157f22612..53dbd3b3384c 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 18c0a131e4e4..66a0efb9651d 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 5aaead9b56f7..000000000000 --- 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 17b42192ea58..000000000000 --- 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/moxart-dma.c b/drivers/dma/moxart-dma.c index 15cab7d79525..b4634109e010 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 7dd6dd121681..167dbaf65742 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/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 5907c1718f8c..92772fffc52f 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 d8468c667925..4bdec752d330 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 2c1bbf740605..4709c6079848 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 0d66ae68d468..e14977ff95db 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 6c9f381e8fe6..ad42587c3f4d 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 1747906f10ce..af3be1914dbb 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 c84eecb191ef..112d63ad1154 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 e6d1691dfa45..9cb082a19d8a 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 f2118bfcf8df..26ecc52e073d 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 f784de1dc793..11260cc3360e 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 9cd0b301f81b..01087a38da22 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 b4705d9366bf..4d4110364f02 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 4382343a7c60..ee1078cd3b96 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 64b68320249f..c26a513f8869 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 10b10521f62e..35ab66c623a3 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 1fa76a588af3..68bf234bdfe6 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 4bd10f94f068..bb19e0732681 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 1b64fd060821..3515b381c131 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 1c9691535e13..fc153aea2f6c 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 69fac068669f..6e45a43ffe84 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, u32 len, int num, +static void dmi_table(u8 *buf, void (*decode)(const struct dmi_header *, void *), void *private_data) { @@ -86,10 +88,13 @@ static void dmi_table(u8 *buf, u32 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; /* @@ -98,9 +103,9 @@ static void dmi_table(u8 *buf, u32 len, int num, * 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); /* @@ -115,8 +120,6 @@ static void dmi_table(u8 *buf, u32 len, int num, } static phys_addr_t dmi_base; -static u32 dmi_len; -static u16 dmi_num; static int __init dmi_walk_early(void (*decode)(const struct dmi_header *, void *)) @@ -127,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); @@ -198,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); @@ -470,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])) { @@ -503,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; @@ -528,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; @@ -901,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 dcae482a9a17..e29560e6b40b 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/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 47437b16b186..e334a01cf92f 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 91da56c4fd54..ef5d764e2a27 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 a6952ba343a8..a65b75161aa4 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 257e2989215c..045a952576c7 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/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index c0929d938ced..df990f29757a 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/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 910ff8ab9c9c..d8135adb2238 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -645,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) { @@ -652,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_kernel_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c index e415a2a9207e..c7d298e62c96 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/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f6d04c7b5115..b6f076b213bc 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -525,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 @@ -1320,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; @@ -2132,7 +2121,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, 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++) @@ -2212,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; diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index 732cb6f8e653..4c0aa97aaf03 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_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 6591d48c1b9d..3fee587bc284 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 a5e74612100e..0a6780367d28 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 63f02e2380ae..970046199608 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 ba9b3d5ed672..000000000000 --- 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 4eb20d78379a..000000000000 --- 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 925fc69af1a0..33a10ce967ea 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 a5616872eee7..8ad5b7294eb4 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 3518bc4654c5..2e3bc57ea50e 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_drv.c b/drivers/gpu/drm/i915/i915_drv.c index cc6ea53d2b81..5c66b568bb81 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1095,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); /* @@ -1189,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); } @@ -1197,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; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 8727086cf48c..b4faa2df9d3d 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; }; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 5b205863b659..27ea6bdebce7 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2737,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; @@ -2789,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); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index b773368fc62c..38a742532c4f 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/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9943c20a741d..f75173c20f47 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> @@ -2416,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) @@ -2429,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; @@ -2453,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, @@ -6602,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"); @@ -6610,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; @@ -7643,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; @@ -7730,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"); @@ -7738,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; @@ -9816,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; @@ -9884,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); @@ -13718,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_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 0a52c44ad03d..9c5451c97942 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/msm/edp/edp_ctrl.c b/drivers/gpu/drm/msm/edp/edp_ctrl.c index 3e246210c46f..0ec5abdba5c4 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/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index 29bd539af183..6efa8f38ff54 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 539561ed3281..108d048da764 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 b038b6eb51db..043e4296084c 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 2e7ec389eea7..57e2c5b13123 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 b52300d8861a..5e9454ba158f 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 956f4dce960c..b2fae6e389e2 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 d1a89b2bd5c1..c4e1f085ee10 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/cikd.h b/drivers/gpu/drm/radeon/cikd.h index c648e1996dab..243a36c93b8f 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -2129,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/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 5587603b4a89..33d5a4f4eebd 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_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c index 63ccb8fa799c..d27e4ccb848c 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_kfd.c b/drivers/gpu/drm/radeon/radeon_kfd.c index 061eaa9c19c7..122eb5693ba1 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 a69bd441dd2d..572b4dbec186 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 43e09942823e..318165d4855c 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 33cf4108386d..c1ba83a8dd8c 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; @@ -858,6 +854,17 @@ static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev, 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 */ @@ -983,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) @@ -1007,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. @@ -1069,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 2456f69efd23..8c7872339c2a 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 d02aa1d0f588..b292aca0f342 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/vce_v2_0.c b/drivers/gpu/drm/radeon/vce_v2_0.c index 1ac7bb825a1b..fbbe78fbd087 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/hid/hid-core.c b/drivers/hid/hid-core.c index 7c669c328c4c..56ce8c2b5530 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1959,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 204312bfab2c..9c4786759f16 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 @@ -898,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 052869d0ab78..32c2da49bd5b 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-sony.c b/drivers/hid/hid-sony.c index 1896c019e302..c906300cf667 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -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; } /* diff --git a/drivers/hid/hid-tivo.c b/drivers/hid/hid-tivo.c index d790d8d71f7f..d98696927453 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 6b61f01e01e7..05e23c417d50 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 10934aa129fb..875694d43e4d 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/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 9be99a67bfe2..a82127753461 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 7db432809e9e..0d0d0dd89d17 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 f0568a7e6de9..ba9af470bea0 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 046351cf17f3..bbe32d66e500 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); @@ -1043,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); @@ -1096,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; } @@ -1116,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) @@ -1125,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); @@ -1147,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; } @@ -1193,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); @@ -1613,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; @@ -1640,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++; } } @@ -1649,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; @@ -1661,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; @@ -1693,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) @@ -1718,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; @@ -1728,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; } @@ -1759,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; /* diff --git a/drivers/hsi/clients/Kconfig b/drivers/hsi/clients/Kconfig index bc60dec3f586..d6126200361f 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 4d5bc0e0b27b..260723266407 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 000000000000..4983529a9c6c --- /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 eb4dc63dbc93..bbb19231fa82 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 110fade9cb74..25d9e72627e9 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 6c941472e707..b4a40f17e2aa 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/coretemp.c b/drivers/hwmon/coretemp.c index 5b7fec824f10..ed303ba3a593 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 36abf814b8c7..a3dae6d0082a 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 030e7ff589be..21b9c72f16bd 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 febe8175d36c..4255514b2c72 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 409116c52cc5..d0ee556e8ce0 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 996bdfd5cf25..9887d3224a86 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 1be41177b620..4fcb48103299 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 000000000000..b77b82f24480 --- /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 1991d9032c38..2d9a712699ff 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 cf1848b8fb32..8ba419d343f8 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/ide/ide-tape.c b/drivers/ide/ide-tape.c index 1793aea4a7d2..6eb738ca6d2f 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 b0e58522780d..5c979d0667a2 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 1096da327130..75c6d2103e07 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 066d0c04072c..75567fd457dc 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 567de269cc00..1a6379525fa4 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 202daf889be2..d82af14986ba 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 0315af640866..3930e63e84bc 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 ff61ae55dd3f..8a0eb4a04fb5 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 000000000000..3445107e10b7 --- /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/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 2e5cc4409f78..a0e7161f040c 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 8ec353c01d98..e63b8e76d4c3 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/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c index 60451b328242..ccf3ea7e1afa 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/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c index e0017c22bb9c..f53e9a803a0e 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 d8d5bed65e07..ef76afe2643c 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; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index 0cd306a72a6e..ba27e277511f 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 5cc3692acf37..b3a36376c719 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 aaba9d3d980e..4df97f650e44 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 a4b397048f71..a99692ba91bc 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/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 74dff4e4a11a..89fca3a70750 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 aec7a6aa2951..8c014b5dab4c 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 c7619716c31d..59040265e361 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 ac6e2b710ea6..b972c0b41799 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/mouse/alps.c b/drivers/input/mouse/alps.c index 1bd15ebc01f2..27bcdbc950c9 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; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index f2cceb6493a0..3b06c8a360b6 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 aedc3299b14e..ee4bd0d12b26 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/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index fc13dd56953e..a3adde6519f0 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/intel-iommu.c b/drivers/iommu/intel-iommu.c index ae4c1a854e57..2d1e05bdbb53 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/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index 10186cac7716..bc39bdf7b99b 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/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 596b0a9eee99..9687f8afebff 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; } @@ -802,6 +802,7 @@ static int its_alloc_tables(struct its_node *its) int i; 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); @@ -848,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); @@ -874,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; } @@ -980,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); @@ -1026,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 */ @@ -1422,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(GITS_CTLR_ENABLE, 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) { diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c index 6a7447c304ac..358a574d9e8b 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 ee035ec4526b..169172d2ba05 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 37de0173b6d2..74adcd2c967e 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 8b204ae216ab..f83a0f3fc365 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 654773cb1eee..921aafd12aee 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 73f28802dc7a..8001fe9e3434 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 cadf9cc02b25..e6178787ce3d 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); @@ -5080,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 a13f738a7b39..3b5d7f704aa3 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/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 5d2d8f45b4b6..67faa8d6950e 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 e4901a503c73..63c0ee5d0bf5 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 12f7452edce3..a92ff4249d10 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 e8c2cad93962..0974b9a7a584 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 8e44a59d8ec2..98374e8bad3e 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 15f7663dd9f5..24262bbb1a35 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 de2b8c69daa5..22dfb3effda8 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 0c4fcf2dfd09..b09bcd140491 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 d826c58b5d53..cefad184fe96 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 5a1835dd65e8..697aaed42486 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 a901b6248557..2554f3719b9e 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 8526bf5c8429..c835beb2a1a8 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 cee7b56f8404..66634b469c98 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 77dcfdf547ac..87fc0fe29ebd 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 60af3b167f3b..3fd94fe7e1eb 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 bc08a829bc13..cc16e76a2493 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 b481d20c8372..69e0483adfee 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 cfff0b643f1b..0d1825696153 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 b1b580a88654..0acbe52b2411 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 f38ec424872e..5615522f8d62 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 ede50244f265..dbd907d7170e 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 38552a31304a..65fed7146e9b 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/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c index 82dc5748f873..7f327121e6d7 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 23f10f72e5f3..c296bc098fe2 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 1d41e8541f38..c84131e28625 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 862356123d78..ab2129781161 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 aba3409e8d6e..096da48c6a7e 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 a2d545904fbf..9d6d2fb21796 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 e9f1d8d84613..0b14b83a53d6 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 ce6cc47206b0..5bc6c7dbbd60 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 61ac63a3776a..7f4db908f89b 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 6a7cfe0de332..711e913450f5 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 c97001e15227..0aa44e679df4 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 fe32948c6114..e761eb1b1441 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 7872ce586b55..595c934e6166 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 e2a726a503ee..dbf166f94f1b 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 4d2e3c2e1830..38b29265cc7c 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 18c4afe683b8..f45ab91de339 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 e4a07546f8b6..ae19d83bb9de 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 7fe16194ebc8..fb266745f824 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 f84cfb01716d..9df2b6801f76 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 a45ed39d062c..22d929fa3371 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 34bb8f92586e..2bd90fb35c75 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 439d259fdf1d..0ef0343c603a 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 a7935a8d0922..59f2923f8054 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 ca969d271a27..407c21f152b2 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 10ef8244a239..82f512d87cb8 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 000000000000..3b423b0ad8e7 --- /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 3d32ce896b09..4a09f7608c66 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 bcb51e9dfdcd..6287d426c96b 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 17fe02ed6672..22e9111b11ff 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 be479279a1d5..4079a96ad37e 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 29eaff78238e..7a3fc16d0a6c 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 c5b01d6bb85d..a207f5aaf62f 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 f6f82ec3618d..32848eb7ad80 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 22e58268545f..df088343d60f 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 882b07e9667e..682f2bb0f4bf 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 f3778d58d1cd..ad28b49f0203 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 0ad412a4876f..c80287a02735 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 0315e1844330..e639b7f435e5 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 7d9d6a321521..072f67066df3 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 e8a4218b5726..4d3e1ffe5508 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 a31c3573d386..dba7e1c19dd7 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 dd2e1aa95ba3..5af00559e9d6 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/ubi/eba.c b/drivers/mtd/ubi/eba.c index da4c79259f67..16e34b37d134 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/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index b979c265fc51..089a4028859d 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 98d73aab52fe..58808f651452 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/flexcan.c b/drivers/net/can/flexcan.c index 80c46ad4cee4..ad0a7e8c2c2b 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 009acc8641fc..8b4d3e6875eb 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 a316fa4b91ab..57611fd91229 100644 --- a/drivers/net/can/usb/kvaser_usb.c +++ b/drivers/net/can/usb/kvaser_usb.c @@ -14,6 +14,7 @@ * Copyright (C) 2015 Valeo S.A. */ +#include <linux/spinlock.h> #include <linux/kernel.h> #include <linux/completion.h> #include <linux/module.h> @@ -24,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 */ @@ -442,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; @@ -455,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; @@ -466,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[] = { @@ -590,8 +596,8 @@ static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id, * for further details. */ if (tmp->len == 0) { - pos = round_up(pos, - dev->bulk_in->wMaxPacketSize); + pos = round_up(pos, le16_to_cpu(dev->bulk_in-> + wMaxPacketSize)); continue; } @@ -655,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; } @@ -694,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; @@ -712,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 && @@ -737,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) @@ -803,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) @@ -1334,7 +1337,8 @@ static void kvaser_usb_read_bulk_callback(struct urb *urb) * number of events in case of a heavy rx load on the bus. */ if (msg->len == 0) { - pos = round_up(pos, dev->bulk_in->wMaxPacketSize); + pos = round_up(pos, le16_to_cpu(dev->bulk_in-> + wMaxPacketSize)); continue; } @@ -1515,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; @@ -1634,6 +1658,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, struct kvaser_msg *msg; 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; @@ -1687,12 +1712,21 @@ 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) { @@ -1704,7 +1738,6 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, } context->priv = priv; - context->echo_index = i; context->dlc = cf->can_dlc; msg->u.tx_can.tid = context->echo_index; @@ -1716,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++; @@ -1854,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; @@ -1868,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; @@ -1990,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, @@ -1997,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 1ba7c25002e1..e8fc4952c6b0 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 0bac0f14edc3..a9221ad9f1a0 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); @@ -937,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/ethernet/amd/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c index 11d6e6561df1..15a8190a6f75 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/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 756053c028be..4085c4b31047 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 bef750a09027..1ec635f54994 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); @@ -12769,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 = @@ -13668,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 e5aca2de1871..cfe3c7695455 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 d1608297c773..800ab44a07ce 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 2beceaefdeea..965539a9dabe 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/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 97842d03675b..c6ff4890d171 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 */ }; @@ -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 78854ceb0870..dcb047945290 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 a22cf932ca35..d92995138f7e 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 b4b9f6048fe7..b688b32c21fe 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 853c38997c82..ee394dc68303 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -1120,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 */ } @@ -4459,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 231a725f6d5d..326674b19983 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 9b353a88cbda..a4a19e0ec7f5 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 e2bd3f747858..b9d1cbac0eee 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 0545f0de1c52..e0d711071afb 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 1b5506df35b1..280b4a215849 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/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c index 3b42556f7f8d..ed41559bae77 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 27de37aa90af..27b9fe99a9bd 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 36916cfa70f9..7f05f309e935 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 db761e8e42a3..a7634a3f052a 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 0a816859aca5..e6b790f0d9dc 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 99492b7e3713..f6a3a7abd468 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); } @@ -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(); diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 357e8b576905..56b774d3a13d 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/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index 072426a72745..cd7675ac5bf9 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, diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 96208f17bb53..2db653225a0e 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 a681d7c0bb9f..546ca4226916 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 2a210c4efb89..3485acf03014 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/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 264bc15c1ff2..6e70ffee8e87 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 2a8268e6be15..ebbe244e80dd 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/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index d97ca88c55b5..6e413ac4e940 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -3095,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/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index 9fb6948e14c6..5cecec282aba 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -4468,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/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 5d093dc0f5f5..8678e39aba08 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -2248,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)); @@ -2343,19 +2342,16 @@ 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) diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c index a495931a66a1..0e0fbb5842b3 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 09322d9db578..4b310002258d 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/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h index 924ea98bd531..54549a6223dd 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 2a175006028b..b7877a194cfe 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 4f4099d5603d..4fa14208d799 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/usb/asix_common.c b/drivers/net/usb/asix_common.c index 5c55f11572ba..75d6f26729a3 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/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 9311a08565be..4545e78840b0 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 80a844e0ae03..c3e4da9e79ca 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 3eed708a6182..1762ad3910b2 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/r8152.c b/drivers/net/usb/r8152.c index 438fc6bcaef1..9f7c0ab3b349 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 b94a0fbb8b3b..953de13267df 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 449835f4331e..777757ae1973 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 f1ff3666f090..59b0e9754ae3 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 1e0a775ea882..f8528a4cf54f 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/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index cb366adc820b..f50a6bc5d06e 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 2b79a568e803..d23737342f4f 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 60aa8d71e753..8529014e1a5e 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 ccbdb05b28cd..75345c1e8c34 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 defb7a44e0bc..7748a1ccf14f 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 50cdf7090198..8eff2753abad 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 a6f22c32a279..3811878ab9cd 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 47e64e8b9517..cceb026e0793 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 4dbef7e58c2e..5244e43bfafb 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 c3817fae16c0..06f6cc08f451 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 21e5d0843a62..890b95f497d6 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 332bbede39e5..724194e23414 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 8f2c3c8c6b84..21b2630763dc 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 996e7f16adf9..c7154ac42c8c 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 1ec4d55155f7..7810c41cf9a7 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 d530ef3da107..542ee74f290a 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 1ff7ec08532d..09654e73a533 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 194bd1f939ca..078f24cf4af3 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 7e9aa3cb3254..c47c8051da77 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 54fafbf9a711..4b81c0bf63b0 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 07304e1fd64a..96a05406babf 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 dbd6bcf52205..686dd301cd53 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/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 1d4677460711..074f716020aa 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 a62170ea0481..8c45cf44ce24 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/netback.c b/drivers/net/xen-netback/netback.c index cab9f5257f57..997cf0901ac2 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); @@ -655,15 +656,10 @@ static void xenvif_tx_err(struct xenvif_queue *queue, unsigned long flags; do { - int notify; - spin_lock_irqsave(&queue->response_lock, flags); make_tx_response(queue, txp, XEN_NETIF_RSP_ERROR); - RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&queue->tx, notify); + push_tx_responses(queue); spin_unlock_irqrestore(&queue->response_lock, flags); - if (notify) - notify_remote_via_irq(queue->tx_irq); - if (cons == end) break; txp = RING_GET_REQUEST(&queue->tx, cons++); @@ -1657,7 +1653,6 @@ static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx, { struct pending_tx_info *pending_tx_info; pending_ring_idx_t index; - int notify; unsigned long flags; pending_tx_info = &queue->pending_tx_info[pending_idx]; @@ -1673,12 +1668,9 @@ static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx, index = pending_index(queue->pending_prod++); queue->pending_ring[index] = pending_idx; - RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&queue->tx, notify); + push_tx_responses(queue); spin_unlock_irqrestore(&queue->response_lock, flags); - - if (notify) - notify_remote_via_irq(queue->tx_irq); } @@ -1699,6 +1691,15 @@ static void make_tx_response(struct xenvif_queue *queue, 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); +} + static struct xen_netif_rx_response *make_rx_response(struct xenvif_queue *queue, u16 id, s8 st, diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index e9b960f0ff32..720aaf6313d2 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/address.c b/drivers/of/address.c index ad2906919d45..78a7dcbec7d8 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 adb8764861c0..8f165b112e03 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -715,13 +715,8 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent, { struct device_node *child; int len; - const char *end; - end = strchr(path, ':'); - if (!end) - end = strchrnul(path, '/'); - - len = end - path; + len = strcspn(path, "/:"); if (!len) return NULL; @@ -1893,10 +1888,8 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) name = of_get_property(of_chosen, "linux,stdout-path", NULL); if (IS_ENABLED(CONFIG_PPC) && !name) name = of_get_property(of_aliases, "stdout", NULL); - if (name) { + if (name) of_stdout = of_find_node_opts_by_path(name, &of_stdout_options); - add_preferred_console("stdout-path", 0, NULL); - } } if (!of_aliases) diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 0d7765807f49..1a7980692f25 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/unittest.c b/drivers/of/unittest.c index aba8946cac46..52c45c7df07f 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -97,6 +97,11 @@ static void __init of_selftest_find_node_by_name(void) "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); diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 1f4ea6f2d910..2e9f84fdd9ce 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-spear13xx.c b/drivers/pci/host/pcie-spear13xx.c index 866465fd3dbf..020d78890719 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 7d48ecae6695..788db48dbbad 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/pci-acpi.c b/drivers/pci/pci-acpi.c index 5eba74716900..6f6f175f51f7 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -257,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); diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index c6849d9e86ce..167fe411ce2e 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/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 3bb49252a098..45f67c63d385 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 f1a7ca04d89e..27e94b30cf96 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 1f67b3ba70fb..000000000000 --- 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 7c99ca256f05..8ccc3952c13d 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 a12d35338313..3791838f4bd4 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 f86cbe68ddaf..179cbf9451aa 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 f017b2f2a54e..df7519a39ba0 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 236a52ad94eb..f30bbb0fb3b2 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 0b9de88579b1..765da90a536f 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 04374018425f..e2a0be750ad9 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 1c139aa0d074..2ed1735a076a 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 34915b4202f1..d6b22659cac1 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 9b2848e6115d..933435214acc 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 6c80154e8bff..51b459db9137 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 efe724f97e02..93252e053a31 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 6f4aef3db248..4757e765696a 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 44ee983d57fe..86665e9dc399 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 22011c3b6a4b..7d4c33643768 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 95c88f929f27..2ba610b72ca2 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 8e87f54671f3..bc42d6a8939f 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 29214a36ea28..2263cd010032 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 5afe03e28b91..2062c224e32f 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 3034fd03bced..82f691eeeec4 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 f4cd0b9b2438..a4814066ea08 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 24c5d88f943f..3c68a8e5e0dd 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 3d0744337736..f8e171b76693 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 5a51523a3459..e248e81a0f9e 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 15c0fab2bfa1..b4e94471f3d5 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/power/88pm860x_battery.c b/drivers/power/88pm860x_battery.c index bd3c997f4fee..d49579b227ec 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 734ec4afa14d..0e448c68c02b 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 27b751b995fb..4091fb092d06 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 36f9e0d10111..b7b0181c95e5 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 4ebf7b0819f7..8f8044e1acf3 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 8c8d170ff0f8..e388171f4e58 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 c908658aa31a..3830dade5d69 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 ab54b8dea670..541f702e0451 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 39763015b360..9d1a7fbcaed4 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 000000000000..ca1cc5a47eb1 --- /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 1f49986fc605..6c534dcbc19c 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 d0e8236a6404..407c4af83891 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 d022b823305b..961a18930027 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 b72ba7c1bd69..a57433de5c24 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 14b0d85318eb..0aed13f90891 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 594e4dbc2d51..2da9ed8ccbb5 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 78cd5d66144b..5ca0f4d90792 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 d17250f745c2..830ec46fe7d0 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 000000000000..60099815296e --- /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 85b4e6eca0b1..80f73ccb77ab 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 9f418fa879e5..a7a0427343f3 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 0a5acc6fc6f0..56d583dae908 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 39694883d3bf..ed4d756d21e4 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 d72733e4f93a..fedc5818fab7 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 29eba88a2963..a50bb988c69a 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 b7424c8501f1..c5869b1941ac 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 de3f39e6fa8e..9fa4acc107ca 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 9d694605cdb7..f03014ea1dc4 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 0b4cf9d63291..f2a7d970388f 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 9cd391d61819..abdfc21ec13f 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 32de636dcd73..7e741f1d3cd5 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 21fc233c7d61..f5a48fd68b01 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 e31c927a6d16..daeb0860736c 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 ef4103ee6021..a36bcaf62dd4 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 14d44706327b..8689c80202b5 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 1da6c5fbdff5..6cc5e87ec031 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 b042970fdeaf..754879eb59f6 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 99e3cdcd3e11..bf2b4b3a7cae 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 a6d45eef64dd..57eb5c2bfc21 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 aefa0c9a3007..0b2eab571528 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 08694c7a9f38..47448d4bc6cd 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 ad9cde705de1..a944338a39de 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 771c4f0fb8ac..d05597b4e40f 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 0c52e2a0d90c..dfe1ee89f7c7 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 777324992c59..cc0893ffbf7e 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 8ce3cc0195df..24181cf9717b 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 023d24993b87..9c8d5253812c 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 694e8cddd5c1..2ed4a4a6b3c5 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 effa093c37b0..2d41a43fc81a 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 62653f50a524..9134e3d2d95e 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 27f6646731b0..aad9c3318c02 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 11de15bae52e..dbe06c368743 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 4b72ea51c364..9847cfb7e23d 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 13584e24736a..01c7055c4200 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 5385460e23bb..9ab7f562a83b 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 faedf16c8111..c70f1bffe038 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 27383de9caa8..a488877a3538 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 000000000000..5560b0dbc180 --- /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 c4049f45663f..d3c7d245ae63 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 be12d9b92957..6a9bf7089373 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 b0e5002f8deb..f07e93c97ba3 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 7b898f41c595..a7a6877b4e16 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 a01aacb32f59..ac6206951d58 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 5948ce058bdd..0ffe5cd3abf6 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 c7b7b4018df3..de1178659d4b 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 acf84e80fe98..0b60a0b5878b 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 f26b1fa00fe1..f986e0cca7ac 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 f4d80df627c7..6e88c1b37945 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 0f4e5971dff5..7e8fbd29c30e 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 d35b83e635b5..02a522cb7753 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 7ef445a6cfa6..f5817e422d64 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 56fb509f4be0..2e33109ca8c7 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 3bed2f55cf7d..0161bdabd5a3 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 b3607e2906d2..5c5880664e09 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 a8e6203673ad..c2f09ed35050 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 814d2e31f0c9..b201e3facf73 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 97b5e4ee1ca4..63d4033eb683 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 9eec453b745d..2ff73d72ca34 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 8169165904c0..a1d07d347c20 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 20079006459a..4116e74effa4 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 1245dca79009..443eaab933fc 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 @@ -3495,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; @@ -3529,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); @@ -3626,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 */ @@ -3643,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++) { @@ -3704,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: @@ -3800,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; @@ -3936,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; @@ -3949,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/da9211-regulator.c b/drivers/regulator/da9211-regulator.c index 01343419555e..df79e4b1946e 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 2d16b9f16de7..3963dfad766c 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 8f785bc9e510..6ec1d400adae 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 07b313e51b21..9665a488e2f1 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 7eee2ca18541..4071d74fa828 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 9205f433573c..8217613807d3 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 00c5cc3d9546..e254272585b2 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/stw481x-vmmc.c b/drivers/regulator/stw481x-vmmc.c index 89025f560259..7d2ae3e9e942 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 e2cffe01b807..fb991ec76423 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 7ec7c390eeda..95f6b040186e 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 92f6af6da699..73354ee27877 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 472a5adc4642..c29ba7e14304 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 37215cf983e9..d43ee409a5f2 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 1d0340fdb820..9b725c553058 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 b4f7744f6751..b283a1a573b3 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -324,7 +324,7 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id) ret = IRQ_HANDLED; } - spin_lock(&suspended_lock); + spin_unlock(&suspended_lock); return ret; } diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c index 5bce904b7ee6..32df1d812367 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 e2436d140175..3a6fd3a8a2ec 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 3c3f8d10ab43..09d422b9f7f7 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-test.c b/drivers/rtc/rtc-test.c index 8f86fa91de1a..3a2da4c892d6 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 eb71872d0361..7728d5e32bf4 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/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index a7cc61837818..923a2b5a2439 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 9219953ee949..d9afc51af7d3 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 932d9cc98d2f..9c706d8c1441 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/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 99f43b7fc9ab..ab4879e12ea7 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 54d7a6cbb98a..b1a263137a23 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 5d60a868830d..2aa85e398f76 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/spi/Kconfig b/drivers/spi/Kconfig index ab8dfbef6f1b..198f96b7fb45 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 06de34001c66..a2f40b1b2225 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. */ diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 419a782ab6d5..f63864a893c5 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 3fb91c81015a..1520554978a3 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 37079937d2f7..a3d65b4f4944 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 c616e41521be..06b34e5bcfa3 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 3ce39d10fafb..bb1052e748f2 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,35 +140,33 @@ 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) @@ -156,35 +186,33 @@ 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) @@ -196,37 +224,36 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *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_writel(dws, DW_SPI_DMARDLR, 0xf); + dw_writel(dws, DW_SPI_DMATDLR, 0x10); - dw_writew(dws, DW_SPI_DMARDLR, 0xf); - dw_writew(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); - spi_enable_chip(dws, 1); + dws->transfer_handler = dma_transfer; + + 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); + /* Prepare the TX dma transfer */ + txdesc = dw_spi_dma_prepare_tx(dws, xfer); - /* 2. Prepare the TX dma transfer */ - txdesc = dw_spi_dma_prepare_tx(dws); - - /* 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) { @@ -244,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 @@ -280,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.c b/drivers/spi/spi-dw.c index 4847afba89f4..8d67d03c71eb 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, @@ -622,11 +499,11 @@ static void spi_hw_init(struct device *dev, struct dw_spi *dws) u32 fifo; for (fifo = 1; fifo < 256; fifo++) { - dw_writew(dws, DW_SPI_TXFLTR, fifo); - if (fifo != dw_readw(dws, DW_SPI_TXFLTR)) + 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 == 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 3d32be68c142..6c91391c1a4f 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 d1a39249704a..5fe54cda309f 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 e649bc7d4c08..788e2b176a4f 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,8 +499,6 @@ 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; if (xfer->len > SPFI_TRANSACTION_TSIZE_MASK) { @@ -466,23 +508,6 @@ static int img_spfi_transfer_one(struct spi_master *master, return -EINVAL; } - /* - * 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); - } - img_spfi_config(master, spi, xfer); if (master->can_dma && master->can_dma(master, spi, xfer)) ret = img_spfi_start_dma(master, spi, xfer); @@ -492,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) { @@ -591,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 6fea4af51c41..f08e812b2984 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 ecae0d4e2945..965d2bdcfdcc 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 b283d537d16a..e99d6a93d394 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 d890d309dff9..35b332dacb13 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 3c0844457c07..55576db31549 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 ee513a85296b..94af80676684 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 @@ -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 6f72ad01e041..e3223ac75a7c 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 ff9cdbdb6672..810a7fae3479 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 1a777dc261d6..68e7efeb9a27 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 46ce47076e63..186924aa4740 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 9231c34b5a5c..b1c6731fbf27 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 5a56acf8a43e..36af4d48a700 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 2faeaa7b57a8..f17c0abe299f 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.c b/drivers/spi/spi.c index c64a3e59fce3..d5d7d2235163 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 4eb7a980e670..92c909eed6b5 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/iio/Kconfig b/drivers/staging/iio/Kconfig index 24183028bd71..6d5b38d69578 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/magnetometer/hmc5843_core.c b/drivers/staging/iio/magnetometer/hmc5843_core.c index fd171d8b38fb..90cc18b703cf 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/nvec/nvec_power.c b/drivers/staging/nvec/nvec_power.c index 6a1459d4f8fb..30b66c3c9b73 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 4324282afe49..03b2a90b9ac0 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 941b2adca95a..7626f635f160 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 c42cde59f598..c4286ccac320 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 50bad55a0c42..77d64251af40 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 1c197bad6132..bdd8731a4daa 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 6b3c32954689..c36bd7c29136 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 58f49ff69b14..7faa6aef9a4d 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 1045dcd7bf65..f6c954c4635f 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 9a2f9d3a6e70..3e7297411110 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 460e93109473..6c8bd6bc175c 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 0adc0f650213..ac3cbabdbdf0 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 97b486c3dda1..583e755d8091 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/st/st_thermal.c b/drivers/thermal/st/st_thermal.c index d1ec5804c0bb..76c515dd802b 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 067bfcdb91d6..fc0c9e198710 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 26d36a242bb8..3df5b7890703 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 174d3bcf8bd7..4108db7e10c1 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; } diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 2ab229ddee38..6ae5b8560e4d 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -119,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); + */ } } @@ -163,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 */ @@ -187,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); + */ } } diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index b1893f3f88f1..3ad1458bfeb0 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/samsung.c b/drivers/tty/serial/samsung.c index af821a908720..cf08876922f1 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/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index ff451048c1ac..4bfb7ac0239f 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/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c index c6b35b77dab7..61d538aa2346 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/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 02e3e2d4ea56..6cf047878dba 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/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index 298b46112b1a..39f49f1ad22f 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_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index e3dae47baef3..3a5ae9900b1e 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), -}; - -static 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), -}; - -static 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/g_zero.h b/drivers/usb/gadget/function/g_zero.h index 2ce28b9d97cc..15f180904f8a 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/legacy/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c index 3a494168661e..6e0a019aad54 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 ff97ac93ac03..5ee95152493c 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 663f7908b15c..be0964a801e8 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 a7865c4b0498..0827d7c96527 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 fd53c9ebd662..2af32e26fafc 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -115,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) { @@ -130,7 +131,6 @@ 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) { diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 5fb66db89e05..73485fa4372f 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1729,7 +1729,7 @@ static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci, if (!command) return; - ep->ep_state |= EP_HALTED | EP_RECENTLY_HALTED; + ep->ep_state |= EP_HALTED; ep->stopped_stream = stream_id; xhci_queue_reset_ep(xhci, command, slot_id, ep_index); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index b06d1a53652d..ec8ac1674854 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1338,12 +1338,6 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) goto exit; } - /* Reject urb if endpoint is in soft reset, queue must stay empty */ - if (xhci->devs[slot_id]->eps[ep_index].ep_state & EP_CONFIG_PENDING) { - xhci_warn(xhci, "Can't enqueue URB while ep is in soft reset\n"); - ret = -EINVAL; - } - if (usb_endpoint_xfer_isoc(&urb->ep->desc)) size = urb->number_of_packets; else @@ -2954,36 +2948,23 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, } } -/* Called after clearing a halted device. USB core should have sent the control +/* Called when clearing halted device. The core should have sent the control * message to clear the device halt condition. The host side of the halt should - * already be cleared with a reset endpoint command issued immediately when the - * STALL tx event was received. + * already be cleared with a reset endpoint command issued when the STALL tx + * event was received. + * + * Context: in_interrupt */ void xhci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) { struct xhci_hcd *xhci; - struct usb_device *udev; - struct xhci_virt_device *virt_dev; - struct xhci_virt_ep *virt_ep; - struct xhci_input_control_ctx *ctrl_ctx; - struct xhci_command *command; - unsigned int ep_index, ep_state; - unsigned long flags; - u32 ep_flag; xhci = hcd_to_xhci(hcd); - udev = (struct usb_device *) ep->hcpriv; - if (!ep->hcpriv) - return; - virt_dev = xhci->devs[udev->slot_id]; - ep_index = xhci_get_endpoint_index(&ep->desc); - virt_ep = &virt_dev->eps[ep_index]; - ep_state = virt_ep->ep_state; /* - * Implement the config ep command in xhci 4.6.8 additional note: + * We might need to implement the config ep cmd in xhci 4.8.1 note: * The Reset Endpoint Command may only be issued to endpoints in the * Halted state. If software wishes reset the Data Toggle or Sequence * Number of an endpoint that isn't in the Halted state, then software @@ -2991,72 +2972,9 @@ void xhci_endpoint_reset(struct usb_hcd *hcd, * for the target endpoint. that is in the Stopped state. */ - if (ep_state & SET_DEQ_PENDING || ep_state & EP_RECENTLY_HALTED) { - virt_ep->ep_state &= ~EP_RECENTLY_HALTED; - xhci_dbg(xhci, "ep recently halted, no toggle reset needed\n"); - return; - } - - /* Only interrupt and bulk ep's use Data toggle, USB2 spec 5.5.4-> */ - if (usb_endpoint_xfer_control(&ep->desc) || - usb_endpoint_xfer_isoc(&ep->desc)) - return; - - ep_flag = xhci_get_endpoint_flag(&ep->desc); - - if (ep_flag == SLOT_FLAG || ep_flag == EP0_FLAG) - return; - - command = xhci_alloc_command(xhci, true, true, GFP_NOWAIT); - if (!command) { - xhci_err(xhci, "Could not allocate xHCI command structure.\n"); - return; - } - - spin_lock_irqsave(&xhci->lock, flags); - - /* block ringing ep doorbell */ - virt_ep->ep_state |= EP_CONFIG_PENDING; - - /* - * Make sure endpoint ring is empty before resetting the toggle/seq. - * Driver is required to synchronously cancel all transfer request. - * - * xhci 4.6.6 says we can issue a configure endpoint command on a - * running endpoint ring as long as it's idle (queue empty) - */ - - if (!list_empty(&virt_ep->ring->td_list)) { - dev_err(&udev->dev, "EP not empty, refuse reset\n"); - spin_unlock_irqrestore(&xhci->lock, flags); - goto cleanup; - } - - xhci_dbg(xhci, "Reset toggle/seq for slot %d, ep_index: %d\n", - udev->slot_id, ep_index); - - ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx); - if (!ctrl_ctx) { - xhci_err(xhci, "Could not get input context, bad type. virt_dev: %p, in_ctx %p\n", - virt_dev, virt_dev->in_ctx); - spin_unlock_irqrestore(&xhci->lock, flags); - goto cleanup; - } - xhci_setup_input_ctx_for_config_ep(xhci, command->in_ctx, - virt_dev->out_ctx, ctrl_ctx, - ep_flag, ep_flag); - xhci_endpoint_copy(xhci, command->in_ctx, virt_dev->out_ctx, ep_index); - - xhci_queue_configure_endpoint(xhci, command, command->in_ctx->dma, - udev->slot_id, false); - xhci_ring_cmd_db(xhci); - spin_unlock_irqrestore(&xhci->lock, flags); - - wait_for_completion(command->completion); - -cleanup: - virt_ep->ep_state &= ~EP_CONFIG_PENDING; - xhci_free_command(xhci, command); + /* For now just print debug to follow the situation */ + xhci_dbg(xhci, "Endpoint 0x%x ep reset callback called\n", + ep->desc.bEndpointAddress); } static int xhci_check_streams_endpoint(struct xhci_hcd *xhci, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 265ab1771d24..8e421b89632d 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -865,8 +865,6 @@ struct xhci_virt_ep { #define EP_HAS_STREAMS (1 << 4) /* Transitioning the endpoint to not using streams, don't enqueue URBs */ #define EP_GETTING_NO_STREAMS (1 << 5) -#define EP_RECENTLY_HALTED (1 << 6) -#define EP_CONFIG_PENDING (1 << 7) /* ---- Related to URB cancellation ---- */ struct list_head cancelled_td_list; struct xhci_td *stopped_td; diff --git a/drivers/usb/isp1760/isp1760-core.c b/drivers/usb/isp1760/isp1760-core.c index b9827556455f..bfa402cf3a27 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-udc.c b/drivers/usb/isp1760/isp1760-udc.c index 9612d7990565..3fc4fe770253 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 14e1628483d9..39db8b603627 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/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 0b1bd2369293..f5b3b928941b 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 403fab772724..7b3035ff9434 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 000fd892455f..6ed67ea4ef7e 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/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 3086dec0ef53..8eb68a31cab6 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: */ @@ -1883,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 56b1b55c4751..4e4f46f3c89c 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -561,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 **/ diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index dd97d8b572c3..4f7e072e4e00 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/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 82570425fdfe..c85ea530085f 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -113,6 +113,13 @@ 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", diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 8d4f3f1ff799..71df240a467a 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/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 0413157f3b49..6a356e344f82 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 cad569890908..6010d7ec0a0f 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/imgpdc_wdt.c b/drivers/watchdog/imgpdc_wdt.c index c8def68d9e4c..0deaa4f971f5 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 a87f6df6e85f..938b987de551 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 b812462083fc..94d96809e686 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/balloon.c b/drivers/xen/balloon.c index 0b52d92cb2e5..fd933695f232 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/xen-scsiback.c b/drivers/xen/xen-scsiback.c index 9faca6a60bb0..42bd55a6c237 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -1659,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; |