Skip to content

Commit

Permalink
hwmon: Add ST-Ericsson ABX500 hwmon driver
Browse files Browse the repository at this point in the history
Each of ST-Ericsson X500 chip set series consists of both ABX500 and DBX500
chips. This is ABX500 hwmon driver, where the abx500.c is a common layer for
all ABX500s, and the ab8500.c is specific for AB8500 chip. Under this designed
structure, other chip specific files can be added simply using the same common
layer abx500.c.

Signed-off-by: Hongbo Zhang <hongbo.zhang@linaro.org>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Acked-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Anton Vorontsov <anton@enomsg.org>
  • Loading branch information
Hongbo Zhang authored and Anton Vorontsov committed Apr 17, 2013
1 parent ea2be6f commit 0bbb06e
Show file tree
Hide file tree
Showing 7 changed files with 830 additions and 0 deletions.
22 changes: 22 additions & 0 deletions Documentation/hwmon/ab8500
Original file line number Diff line number Diff line change
@@ -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.
28 changes: 28 additions & 0 deletions Documentation/hwmon/abx500
Original file line number Diff line number Diff line change
@@ -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.
13 changes: 13 additions & 0 deletions drivers/hwmon/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions drivers/hwmon/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
206 changes: 206 additions & 0 deletions drivers/hwmon/ab8500.c
Original file line number Diff line number Diff line change
@@ -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");
Loading

0 comments on commit 0bbb06e

Please sign in to comment.