diff options
64 files changed, 5984 insertions, 1091 deletions
diff --git a/Documentation/devicetree/bindings/power_supply/power_supply.txt b/Documentation/devicetree/bindings/power_supply/power_supply.txt new file mode 100644 index 00000000000..8391bfa0eda --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/power_supply.txt @@ -0,0 +1,23 @@ +Power Supply Core Support + +Optional Properties: + - power-supplies : This property is added to a supply in order to list the + devices which supply it power, referenced by their phandles. + +Example: + + usb-charger: power@e { + compatible = "some,usb-charger"; + ... + }; + + ac-charger: power@c { + compatible = "some,ac-charger"; + ... + }; + + battery@b { + compatible = "some,battery"; + ... + power-supplies = <&usb-charger>, <&ac-charger>; + }; diff --git a/Documentation/devicetree/bindings/power_supply/tps65090.txt b/Documentation/devicetree/bindings/power_supply/tps65090.txt new file mode 100644 index 00000000000..8e5e0d3910d --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/tps65090.txt @@ -0,0 +1,17 @@ +TPS65090 Frontend PMU with Switchmode Charger + +Required Properties: +-compatible: "ti,tps65090-charger" + +Optional Properties: +-ti,enable-low-current-chrg: Enables charging when a low current is detected + while the default logic is to stop charging. + +This node is a subnode of the tps65090 PMIC. + +Example: + + tps65090-charger { + compatible = "ti,tps65090-charger"; + ti,enable-low-current-chrg; + }; diff --git a/Documentation/hwmon/ab8500 b/Documentation/hwmon/ab8500 new file mode 100644 index 00000000000..cf169c8ef4e --- /dev/null +++ b/Documentation/hwmon/ab8500 @@ -0,0 +1,22 @@ +Kernel driver ab8500 +==================== + +Supported chips: + * ST-Ericsson AB8500 + Prefix: 'ab8500' + Addresses scanned: - + Datasheet: http://www.stericsson.com/developers/documentation.jsp + +Authors: + Martin Persson <martin.persson@stericsson.com> + Hongbo Zhang <hongbo.zhang@linaro.org> + +Description +----------- + +See also Documentation/hwmon/abx500. This is the ST-Ericsson AB8500 specific +driver. + +Currently only the AB8500 internal sensor and one external sensor for battery +temperature are monitored. Other GPADC channels can also be monitored if needed +in future. diff --git a/Documentation/hwmon/abx500 b/Documentation/hwmon/abx500 new file mode 100644 index 00000000000..319a058cec7 --- /dev/null +++ b/Documentation/hwmon/abx500 @@ -0,0 +1,28 @@ +Kernel driver abx500 +==================== + +Supported chips: + * ST-Ericsson ABx500 series + Prefix: 'abx500' + Addresses scanned: - + Datasheet: http://www.stericsson.com/developers/documentation.jsp + +Authors: + Martin Persson <martin.persson@stericsson.com> + Hongbo Zhang <hongbo.zhang@linaro.org> + +Description +----------- + +Every ST-Ericsson Ux500 SOC consists of both ABx500 and DBx500 physically, +this is kernel hwmon driver for ABx500. + +There are some GPADCs inside ABx500 which are designed for connecting to +thermal sensors, and there is also a thermal sensor inside ABx500 too, which +raises interrupt when critical temperature reached. + +This abx500 is a common layer which can monitor all of the sensors, every +specific abx500 chip has its special configurations in its own file, e.g. some +sensors can be configured invisible if they are not available on that chip, and +the corresponding gpadc_addr should be set to 0, thus this sensor won't be +polled. diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig index 52d315b792c..0f1c5e53fb2 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig @@ -17,6 +17,9 @@ config ARCH_VEXPRESS select NO_IOPORT select PLAT_VERSATILE select PLAT_VERSATILE_CLCD + select POWER_RESET + select POWER_RESET_VEXPRESS + select POWER_SUPPLY select REGULATOR_FIXED_VOLTAGE if REGULATOR select VEXPRESS_CONFIG help diff --git a/arch/arm/mach-vexpress/Makefile b/arch/arm/mach-vexpress/Makefile index 80b64971fbd..42703e8b4d3 100644 --- a/arch/arm/mach-vexpress/Makefile +++ b/arch/arm/mach-vexpress/Makefile @@ -4,7 +4,7 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ -I$(srctree)/arch/arm/plat-versatile/include -obj-y := v2m.o reset.o +obj-y := v2m.o obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c index c5e20b52e3b..eb2b3a627f0 100644 --- a/arch/arm/mach-vexpress/v2m.c +++ b/arch/arm/mach-vexpress/v2m.c @@ -363,8 +363,6 @@ static void __init v2m_init(void) for (i = 0; i < ARRAY_SIZE(v2m_amba_devs); i++) amba_device_register(v2m_amba_devs[i], &iomem_resource); - pm_power_off = vexpress_power_off; - ct_desc->init_tile(); } @@ -376,7 +374,6 @@ MACHINE_START(VEXPRESS, "ARM-Versatile Express") .init_irq = v2m_init_irq, .init_time = v2m_timer_init, .init_machine = v2m_init, - .restart = vexpress_restart, MACHINE_END static struct map_desc v2m_rs1_io_desc __initdata = { @@ -470,7 +467,6 @@ static void __init v2m_dt_init(void) { l2x0_of_init(0x00400000, 0xfe0fffff); of_platform_populate(NULL, v2m_dt_bus_match, NULL, NULL); - pm_power_off = vexpress_power_off; } static const char * const v2m_dt_match[] __initconst = { @@ -487,5 +483,4 @@ DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express") .init_irq = irqchip_init, .init_time = v2m_dt_timer_init, .init_machine = v2m_dt_init, - .restart = vexpress_restart, MACHINE_END diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index f61d98a6296..9c333d47143 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -39,6 +39,19 @@ config HWMON_DEBUG_CHIP comment "Native drivers" +config SENSORS_AB8500 + tristate "AB8500 thermal monitoring" + depends on AB8500_GPADC && AB8500_BM + default n + help + If you say yes here you get support for the thermal sensor part + of the AB8500 chip. The driver includes thermal management for + AB8500 die and two GPADC channels. The GPADC channel are preferably + used to access sensors outside the AB8500 chip. + + This driver can also be built as a module. If so, the module + will be called abx500-temp. + config SENSORS_ABITUGURU tristate "Abit uGuru (rev 1 & 2)" depends on X86 && DMI diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index c51b0dc35dc..d17d3e64f9f 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_SENSORS_W83795) += w83795.o obj-$(CONFIG_SENSORS_W83781D) += w83781d.o obj-$(CONFIG_SENSORS_W83791D) += w83791d.o +obj-$(CONFIG_SENSORS_AB8500) += abx500.o ab8500.o obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o obj-$(CONFIG_SENSORS_AD7314) += ad7314.o diff --git a/drivers/hwmon/ab8500.c b/drivers/hwmon/ab8500.c new file mode 100644 index 00000000000..d844dc80685 --- /dev/null +++ b/drivers/hwmon/ab8500.c @@ -0,0 +1,206 @@ +/* + * Copyright (C) ST-Ericsson 2010 - 2013 + * Author: Martin Persson <martin.persson@stericsson.com> + * Hongbo Zhang <hongbo.zhang@linaro.org> + * License Terms: GNU General Public License v2 + * + * When the AB8500 thermal warning temperature is reached (threshold cannot + * be changed by SW), an interrupt is set, and if no further action is taken + * within a certain time frame, pm_power off will be called. + * + * When AB8500 thermal shutdown temperature is reached a hardware shutdown of + * the AB8500 will occur. + */ + +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/mfd/abx500.h> +#include <linux/mfd/abx500/ab8500-bm.h> +#include <linux/mfd/abx500/ab8500-gpadc.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/power/ab8500.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include "abx500.h" + +#define DEFAULT_POWER_OFF_DELAY (HZ * 10) +#define THERMAL_VCC 1800 +#define PULL_UP_RESISTOR 47000 +/* Number of monitored sensors should not greater than NUM_SENSORS */ +#define NUM_MONITORED_SENSORS 4 + +struct ab8500_gpadc_cfg { + const struct abx500_res_to_temp *temp_tbl; + int tbl_sz; + int vcc; + int r_up; +}; + +struct ab8500_temp { + struct ab8500_gpadc *gpadc; + struct ab8500_btemp *btemp; + struct delayed_work power_off_work; + struct ab8500_gpadc_cfg cfg; + struct abx500_temp *abx500_data; +}; + +/* + * The hardware connection is like this: + * VCC----[ R_up ]-----[ NTC ]----GND + * where R_up is pull-up resistance, and GPADC measures voltage on NTC. + * and res_to_temp table is strictly sorted by falling resistance values. + */ +static int ab8500_voltage_to_temp(struct ab8500_gpadc_cfg *cfg, + int v_ntc, int *temp) +{ + int r_ntc, i = 0, tbl_sz = cfg->tbl_sz; + const struct abx500_res_to_temp *tbl = cfg->temp_tbl; + + if (cfg->vcc < 0 || v_ntc >= cfg->vcc) + return -EINVAL; + + r_ntc = v_ntc * cfg->r_up / (cfg->vcc - v_ntc); + if (r_ntc > tbl[0].resist || r_ntc < tbl[tbl_sz - 1].resist) + return -EINVAL; + + while (!(r_ntc <= tbl[i].resist && r_ntc > tbl[i + 1].resist) && + i < tbl_sz - 2) + i++; + + /* return milli-Celsius */ + *temp = tbl[i].temp * 1000 + ((tbl[i + 1].temp - tbl[i].temp) * 1000 * + (r_ntc - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist); + + return 0; +} + +static int ab8500_read_sensor(struct abx500_temp *data, u8 sensor, int *temp) +{ + int voltage, ret; + struct ab8500_temp *ab8500_data = data->plat_data; + + if (sensor == BAT_CTRL) { + *temp = ab8500_btemp_get_batctrl_temp(ab8500_data->btemp); + } else if (sensor == BTEMP_BALL) { + *temp = ab8500_btemp_get_temp(ab8500_data->btemp); + } else { + voltage = ab8500_gpadc_convert(ab8500_data->gpadc, sensor); + if (voltage < 0) + return voltage; + + ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp); + if (ret < 0) + return ret; + } + + return 0; +} + +static void ab8500_thermal_power_off(struct work_struct *work) +{ + struct ab8500_temp *ab8500_data = container_of(work, + struct ab8500_temp, power_off_work.work); + struct abx500_temp *abx500_data = ab8500_data->abx500_data; + + dev_warn(&abx500_data->pdev->dev, "Power off due to critical temp\n"); + + pm_power_off(); +} + +static ssize_t ab8500_show_name(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + return sprintf(buf, "ab8500\n"); +} + +static ssize_t ab8500_show_label(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + char *label; + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int index = attr->index; + + switch (index) { + case 1: + label = "ext_adc1"; + break; + case 2: + label = "ext_adc2"; + break; + case 3: + label = "bat_temp"; + break; + case 4: + label = "bat_ctrl"; + break; + default: + return -EINVAL; + } + + return sprintf(buf, "%s\n", label); +} + +static int ab8500_temp_irq_handler(int irq, struct abx500_temp *data) +{ + struct ab8500_temp *ab8500_data = data->plat_data; + + dev_warn(&data->pdev->dev, "Power off in %d s\n", + DEFAULT_POWER_OFF_DELAY / HZ); + + schedule_delayed_work(&ab8500_data->power_off_work, + DEFAULT_POWER_OFF_DELAY); + return 0; +} + +int abx500_hwmon_init(struct abx500_temp *data) +{ + struct ab8500_temp *ab8500_data; + + ab8500_data = devm_kzalloc(&data->pdev->dev, sizeof(*ab8500_data), + GFP_KERNEL); + if (!ab8500_data) + return -ENOMEM; + + ab8500_data->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + if (IS_ERR(ab8500_data->gpadc)) + return PTR_ERR(ab8500_data->gpadc); + + ab8500_data->btemp = ab8500_btemp_get(); + if (IS_ERR(ab8500_data->btemp)) + return PTR_ERR(ab8500_data->btemp); + + INIT_DELAYED_WORK(&ab8500_data->power_off_work, + ab8500_thermal_power_off); + + ab8500_data->cfg.vcc = THERMAL_VCC; + ab8500_data->cfg.r_up = PULL_UP_RESISTOR; + ab8500_data->cfg.temp_tbl = ab8500_temp_tbl_a_thermistor; + ab8500_data->cfg.tbl_sz = ab8500_temp_tbl_a_size; + + data->plat_data = ab8500_data; + + /* + * ADC_AUX1 and ADC_AUX2, connected to external NTC + * BTEMP_BALL and BAT_CTRL, fixed usage + */ + data->gpadc_addr[0] = ADC_AUX1; + data->gpadc_addr[1] = ADC_AUX2; + data->gpadc_addr[2] = BTEMP_BALL; + data->gpadc_addr[3] = BAT_CTRL; + data->monitored_sensors = NUM_MONITORED_SENSORS; + + data->ops.read_sensor = ab8500_read_sensor; + data->ops.irq_handler = ab8500_temp_irq_handler; + data->ops.show_name = ab8500_show_name; + data->ops.show_label = ab8500_show_label; + data->ops.is_visible = NULL; + + return 0; +} +EXPORT_SYMBOL(abx500_hwmon_init); + +MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@linaro.org>"); +MODULE_DESCRIPTION("AB8500 temperature driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/abx500.c b/drivers/hwmon/abx500.c new file mode 100644 index 00000000000..b4ad87b31a3 --- /dev/null +++ b/drivers/hwmon/abx500.c @@ -0,0 +1,491 @@ +/* + * Copyright (C) ST-Ericsson 2010 - 2013 + * Author: Martin Persson <martin.persson@stericsson.com> + * Hongbo Zhang <hongbo.zhang@linaro.org> + * License Terms: GNU General Public License v2 + * + * ABX500 does not provide auto ADC, so to monitor the required temperatures, + * a periodic work is used. It is more important to not wake up the CPU than + * to perform this job, hence the use of a deferred delay. + * + * A deferred delay for thermal monitor is considered safe because: + * If the chip gets too hot during a sleep state it's most likely due to + * external factors, such as the surrounding temperature. I.e. no SW decisions + * will make any difference. + */ + +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/workqueue.h> +#include "abx500.h" + +#define DEFAULT_MONITOR_DELAY HZ +#define DEFAULT_MAX_TEMP 130 + +static inline void schedule_monitor(struct abx500_temp *data) +{ + data->work_active = true; + schedule_delayed_work(&data->work, DEFAULT_MONITOR_DELAY); +} + +static void threshold_updated(struct abx500_temp *data) +{ + int i; + for (i = 0; i < data->monitored_sensors; i++) + if (data->max[i] != 0 || data->min[i] != 0) { + schedule_monitor(data); + return; + } + + dev_dbg(&data->pdev->dev, "No active thresholds.\n"); + cancel_delayed_work_sync(&data->work); + data->work_active = false; +} + +static void gpadc_monitor(struct work_struct *work) +{ + int temp, i, ret; + char alarm_node[30]; + bool updated_min_alarm, updated_max_alarm; + struct abx500_temp *data; + + data = container_of(work, struct abx500_temp, work.work); + mutex_lock(&data->lock); + + for (i = 0; i < data->monitored_sensors; i++) { + /* Thresholds are considered inactive if set to 0 */ + if (data->max[i] == 0 && data->min[i] == 0) + continue; + + if (data->max[i] < data->min[i]) + continue; + + ret = data->ops.read_sensor(data, data->gpadc_addr[i], &temp); + if (ret < 0) { + dev_err(&data->pdev->dev, "GPADC read failed\n"); + continue; + } + + updated_min_alarm = false; + updated_max_alarm = false; + + if (data->min[i] != 0) { + if (temp < data->min[i]) { + if (data->min_alarm[i] == false) { + data->min_alarm[i] = true; + updated_min_alarm = true; + } + } else { + if (data->min_alarm[i] == true) { + data->min_alarm[i] = false; + updated_min_alarm = true; + } + } + } + if (data->max[i] != 0) { + if (temp > data->max[i]) { + if (data->max_alarm[i] == false) { + data->max_alarm[i] = true; + updated_max_alarm = true; + } + } else if (temp < data->max[i] - data->max_hyst[i]) { + if (data->max_alarm[i] == true) { + data->max_alarm[i] = false; + updated_max_alarm = true; + } + } + } + + if (updated_min_alarm) { + ret = sprintf(alarm_node, "temp%d_min_alarm", i + 1); + sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node); + } + if (updated_max_alarm) { + ret = sprintf(alarm_node, "temp%d_max_alarm", i + 1); + sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node); + } + } + + schedule_monitor(data); + mutex_unlock(&data->lock); +} + +/* HWMON sysfs interfaces */ +static ssize_t show_name(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct abx500_temp *data = dev_get_drvdata(dev); + /* Show chip name */ + return data->ops.show_name(dev, devattr, buf); +} + +static ssize_t show_label(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct abx500_temp *data = dev_get_drvdata(dev); + /* Show each sensor label */ + return data->ops.show_label(dev, devattr, buf); +} + +static ssize_t show_input(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int ret, temp; + struct abx500_temp *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + u8 gpadc_addr = data->gpadc_addr[attr->index]; + + ret = data->ops.read_sensor(data, gpadc_addr, &temp); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", temp); +} + +/* Set functions (RW nodes) */ +static ssize_t set_min(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned long val; + struct abx500_temp *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int res = kstrtol(buf, 10, &val); + if (res < 0) + return res; + + val = clamp_val(val, 0, DEFAULT_MAX_TEMP); + + mutex_lock(&data->lock); + data->min[attr->index] = val; + threshold_updated(data); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t set_max(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned long val; + struct abx500_temp *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int res = kstrtol(buf, 10, &val); + if (res < 0) + return res; + + val = clamp_val(val, 0, DEFAULT_MAX_TEMP); + + mutex_lock(&data->lock); |