diff options
Diffstat (limited to 'drivers')
320 files changed, 13654 insertions, 6754 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_root.c b/drivers/acpi/pci_root.c index 68a5f712cd19..1b5569c092c6 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -423,8 +423,7 @@ out: } EXPORT_SYMBOL(acpi_pci_osc_control_set); -static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, - int *clear_aspm) +static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm) { u32 support, control, requested; acpi_status status; @@ -495,10 +494,12 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, decode_osc_control(root, "OS now controls", control); if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { /* - * We have ASPM control, but the FADT indicates - * that it's unsupported. Clear it. + * We have ASPM control, but the FADT indicates that + * it's unsupported. Leave existing configuration + * intact and prevent the OS from touching it. */ - *clear_aspm = 1; + dev_info(&device->dev, "FADT indicates ASPM is unsupported, using BIOS configuration\n"); + *no_aspm = 1; } } else { decode_osc_control(root, "OS requested", requested); @@ -525,7 +526,7 @@ static int acpi_pci_root_add(struct acpi_device *device, int result; struct acpi_pci_root *root; acpi_handle handle = device->handle; - int no_aspm = 0, clear_aspm = 0; + int no_aspm = 0; bool hotadd = system_state != SYSTEM_BOOTING; root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL); @@ -584,7 +585,7 @@ static int acpi_pci_root_add(struct acpi_device *device, root->mcfg_addr = acpi_pci_root_get_mcfg_addr(handle); - negotiate_os_control(root, &no_aspm, &clear_aspm); + negotiate_os_control(root, &no_aspm); /* * TBD: Need PCI interface for enumeration/configuration of roots. @@ -607,10 +608,6 @@ static int acpi_pci_root_add(struct acpi_device *device, goto remove_dmar; } - if (clear_aspm) { - dev_info(&device->dev, "Disabling ASPM (FADT indicates it is unsupported)\n"); - pcie_clear_aspm(root->bus); - } if (no_aspm) pcie_no_aspm(); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 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/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/regcache.c b/drivers/base/regmap/regcache.c index 87db9893b463..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[] = { diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index dbfe6a69c3da..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" 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/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/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 ec57ba2bbd87..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, 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 58597fbcc046..28aa4b7bb602 100644 --- a/drivers/clocksource/timer-sun5i.c +++ b/drivers/clocksource/timer-sun5i.c @@ -17,6 +17,7 @@ #include <linux/irq.h> #include <linux/irqreturn.h> #include <linux/reset.h> +#include <linux/slab.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> @@ -36,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 @@ -45,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; @@ -76,75 +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 int sun5i_rate_cb_clksrc(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_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"); @@ -155,35 +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)); - - 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); - - /* 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); - - ret = setup_irq(irq, &sun5i_timer_irq); - if (ret) - pr_warn("failed to setup irq %d\n", irq); + 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.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/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/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/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/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 2eebd28b4c40..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) { @@ -91,8 +93,8 @@ static void dmi_table(u8 *buf, u32 len, int num, * off the end of the table (should never happen but sometimes does * on bogus implementations.) */ - while ((!num || 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; /* @@ -101,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); /* @@ -118,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 *)) @@ -130,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); @@ -201,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); @@ -473,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])) { @@ -506,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; @@ -531,14 +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); 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; @@ -893,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/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 679b10e34fb5..b6f076b213bc 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2121,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++) @@ -2201,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/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/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/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-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/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/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/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/adc/Kconfig b/drivers/iio/adc/Kconfig index 46379b1fb25b..d82af14986ba 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -135,6 +135,15 @@ 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 HAVE_CLK || REGULATOR 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/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/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index af1dc6a1c0a1..43429ab62228 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -133,19 +133,25 @@ struct iommu_ops *of_iommu_get_ops(struct device_node *np) return ops; } -struct iommu_ops *of_iommu_configure(struct device *dev) +struct iommu_ops *of_iommu_configure(struct device *dev, + struct device_node *master_np) { struct of_phandle_args iommu_spec; struct device_node *np; struct iommu_ops *ops = NULL; int idx = 0; + if (dev_is_pci(dev)) { + dev_err(dev, "IOMMU is currently not supported for PCI\n"); + return NULL; + } + /* * We don't currently walk up the tree looking for a parent IOMMU. * See the `Notes:' section of * Documentation/devicetree/bindings/iommu/iommu.txt */ - while (!of_parse_phandle_with_args(dev->of_node, "iommus", + while (!of_parse_phandle_with_args(master_np, "iommus", "#iommu-cells", idx, &iommu_spec)) { np = iommu_spec.np; diff --git a/drivers/md/md.c b/drivers/md/md.c index 717daad71fb1..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); diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 3ed9f42ddca6..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) @@ -524,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/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 c53f14a7ce54..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) && @@ -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/of/device.c b/drivers/of/device.c index 46d6c75c1404..20c1332a0018 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -2,6 +2,9 @@ #include <linux/kernel.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/of_iommu.h> +#include <linux/dma-mapping.h> #include <linux/init.h> #include <linux/module.h> #include <linux/mod_devicetable.h> @@ -66,6 +69,87 @@ int of_device_add(struct platform_device *ofdev) return device_add(&ofdev->dev); } +/** + * of_dma_configure - Setup DMA configuration + * @dev: Device to apply DMA configuration + * @np: Pointer to OF node having DMA configuration + * + * Try to get devices's DMA configuration from DT and update it + * accordingly. + * + * If platform code needs to use its own special DMA configuration, it + * can use a platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE events + * to fix up DMA configuration. + */ +void of_dma_configure(struct device *dev, struct device_node *np) +{ + u64 dma_addr, paddr, size; + int ret; + bool coherent; + unsigned long offset; + struct iommu_ops *iommu; + + /* + * Set default coherent_dma_mask to 32 bit. Drivers are expected to + * setup the correct supported mask. + */ + if (!dev->coherent_dma_mask) + dev->coherent_dma_mask = DMA_BIT_MASK(32); + + /* + * Set it to coherent_dma_mask by default if the architecture + * code has not set it. + */ + if (!dev->dma_mask) + dev->dma_mask = &dev->coherent_dma_mask; + + ret = of_dma_get_range(np, &dma_addr, &paddr, &size); + if (ret < 0) { + dma_addr = offset = 0; + size = dev->coherent_dma_mask + 1; + } else { + offset = PFN_DOWN(paddr - dma_addr); + + /* + * Add a work around to treat the size as mask + 1 in case + * it is defined in DT as a mask. + */ + if (size & 1) { + dev_warn(dev, "Invalid size 0x%llx for dma-range\n", + size); + size = size + 1; + } + + if (!size) { + dev_err(dev, "Adjusted size 0x%llx invalid\n", size); + return; + } + dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset); + } + + dev->dma_pfn_offset = offset; + + /* + * Limit coherent and dma mask based on size and default mask + * set by the driver. + */ + dev->coherent_dma_mask = min(dev->coherent_dma_mask, + DMA_BIT_MASK(ilog2(dma_addr + size))); + *dev->dma_mask = min((*dev->dma_mask), + DMA_BIT_MASK(ilog2(dma_addr + size))); + + coherent = of_dma_is_coherent(np); + dev_dbg(dev, "device is%sdma coherent\n", + coherent ? " " : " not "); + + iommu = of_iommu_configure(dev, np); + dev_dbg(dev, "device is%sbehind an iommu\n", + iommu ? " " : " not "); + + arch_setup_dma_ops(dev, dma_addr, size, iommu, coherent); +} +EXPORT_SYMBOL_GPL(of_dma_configure); + int of_device_register(struct platform_device *pdev) { device_initialize(&pdev->dev); diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index 62426d81a4d6..5751dc5b6494 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -2,6 +2,7 @@ #include <linux/export.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/of_pci.h> #include <linux/slab.h> @@ -116,6 +117,26 @@ int of_get_pci_domain_nr(struct device_node *node) } EXPORT_SYMBOL_GPL(of_get_pci_domain_nr); +/** + * of_pci_dma_configure - Setup DMA configuration + * @dev: ptr to pci_dev struct of the PCI device + * + * Function to update PCI devices's DMA configuration using the same + * info from the OF node of host bridge's parent (if any). + */ +void of_pci_dma_configure(struct pci_dev *pci_dev) +{ + struct device *dev = &pci_dev->dev; + struct device *bridge = pci_get_host_bridge_device(pci_dev); + + if (!bridge->parent) + return; + + of_dma_configure(dev, bridge->parent->of_node); + pci_put_host_bridge_device(bridge); +} +EXPORT_SYMBOL_GPL(of_pci_dma_configure); + #if defined(CONFIG_OF_ADDRESS) /** * of_pci_get_host_bridge_resources - Parse PCI host bridge resources from DT diff --git a/drivers/of/platform.c b/drivers/of/platform.c index b189733a1539..a01f57c9e34e 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -19,7 +19,6 @@ #include <linux/slab.h> #include <linux/of_address.h> #include <linux/of_device.h> -#include <linux/of_iommu.h> #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> @@ -150,59 +149,6 @@ struct platform_device *of_device_alloc(struct device_node *np, } EXPORT_SYMBOL(of_device_alloc); -/** - * of_dma_configure - Setup DMA configuration - * @dev: Device to apply DMA configuration - * - * Try to get devices's DMA configuration from DT and update it - * accordingly. - * - * In case if platform code need to use own special DMA configuration,it - * can use Platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE event - * to fix up DMA configuration. - */ -static void of_dma_configure(struct device *dev) -{ - u64 dma_addr, paddr, size; - int ret; - bool coherent; - unsigned long offset; - struct iommu_ops *iommu; - - /* - * Set default dma-mask to 32 bit. Drivers are expected to setup - * the correct supported dma_mask. - */ - dev->coherent_dma_mask = DMA_BIT_MASK(32); - - /* - * Set it to coherent_dma_mask by default if the architecture - * code has not set it. - */ - if (!dev->dma_mask) - dev->dma_mask = &dev->coherent_dma_mask; - - ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size); - if (ret < 0) { - dma_addr = offset = 0; - size = dev->coherent_dma_mask; - } else { - offset = PFN_DOWN(paddr - dma_addr); - dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset); - } - dev->dma_pfn_offset = offset; - - coherent = of_dma_is_coherent(dev->of_node); - dev_dbg(dev, "device is%sdma coherent\n", - coherent ? " " : " not "); - - iommu = of_iommu_configure(dev); - dev_dbg(dev, "device is%sbehind an iommu\n", - iommu ? " " : " not "); - - arch_setup_dma_ops(dev, dma_addr, size, iommu, coherent); -} - static void of_dma_deconfigure(struct device *dev) { arch_teardown_dma_ops(dev); @@ -236,7 +182,7 @@ static struct platform_device *of_platform_device_create_pdata( dev->dev.bus = &platform_bus_type; dev->dev.platform_data = platform_data; - of_dma_configure(&dev->dev); + of_dma_configure(&dev->dev, dev->dev.of_node); if (of_device_add(dev) != 0) { of_dma_deconfigure(&dev->dev); @@ -299,7 +245,7 @@ static struct amba_device *of_amba_device_create(struct device_node *node, dev_set_name(&dev->dev, "%s", bus_id); else of_device_make_bus_id(&dev->dev); - of_dma_configure(&dev->dev); + of_dma_configure(&dev->dev, dev->dev.of_node); /* Allow the HW Peripheral ID to be overridden */ prop = of_get_property(node, "arm,primecell-periphid", NULL); diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c index 39b2dbe585aa..5f4a2e04c8d7 100644 --- a/drivers/pci/host-bridge.c +++ b/drivers/pci/host-bridge.c @@ -16,13 +16,27 @@ static struct pci_bus *find_pci_root_bus(struct pci_bus *bus) return bus; } -static struct pci_host_bridge *find_pci_host_bridge(struct pci_bus *bus) +struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus) { struct pci_bus *root_bus = find_pci_root_bus(bus); return to_pci_host_bridge(root_bus->bridge); } +struct device *pci_get_host_bridge_device(struct pci_dev *dev) +{ + struct pci_bus *root_bus = find_pci_root_bus(dev->bus); + struct device *bridge = root_bus->bridge; + + kobject_get(&bridge->kobj); + return bridge; +} + +void pci_put_host_bridge_device(struct device *dev) +{ + kobject_put(&dev->kobj); +} + void pci_set_host_bridge_release(struct pci_host_bridge *bridge, void (*release_fn)(struct pci_host_bridge *), void *release_data) @@ -34,7 +48,7 @@ void pci_set_host_bridge_release(struct pci_host_bridge *bridge, void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region, struct resource *res) { - struct pci_host_bridge *bridge = find_pci_host_bridge(bus); + struct pci_host_bridge *bridge = pci_find_host_bridge(bus); struct resource_entry *window; resource_size_t offset = 0; @@ -59,7 +73,7 @@ static bool region_contains(struct pci_bus_region *region1, void pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res, struct pci_bus_region *region) { - struct pci_host_bridge *bridge = find_pci_host_bridge(bus); + struct pci_host_bridge *bridge = pci_find_host_bridge(bus); struct resource_entry *window; resource_size_t offset = 0; diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 7b892a9cc4fc..1dfb567b3522 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -106,4 +106,23 @@ config PCI_VERSATILE bool "ARM Versatile PB PCI controller" depends on ARCH_VERSATILE +config PCIE_IPROC + tristate "Broadcom iProc PCIe controller" + depends on OF && ARM + default n + help + This enables the iProc PCIe core controller support for Broadcom's + iProc family of SoCs. An appropriate bus interface driver also needs + to be enabled + +config PCIE_IPROC_PLATFORM + tristate "Broadcom iProc PCIe platform bus driver" + depends on ARCH_BCM_IPROC || (ARM && COMPILE_TEST) + depends on OF + select PCIE_IPROC + default ARCH_BCM_IPROC + help + Say Y here if you want to use the Broadcom iProc PCIe controller + through the generic platform bus interface + endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index e61d91c92bf1..f733b4e27642 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -13,3 +13,5 @@ obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o obj-$(CONFIG_PCI_XGENE) += pci-xgene.o obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o +obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o +obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c index d202b37c3698..c139237e0e52 100644 --- a/drivers/pci/host/pci-exynos.c +++ b/drivers/pci/host/pci-exynos.c @@ -396,7 +396,7 @@ static void exynos_pcie_enable_irq_pulse(struct pcie_port *pp) /* enable INTX interrupt */ val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT | - IRQ_INTC_ASSERT | IRQ_INTD_ASSERT, + IRQ_INTC_ASSERT | IRQ_INTD_ASSERT; exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_EN_PULSE); } diff --git a/drivers/pci/host/pci-keystone-dw.c b/drivers/pci/host/pci-keystone-dw.c index 66d8ea41b972..f34892e0edb4 100644 --- a/drivers/pci/host/pci-keystone-dw.c +++ b/drivers/pci/host/pci-keystone-dw.c @@ -496,11 +496,12 @@ int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie, /* Index 1 is the application reg. space address */ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - ks_pcie->app = *res; ks_pcie->va_app_base = devm_ioremap_resource(pp->dev, res); if (IS_ERR(ks_pcie->va_app_base)) return PTR_ERR(ks_pcie->va_app_base); + ks_pcie->app = *res; + /* Create legacy IRQ domain */ ks_pcie->legacy_irq_domain = irq_domain_add_linear(ks_pcie->legacy_intc_np, diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci-layerscape.c index 68c9e5e9b0a8..4a6e62f67579 100644 --- a/drivers/pci/host/pci-layerscape.c +++ b/drivers/pci/host/pci-layerscape.c @@ -127,14 +127,11 @@ static int __init ls_pcie_probe(struct platform_device *pdev) pcie->dev = &pdev->dev; dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - if (!dbi_base) { - dev_err(&pdev->dev, "missing *regs* space\n"); - return -ENODEV; - } - pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base); - if (IS_ERR(pcie->dbi)) + if (IS_ERR(pcie->dbi)) { + dev_err(&pdev->dev, "missing *regs* space\n"); return PTR_ERR(pcie->dbi); + } pcie->scfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "fsl,pcie-scfg"); diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 1309cfbaa719..1ab863551920 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -129,6 +129,7 @@ struct mvebu_pcie_port { size_t memwin_size; phys_addr_t iowin_base; size_t iowin_size; + u32 saved_pcie_stat; }; static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg) @@ -899,6 +900,35 @@ static void mvebu_pcie_msi_enable(struct mvebu_pcie *pcie) pcie->msi->dev = &pcie->pdev->dev; } +static int mvebu_pcie_suspend(struct device *dev) +{ + struct mvebu_pcie *pcie; + int i; + + pcie = dev_get_drvdata(dev); + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = pcie->ports + i; + port->saved_pcie_stat = mvebu_readl(port, PCIE_STAT_OFF); + } + + return 0; +} + +static int mvebu_pcie_resume(struct device *dev) +{ + struct mvebu_pcie *pcie; + int i; + + pcie = dev_get_drvdata(dev); + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = pcie->ports + i; + mvebu_writel(port, port->saved_pcie_stat, PCIE_STAT_OFF); + mvebu_pcie_setup_hw(port); + } + + return 0; +} + static int mvebu_pcie_probe(struct platform_device *pdev) { struct mvebu_pcie *pcie; @@ -1056,6 +1086,8 @@ static int mvebu_pcie_probe(struct platform_device *pdev) mvebu_pcie_msi_enable(pcie); mvebu_pcie_enable(pcie); + platform_set_drvdata(pdev, pcie); + return 0; } @@ -1068,12 +1100,18 @@ static const struct of_device_id mvebu_pcie_of_match_table[] = { }; MODULE_DEVICE_TABLE(of, mvebu_pcie_of_match_table); +static struct dev_pm_ops mvebu_pcie_pm_ops = { + .suspend_noirq = mvebu_pcie_suspend, + .resume_noirq = mvebu_pcie_resume, +}; + static struct platform_driver mvebu_pcie_driver = { .driver = { .name = "mvebu-pcie", .of_match_table = mvebu_pcie_of_match_table, /* driver unloading/unbinding currently not supported */ .suppress_bind_attrs = true, + .pm = &mvebu_pcie_pm_ops, }, .probe = mvebu_pcie_probe, }; diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index dd6b84e6206c..367e28fa7564 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -301,6 +301,9 @@ static int rcar_pci_probe(struct platform_device *pdev) if (!mem_res || !mem_res->start) return -ENODEV; + if (mem_res->start & 0xFFFF) + return -EINVAL; + priv = devm_kzalloc(&pdev->dev, sizeof(struct rcar_pci_priv), GFP_KERNEL); if (!priv) diff --git a/drivers/pci/host/pci-versatile.c b/drivers/pci/host/pci-versatile.c index 464bf492ee2a..0863d9cc25f8 100644 --- a/drivers/pci/host/pci-versatile.c +++ b/drivers/pci/host/pci-versatile.c @@ -138,19 +138,19 @@ static int versatile_pci_probe(struct platform_device *pdev) LIST_HEAD(pci_res); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; versatile_pci_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(versatile_pci_base)) + return PTR_ERR(versatile_pci_base); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) - return -ENODEV; versatile_cfg_base[0] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(versatile_cfg_base[0])) + return PTR_ERR(versatile_cfg_base[0]); res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - if (!res) - return -ENODEV; versatile_cfg_base[1] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(versatile_cfg_base[1])) + return PTR_ERR(versatile_cfg_base[1]); ret = versatile_pci_parse_request_of_pci_ranges(&pdev->dev, &pci_res); if (ret) @@ -214,6 +214,7 @@ static int versatile_pci_probe(struct platform_device *pdev) pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); pci_assign_unassigned_bus_resources(bus); + pci_bus_add_devices(bus); return 0; } diff --git a/drivers/pci/host/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-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c new file mode 100644 index 000000000000..afad6c21fcfa --- /dev/null +++ b/drivers/pci/host/pcie-iproc-platform.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/of_address.h> +#include <linux/of_pci.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/phy/phy.h> + +#include "pcie-iproc.h" + +static int iproc_pcie_pltfm_probe(struct platform_device *pdev) +{ + struct iproc_pcie *pcie; + struct device_node *np = pdev->dev.of_node; + struct resource reg; + resource_size_t iobase = 0; + LIST_HEAD(res); + int ret; + + pcie = devm_kzalloc(&pdev->dev, sizeof(struct iproc_pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pcie->dev = &pdev->dev; + platform_set_drvdata(pdev, pcie); + + ret = of_address_to_resource(np, 0, ®); + if (ret < 0) { + dev_err(pcie->dev, "unable to obtain controller resources\n"); + return ret; + } + + pcie->base = devm_ioremap(pcie->dev, reg.start, resource_size(®)); + if (!pcie->base) { + dev_err(pcie->dev, "unable to map controller registers\n"); + return -ENOMEM; + } + + /* PHY use is optional */ + pcie->phy = devm_phy_get(&pdev->dev, "pcie-phy"); + if (IS_ERR(pcie->phy)) { + if (PTR_ERR(pcie->phy) == -EPROBE_DEFER) + return -EPROBE_DEFER; + pcie->phy = NULL; + } + + ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &iobase); + if (ret) { + dev_err(pcie->dev, + "unable to get PCI host bridge resources\n"); + return ret; + } + + pcie->resources = &res; + + ret = iproc_pcie_setup(pcie); + if (ret) { + dev_err(pcie->dev, "PCIe controller setup failed\n"); + return ret; + } + + return 0; +} + +static int iproc_pcie_pltfm_remove(struct platform_device *pdev) +{ + struct iproc_pcie *pcie = platform_get_drvdata(pdev); + + return iproc_pcie_remove(pcie); +} + +static const struct of_device_id iproc_pcie_of_match_table[] = { + { .compatible = "brcm,iproc-pcie", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table); + +static struct platform_driver iproc_pcie_pltfm_driver = { + .driver = { + .name = "iproc-pcie", + .of_match_table = of_match_ptr(iproc_pcie_of_match_table), + }, + .probe = iproc_pcie_pltfm_probe, + .remove = iproc_pcie_pltfm_remove, +}; +module_platform_driver(iproc_pcie_pltfm_driver); + +MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom iPROC PCIe platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c new file mode 100644 index 000000000000..329e1b54528b --- /dev/null +++ b/drivers/pci/host/pcie-iproc.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2014 Hauke Mehrtens <hauke@hauke-m.de> + * Copyright (C) 2015 Broadcom Corporatcommon ion + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/msi.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/mbus.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/of_address.h> +#include <linux/of_pci.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/phy/phy.h> + +#include "pcie-iproc.h" + +#define CLK_CONTROL_OFFSET 0x000 +#define EP_MODE_SURVIVE_PERST_SHIFT 1 +#define EP_MODE_SURVIVE_PERST BIT(EP_MODE_SURVIVE_PERST_SHIFT) +#define RC_PCIE_RST_OUTPUT_SHIFT 0 +#define RC_PCIE_RST_OUTPUT BIT(RC_PCIE_RST_OUTPUT_SHIFT) + +#define CFG_IND_ADDR_OFFSET 0x120 +#define CFG_IND_ADDR_MASK 0x00001ffc + +#define CFG_IND_DATA_OFFSET 0x124 + +#define CFG_ADDR_OFFSET 0x1f8 +#define CFG_ADDR_BUS_NUM_SHIFT 20 +#define CFG_ADDR_BUS_NUM_MASK 0x0ff00000 +#define CFG_ADDR_DEV_NUM_SHIFT 15 +#define CFG_ADDR_DEV_NUM_MASK 0x000f8000 +#define CFG_ADDR_FUNC_NUM_SHIFT 12 +#define CFG_ADDR_FUNC_NUM_MASK 0x00007000 +#define CFG_ADDR_REG_NUM_SHIFT 2 +#define CFG_ADDR_REG_NUM_MASK 0x00000ffc +#define CFG_ADDR_CFG_TYPE_SHIFT 0 +#define CFG_ADDR_CFG_TYPE_MASK 0x00000003 + +#define CFG_DATA_OFFSET 0x1fc + +#define SYS_RC_INTX_EN 0x330 +#define SYS_RC_INTX_MASK 0xf + +static inline struct iproc_pcie *sys_to_pcie(struct pci_sys_data *sys) +{ + return sys->private_data; +} + +/** + * Note access to the configuration registers are protected at the higher layer + * by 'pci_lock' in drivers/pci/access.c + */ +static void __iomem *iproc_pcie_map_cfg_bus(struct pci_bus *bus, + unsigned int devfn, + int where) +{ + struct pci_sys_data *sys = bus->sysdata; + struct iproc_pcie *pcie = sys_to_pcie(sys); + unsigned slot = PCI_SLOT(devfn); + unsigned fn = PCI_FUNC(devfn); + unsigned busno = bus->number; + u32 val; + + /* root complex access */ + if (busno == 0) { + if (slot >= 1) + return NULL; + writel(where & CFG_IND_ADDR_MASK, + pcie->base + CFG_IND_ADDR_OFFSET); + return (pcie->base + CFG_IND_DATA_OFFSET); + } + + if (fn > 1) + return NULL; + + /* EP device access */ + val = (busno << CFG_ADDR_BUS_NUM_SHIFT) | + (slot << CFG_ADDR_DEV_NUM_SHIFT) | + (fn << CFG_ADDR_FUNC_NUM_SHIFT) | + (where & CFG_ADDR_REG_NUM_MASK) | + (1 & CFG_ADDR_CFG_TYPE_MASK); + writel(val, pcie->base + CFG_ADDR_OFFSET); + + return (pcie->base + CFG_DATA_OFFSET); +} + +static struct pci_ops iproc_pcie_ops = { + .map_bus = iproc_pcie_map_cfg_bus, + .read = pci_generic_config_read32, + .write = pci_generic_config_write32, +}; + +static void iproc_pcie_reset(struct iproc_pcie *pcie) +{ + u32 val; + + /* + * Configure the PCIe controller as root complex and send a downstream + * reset + */ + val = EP_MODE_SURVIVE_PERST | RC_PCIE_RST_OUTPUT; + writel(val, pcie->base + CLK_CONTROL_OFFSET); + udelay(250); + val &= ~EP_MODE_SURVIVE_PERST; + writel(val, pcie->base + CLK_CONTROL_OFFSET); + msleep(250); +} + +static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus) +{ + u8 hdr_type; + u32 link_ctrl; + u16 pos, link_status; + int link_is_active = 0; + + /* make sure we are not in EP mode */ + pci_bus_read_config_byte(bus, 0, PCI_HEADER_TYPE, &hdr_type); + if ((hdr_type & 0x7f) != PCI_HEADER_TYPE_BRIDGE) { + dev_err(pcie->dev, "in EP mode, hdr=%#02x\n", hdr_type); + return -EFAULT; + } + + /* force class to PCI_CLASS_BRIDGE_PCI (0x0604) */ + pci_bus_write_config_word(bus, 0, PCI_CLASS_DEVICE, + PCI_CLASS_BRIDGE_PCI); + + /* check link status to see if link is active */ + pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP); + pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA, &link_status); + if (link_status & PCI_EXP_LNKSTA_NLW) + link_is_active = 1; + + if (!link_is_active) { + /* try GEN 1 link speed */ +#define PCI_LINK_STATUS_CTRL_2_OFFSET 0x0dc +#define PCI_TARGET_LINK_SPEED_MASK 0xf +#define PCI_TARGET_LINK_SPEED_GEN2 0x2 +#define PCI_TARGET_LINK_SPEED_GEN1 0x1 + pci_bus_read_config_dword(bus, 0, + PCI_LINK_STATUS_CTRL_2_OFFSET, + &link_ctrl); + if ((link_ctrl & PCI_TARGET_LINK_SPEED_MASK) == + PCI_TARGET_LINK_SPEED_GEN2) { + link_ctrl &= ~PCI_TARGET_LINK_SPEED_MASK; + link_ctrl |= PCI_TARGET_LINK_SPEED_GEN1; + pci_bus_write_config_dword(bus, 0, + PCI_LINK_STATUS_CTRL_2_OFFSET, + link_ctrl); + msleep(100); + + pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP); + pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA, + &link_status); + if (link_status & PCI_EXP_LNKSTA_NLW) + link_is_active = 1; + } + } + + dev_info(pcie->dev, "link: %s\n", link_is_active ? "UP" : "DOWN"); + + return link_is_active ? 0 : -ENODEV; +} + +static void iproc_pcie_enable(struct iproc_pcie *pcie) +{ + writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN); +} + +int iproc_pcie_setup(struct iproc_pcie *pcie) +{ + int ret; + struct pci_bus *bus; + + if (!pcie || !pcie->dev || !pcie->base) + return -EINVAL; + + if (pcie->phy) { + ret = phy_init(pcie->phy); + if (ret) { + dev_err(pcie->dev, "unable to initialize PCIe PHY\n"); + return ret; + } + + ret = phy_power_on(pcie->phy); + if (ret) { + dev_err(pcie->dev, "unable to power on PCIe PHY\n"); + goto err_exit_phy; + } + + } + + iproc_pcie_reset(pcie); + + pcie->sysdata.private_data = pcie; + + bus = pci_create_root_bus(pcie->dev, 0, &iproc_pcie_ops, + &pcie->sysdata, pcie->resources); + if (!bus) { + dev_err(pcie->dev, "unable to create PCI root bus\n"); + ret = -ENOMEM; + goto err_power_off_phy; + } + pcie->root_bus = bus; + + ret = iproc_pcie_check_link(pcie, bus); + if (ret) { + dev_err(pcie->dev, "no PCIe EP device detected\n"); + goto err_rm_root_bus; + } + + iproc_pcie_enable(pcie); + + pci_scan_child_bus(bus); + pci_assign_unassigned_bus_resources(bus); + pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); + pci_bus_add_devices(bus); + + return 0; + +err_rm_root_bus: + pci_stop_root_bus(bus); + pci_remove_root_bus(bus); + +err_power_off_phy: + if (pcie->phy) + phy_power_off(pcie->phy); +err_exit_phy: + if (pcie->phy) + phy_exit(pcie->phy); + + return ret; +} +EXPORT_SYMBOL(iproc_pcie_setup); + +int iproc_pcie_remove(struct iproc_pcie *pcie) +{ + pci_stop_root_bus(pcie->root_bus); + pci_remove_root_bus(pcie->root_bus); + + if (pcie->phy) { + phy_power_off(pcie->phy); + phy_exit(pcie->phy); + } + + return 0; +} +EXPORT_SYMBOL(iproc_pcie_remove); + +MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom iPROC PCIe common driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h new file mode 100644 index 000000000000..e28075ed1856 --- /dev/null +++ b/drivers/pci/host/pcie-iproc.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014-2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _PCIE_IPROC_H +#define _PCIE_IPROC_H + +#define IPROC_PCIE_MAX_NUM_IRQS 6 + +/** + * iProc PCIe device + * @dev: pointer to device data structure + * @base: PCIe host controller I/O register base + * @resources: linked list of all PCI resources + * @sysdata: Per PCI controller data + * @root_bus: pointer to root bus + * @phy: optional PHY device that controls the Serdes + * @irqs: interrupt IDs + */ +struct iproc_pcie { + struct device *dev; + void __iomem *base; + struct list_head *resources; + struct pci_sys_data sysdata; + struct pci_bus *root_bus; + struct phy *phy; + int irqs[IPROC_PCIE_MAX_NUM_IRQS]; +}; + +int iproc_pcie_setup(struct iproc_pcie *pcie); +int iproc_pcie_remove(struct iproc_pcie *pcie); + +#endif /* _PCIE_IPROC_H */ diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index c57bd0ac39a0..c086210f2ffd 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -64,8 +64,8 @@ #define LAR_ENABLE (1 << 1) /* PCIe address reg & mask */ -#define PCIEPARL(x) (0x03400 + ((x) * 0x20)) -#define PCIEPARH(x) (0x03404 + ((x) * 0x20)) +#define PCIEPALR(x) (0x03400 + ((x) * 0x20)) +#define PCIEPAUR(x) (0x03404 + ((x) * 0x20)) #define PCIEPAMR(x) (0x03408 + ((x) * 0x20)) #define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20)) #define PAR_ENABLE (1 << 31) @@ -341,8 +341,9 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie) else res_start = res->start; - rcar_pci_write_reg(pcie, upper_32_bits(res_start), PCIEPARH(win)); - rcar_pci_write_reg(pcie, lower_32_bits(res_start), PCIEPARL(win)); + rcar_pci_write_reg(pcie, upper_32_bits(res_start), PCIEPAUR(win)); + rcar_pci_write_reg(pcie, lower_32_bits(res_start) & ~0x7F, + PCIEPALR(win)); /* First resource is for IO */ mask = PAR_ENABLE; @@ -501,7 +502,7 @@ static int rcar_pcie_hw_init(struct rcar_pcie *pcie) /* Enable MSI */ if (IS_ENABLED(CONFIG_PCI_MSI)) - rcar_pci_write_reg(pcie, 0x101f0000, PCIEMSITXR); + rcar_pci_write_reg(pcie, 0x801f0000, PCIEMSITXR); /* Finish initialization - establish a PCI Express link */ rcar_pci_write_reg(pcie, CFINIT, PCIETCTLR); diff --git a/drivers/pci/host/pcie-spear13xx.c b/drivers/pci/host/pcie-spear13xx.c index 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/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index 96c5c729cdbc..15302475f5b7 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -738,7 +738,7 @@ static void ibm_unconfigure_device(struct pci_func *func) */ static u8 bus_structure_fixup(u8 busno) { - struct pci_bus *bus; + struct pci_bus *bus, *b; struct pci_dev *dev; u16 l; @@ -765,7 +765,11 @@ static u8 bus_structure_fixup(u8 busno) (l != 0x0000) && (l != 0xffff)) { debug("%s - Inside bus_structure_fixup()\n", __func__); - pci_scan_bus(busno, ibmphp_pci_bus->ops, NULL); + b = pci_scan_bus(busno, ibmphp_pci_bus->ops, NULL); + if (!b) + continue; + + pci_bus_add_devices(b); break; } } diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 489063987325..6f6f175f51f7 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -18,6 +18,15 @@ #include <linux/pm_qos.h> #include "pci.h" +/* + * The UUID is defined in the PCI Firmware Specification available here: + * https://www.pcisig.com/members/downloads/pcifw_r3_1_13Dec10.pdf + */ +const u8 pci_acpi_dsm_uuid[] = { + 0xd0, 0x37, 0xc9, 0xe5, 0x53, 0x35, 0x7a, 0x4d, + 0x91, 0x17, 0xea, 0x4d, 0x19, 0xc3, 0x43, 0x4d +}; + phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle) { acpi_status status = AE_NOT_EXIST; @@ -248,6 +257,9 @@ int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp) acpi_handle handle, phandle; struct pci_bus *pbus; + if (acpi_pci_disabled) + return -ENODEV; + handle = NULL; for (pbus = dev->bus; pbus; pbus = pbus->parent) { handle = acpi_pci_get_bridge_handle(pbus); @@ -528,11 +540,32 @@ static struct pci_platform_pm_ops acpi_pci_platform_pm = { void acpi_pci_add_bus(struct pci_bus *bus) { + union acpi_object *obj; + struct pci_host_bridge *bridge; + if (acpi_pci_disabled || !bus->bridge) return; acpi_pci_slot_enumerate(bus); acpiphp_enumerate_slots(bus); + + /* + * For a host bridge, check its _DSM for function 8 and if + * that is available, mark it in pci_host_bridge. + */ + if (!pci_is_root_bus(bus)) + return; + + obj = acpi_evaluate_dsm(ACPI_HANDLE(bus->bridge), pci_acpi_dsm_uuid, 3, + RESET_DELAY_DSM, NULL); + if (!obj) + return; + + if (obj->type == ACPI_TYPE_INTEGER && obj->integer.value == 1) { + bridge = pci_find_host_bridge(bus); + bridge->ignore_reset_delay = 1; + } + ACPI_FREE(obj); } void acpi_pci_remove_bus(struct pci_bus *bus) @@ -558,6 +591,57 @@ static struct acpi_device *acpi_pci_find_companion(struct device *dev) check_children); } +/** + * pci_acpi_optimize_delay - optimize PCI D3 and D3cold delay from ACPI + * @pdev: the PCI device whose delay is to be updated + * @adev: the companion ACPI device of this PCI device + * + * Update the d3_delay and d3cold_delay of a PCI device from the ACPI _DSM + * control method of either the device itself or the PCI host bridge. + * + * Function 8, "Reset Delay," applies to the entire hierarchy below a PCI + * host bridge. If it returns one, the OS may assume that all devices in + * the hierarchy have already completed power-on reset delays. + * + * Function 9, "Device Readiness Durations," applies only to the object + * where it is located. It returns delay durations required after various + * events if the device requires less time than the spec requires. Delays + * from this function take precedence over the Reset Delay function. + * + * These _DSM functions are defined by the draft ECN of January 28, 2014, + * titled "ACPI additions for FW latency optimizations." + */ +static void pci_acpi_optimize_delay(struct pci_dev *pdev, + acpi_handle handle) +{ + struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus); + int value; + union acpi_object *obj, *elements; + + if (bridge->ignore_reset_delay) + pdev->d3cold_delay = 0; + + obj = acpi_evaluate_dsm(handle, pci_acpi_dsm_uuid, 3, + FUNCTION_DELAY_DSM, NULL); + if (!obj) + return; + + if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 5) { + elements = obj->package.elements; + if (elements[0].type == ACPI_TYPE_INTEGER) { + value = (int)elements[0].integer.value / 1000; + if (value < PCI_PM_D3COLD_WAIT) + pdev->d3cold_delay = value; + } + if (elements[3].type == ACPI_TYPE_INTEGER) { + value = (int)elements[3].integer.value / 1000; + if (value < PCI_PM_D3_WAIT) + pdev->d3_delay = value; + } + } + ACPI_FREE(obj); +} + static void pci_acpi_setup(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -566,6 +650,8 @@ static void pci_acpi_setup(struct device *dev) if (!adev) return; + pci_acpi_optimize_delay(pci_dev, adev->handle); + pci_acpi_add_pm_notifier(adev, pci_dev); if (!adev->wakeup.flags.valid) return; diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c index 2ab1b47c7651..024b5c179348 100644 --- a/drivers/pci/pci-label.c +++ b/drivers/pci/pci-label.c @@ -31,8 +31,6 @@ #include <linux/pci-acpi.h> #include "pci.h" -#define DEVICE_LABEL_DSM 0x07 - #ifdef CONFIG_DMI enum smbios_attr_enum { SMBIOS_ATTR_NONE = 0, @@ -148,11 +146,6 @@ static inline void pci_remove_smbiosname_file(struct pci_dev *pdev) #endif #ifdef CONFIG_ACPI -static const char device_label_dsm_uuid[] = { - 0xD0, 0x37, 0xC9, 0xE5, 0x53, 0x35, 0x7A, 0x4D, - 0x91, 0x17, 0xEA, 0x4D, 0x19, 0xC3, 0x43, 0x4D -}; - enum acpi_attr_enum { ACPI_ATTR_LABEL_SHOW, ACPI_ATTR_INDEX_SHOW, @@ -179,7 +172,7 @@ static int dsm_get_label(struct device *dev, char *buf, if (!handle) return -1; - obj = acpi_evaluate_dsm(handle, device_label_dsm_uuid, 0x2, + obj = acpi_evaluate_dsm(handle, pci_acpi_dsm_uuid, 0x2, DEVICE_LABEL_DSM, NULL); if (!obj) return -1; @@ -219,7 +212,7 @@ static bool device_has_dsm(struct device *dev) if (!handle) return false; - return !!acpi_check_dsm(handle, device_label_dsm_uuid, 0x2, + return !!acpi_check_dsm(handle, pci_acpi_dsm_uuid, 0x2, 1 << DEVICE_LABEL_DSM); } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 81f06e8dcc04..acc4b6ef78c4 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -126,15 +126,16 @@ EXPORT_SYMBOL_GPL(pci_bus_max_busnr); #ifdef CONFIG_HAS_IOMEM void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar) { + struct resource *res = &pdev->resource[bar]; + /* * Make sure the BAR is actually a memory resource, not an IO resource */ - if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { - WARN_ON(1); + if (res->flags & IORESOURCE_UNSET || !(res->flags & IORESOURCE_MEM)) { + dev_warn(&pdev->dev, "can't ioremap BAR %d: %pR\n", bar, res); return NULL; } - return ioremap_nocache(pci_resource_start(pdev, bar), - pci_resource_len(pdev, bar)); + return ioremap_nocache(res->start, resource_size(res)); } EXPORT_SYMBOL_GPL(pci_ioremap_bar); #endif @@ -145,19 +146,22 @@ static int __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn, u8 pos, int cap, int *ttl) { u8 id; + u16 ent; + + pci_bus_read_config_byte(bus, devfn, pos, &pos); while ((*ttl)--) { - pci_bus_read_config_byte(bus, devfn, pos, &pos); if (pos < 0x40) break; pos &= ~3; - pci_bus_read_config_byte(bus, devfn, pos + PCI_CAP_LIST_ID, - &id); + pci_bus_read_config_word(bus, devfn, pos, &ent); + + id = ent & 0xff; if (id == 0xff) break; if (id == cap) return pos; - pos += PCI_CAP_LIST_NEXT; + pos = (ent >> 8); } return 0; } @@ -2492,6 +2496,7 @@ u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp) *pinp = pin; return PCI_SLOT(dev->devfn); } +EXPORT_SYMBOL_GPL(pci_common_swizzle); /** * pci_release_region - Release a PCI bar diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4091f82239cd..d72f849174a4 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -321,4 +321,6 @@ static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe) } #endif +struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus); + #endif /* DRIVERS_PCI_H */ diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 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/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 820740a22e94..7d4fcdc512aa 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -782,24 +782,6 @@ void pci_disable_link_state(struct pci_dev *pdev, int state) } EXPORT_SYMBOL(pci_disable_link_state); -void pcie_clear_aspm(struct pci_bus *bus) -{ - struct pci_dev *child; - - if (aspm_force) - return; - - /* - * Clear any ASPM setup that the firmware has carried out on this bus - */ - list_for_each_entry(child, &bus->devices, bus_list) { - __pci_disable_link_state(child, PCIE_LINK_STATE_L0S | - PCIE_LINK_STATE_L1 | - PCIE_LINK_STATE_CLKPM, - false, true); - } -} - static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) { int i; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 8d2f400e96cb..6675a7a1b9fc 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -6,6 +6,7 @@ #include <linux/delay.h> #include <linux/init.h> #include <linux/pci.h> +#include <linux/of_pci.h> #include <linux/pci_hotplug.h> #include <linux/slab.h> #include <linux/module.h> @@ -1520,6 +1521,7 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) dev->dev.dma_mask = &dev->dma_mask; dev->dev.dma_parms = &dev->dma_parms; dev->dev.coherent_dma_mask = 0xffffffffull; + of_pci_dma_configure(dev); pci_set_dma_max_seg_size(dev, 65536); pci_set_dma_seg_boundary(dev, 0xffffffff); @@ -1993,6 +1995,7 @@ err_out: kfree(b); return NULL; } +EXPORT_SYMBOL_GPL(pci_create_root_bus); int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max) { @@ -2087,7 +2090,6 @@ struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, if (!found) pci_bus_update_busn_res_end(b, max); - pci_bus_add_devices(b); return b; } EXPORT_SYMBOL(pci_scan_root_bus); @@ -2123,7 +2125,6 @@ struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops, b = pci_create_root_bus(NULL, bus, ops, sysdata, &resources); if (b) { pci_scan_child_bus(b); - pci_bus_add_devices(b); } else { pci_free_resource_list(&resources); } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 85f247e28a80..c6dc1dfd25d5 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3182,7 +3182,7 @@ static void quirk_apple_wait_for_thunderbolt(struct pci_dev *dev) || nhi->subsystem_vendor != 0x2222 || nhi->subsystem_device != 0x1111) goto out; - dev_info(&dev->dev, "quirk: wating for thunderbolt to reestablish pci tunnels...\n"); + dev_info(&dev->dev, "quirk: waiting for thunderbolt to reestablish PCI tunnels...\n"); device_pm_wait_for_dev(&dev->dev, &nhi->dev); out: pci_dev_put(nhi); @@ -3822,6 +3822,38 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_INTEL, 0x154F, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_INTEL, 0x1551, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_INTEL, 0x1558, pci_quirk_mf_endpoint_acs }, + /* 82580 */ + { PCI_VENDOR_ID_INTEL, 0x1509, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x150E, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x150F, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1510, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1511, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1516, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1527, pci_quirk_mf_endpoint_acs }, + /* 82576 */ + { PCI_VENDOR_ID_INTEL, 0x10C9, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x10E6, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x10E7, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x10E8, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x150A, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x150D, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1518, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1526, pci_quirk_mf_endpoint_acs }, + /* 82575 */ + { PCI_VENDOR_ID_INTEL, 0x10A7, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x10A9, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x10D6, pci_quirk_mf_endpoint_acs }, + /* I350 */ + { PCI_VENDOR_ID_INTEL, 0x1521, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1522, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1523, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1524, pci_quirk_mf_endpoint_acs }, + /* 82571 (Quads omitted due to non-ACS switch) */ + { PCI_VENDOR_ID_INTEL, 0x105E, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x105F, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x1060, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_INTEL, 0x10D9, pci_quirk_mf_endpoint_acs }, + /* Intel PCH root ports */ { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs }, { 0x19a2, 0x710, pci_quirk_mf_endpoint_acs }, /* Emulex BE3-R */ { 0x10df, 0x720, pci_quirk_mf_endpoint_acs }, /* Emulex Skyhawk-R */ diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 8bd76c9ba21c..8a280e9c2ad1 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -139,6 +139,7 @@ void pci_stop_root_bus(struct pci_bus *bus) /* stop the host bridge */ device_release_driver(&host_bridge->dev); } +EXPORT_SYMBOL_GPL(pci_stop_root_bus); void pci_remove_root_bus(struct pci_bus *bus) { @@ -158,3 +159,4 @@ void pci_remove_root_bus(struct pci_bus *bus) /* remove the host bridge */ device_unregister(&host_bridge->dev); } +EXPORT_SYMBOL_GPL(pci_remove_root_bus); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index e3e17f3c0f0f..8169597e47cb 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1750,3 +1750,4 @@ void pci_assign_unassigned_bus_resources(struct pci_bus *bus) __pci_bus_assign_resources(bus, &add_list, NULL); BUG_ON(!list_empty(&add_list)); } +EXPORT_SYMBOL_GPL(pci_assign_unassigned_bus_resources); diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c index 4e2d595d50ca..95c225be49d1 100644 --- a/drivers/pci/setup-irq.c +++ b/drivers/pci/setup-irq.c @@ -65,3 +65,4 @@ void pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *), for_each_pci_dev(dev) pdev_fixup_irq(dev, swizzle, map_irq); } +EXPORT_SYMBOL_GPL(pci_fixup_irqs); diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index b7c3a5ea1fca..232f9254c11a 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -120,6 +120,7 @@ int pci_claim_resource(struct pci_dev *dev, int resource) if (!root) { dev_info(&dev->dev, "can't claim BAR %d %pR: no compatible bridge window\n", resource, res); + res->flags |= IORESOURCE_UNSET; return -EINVAL; } @@ -127,6 +128,7 @@ int pci_claim_resource(struct pci_dev *dev, int resource) if (conflict) { dev_info(&dev->dev, "can't claim BAR %d %pR: address conflict with %s %pR\n", resource, res, conflict->name, conflict); + res->flags |= IORESOURCE_UNSET; return -EBUSY; } diff --git a/drivers/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/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/pnp/quirks.c b/drivers/pnp/quirks.c index ebf0d6710b5a..943c1cb9566c 100644 --- a/drivers/pnp/quirks.c +++ b/drivers/pnp/quirks.c @@ -246,13 +246,16 @@ static void quirk_system_pci_resources(struct pnp_dev *dev) */ for_each_pci_dev(pdev) { for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { - unsigned long type; + unsigned long flags, type; - type = pci_resource_flags(pdev, i) & - (IORESOURCE_IO | IORESOURCE_MEM); + flags = pci_resource_flags(pdev, i); + type = flags & (IORESOURCE_IO | IORESOURCE_MEM); if (!type || pci_resource_len(pdev, i) == 0) continue; + if (flags & IORESOURCE_UNSET) + continue; + pci_start = pci_resource_start(pdev, i); pci_end = pci_resource_end(pdev, i); for (j = 0; diff --git a/drivers/power/88pm860x_battery.c b/drivers/power/88pm860x_battery.c index 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/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 a4a8a6dc60c4..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; @@ -2998,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 * @@ -3021,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; @@ -3035,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 @@ -3499,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; @@ -3533,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); @@ -3641,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; + rdev->supply_name = regulator_desc->supply_name; - 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; - } - } - -add_dev: /* add consumers devices */ if (init_data) { for (i = 0; i < init_data->num_consumer_supplies; i++) { @@ -3702,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: @@ -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 18198316b6cf..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,9 +1577,11 @@ static int palmas_regulators_probe(struct platform_device *pdev) if (!pmic) return -ENOMEM; - if (of_device_is_compatible(node, "ti,tps659038-pmic")) + 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; 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/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/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-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-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/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 4f8c798e0633..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. @@ -111,33 +143,30 @@ static void dw_spi_dma_tx_done(void *arg) 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) @@ -160,33 +189,30 @@ static void dw_spi_dma_rx_done(void *arg) 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) @@ -198,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) { @@ -246,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 @@ -282,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 2b2c359f5a50..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; - - n_words = xfer->len / w_size; - controller->w_size = w_size; + mode = spi_qup_get_mode(spi->master, xfer); + n_words = controller->n_words; - 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; } @@ -563,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); @@ -574,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; @@ -610,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); @@ -634,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); @@ -649,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); @@ -740,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 57a195041dc7..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; @@ -1360,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", @@ -1894,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/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/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 2accb6e47beb..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); diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 79b4ec3ca2db..7faa6aef9a4d 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -781,8 +781,8 @@ int se_dev_set_emulate_fua_write(struct se_device *dev, int flag) } if (flag && dev->transport->get_write_cache) { - pr_err("emulate_fua_write not supported for this device\n"); - return -EINVAL; + 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" 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/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-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"); } |