diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hwmon/Kconfig | 8 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/ibmpowernv.c | 529 | ||||
-rw-r--r-- | drivers/macintosh/adb.c | 57 | ||||
-rw-r--r-- | drivers/watchdog/booke_wdt.c | 8 |
5 files changed, 581 insertions, 22 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index f288b60..b53d879 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -554,6 +554,14 @@ config SENSORS_IBMPEX This driver can also be built as a module. If so, the module will be called ibmpex. +config SENSORS_IBMPOWERNV + tristate "IBM PowerNv Platform temperature/power/fan sensor" + depends on PPC_POWERNV + default y + help + If you say yes here you get support for the temperature/fan/power + sensors on your platform. + config SENSORS_IIO_HWMON tristate "Hwmon driver that uses channels specified via iio maps" depends on IIO diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index c48f987..199c401 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o +obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o obj-$(CONFIG_SENSORS_IIO_HWMON) += iio_hwmon.o obj-$(CONFIG_SENSORS_INA209) += ina209.o obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c new file mode 100644 index 0000000..b7b1297 --- /dev/null +++ b/drivers/hwmon/ibmpowernv.c @@ -0,0 +1,529 @@ +/* + * hwmon driver for temperature/power/fan on IBM PowerNV platform + * Copyright (C) 2013 IBM + * + * 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. + * + * 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/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/of.h> +#include <linux/slab.h> + +#include <linux/jiffies.h> +#include <linux/platform_device.h> +#include <asm/opal.h> +#include <linux/err.h> + +MODULE_DESCRIPTION("IBM PowerNV Platform power/temp/fan sensor hwmon module"); +MODULE_LICENSE("GPL"); + +#define MAX_ATTR_LENGTH 32 + +/* Device tree sensor name prefixes. The device tree has the names in the + * format "cooling-fan#2-faulted" where the "cooling-fan" is the sensor type, + * 2 is the sensor count, and "faulted" is the sensor data attribute type. + */ +#define DT_FAULT_ATTR_SUFFIX "faulted" +#define DT_DATA_ATTR_SUFFIX "data" +#define DT_THRESHOLD_ATTR_SUFFIX "thrs" + +enum sensors { + FAN, + TEMPERATURE, + POWERSUPPLY, + POWER, + MAX_SENSOR_TYPE, +}; + +enum attributes { + INPUT, + MINIMUM, + MAXIMUM, + FAULT, + MAX_ATTR_TYPES +}; + +static struct sensor_name { + char *name; + char *compaible; +} sensor_names[] = { + {"fan-sensor", "ibm,opal-sensor-cooling-fan"}, + {"amb-temp-sensor", "ibm,opal-sensor-amb-temp"}, + {"power-sensor", "ibm,opal-sensor-power-supply"}, + {"power", "ibm,opal-sensor-power"} +}; + +static const char * const attribute_type_table[] = { + "input", + "min", + "max", + "fault", + NULL +}; + +struct pdev_entry { + struct list_head list; + struct platform_device *pdev; + enum sensors type; +}; + +static LIST_HEAD(pdev_list); + +/* The sensors are categorised on type. + * + * The sensors of same type are categorised under a common platform device. + * So, The pdev is shared by all sensors of same type. + * Ex : temp1_input, temp1_max, temp2_input,temp2_max all share same platform + * device. + * + * "sensor_data" is the Platform device specific data. + * There is one hwmon_device instance for all the sensors of same type. + * This also holds the list of all sensors with same type but different + * attribute and index. + */ +struct sensor_specific_data { + u32 sensor_id; /* The hex value as in the device tree */ + u32 sensor_index; /* The sensor instance index */ + struct sensor_device_attribute sd_attr; + enum attributes attr_type; + char attr_name[64]; +}; + +struct sensor_data { + struct device *hwmon_dev; + struct list_head sensor_list; + struct device_attribute name_attr; +}; + +struct sensor_entry { + struct list_head list; + struct sensor_specific_data *sensor_data; +}; + +static struct platform_device *powernv_sensor_get_pdev(enum sensors type) +{ + struct pdev_entry *p; + list_for_each_entry(p, &pdev_list, list) + if (p->type == type) + return p->pdev; + + return NULL; +} + +static struct sensor_specific_data *powernv_sensor_get_sensor_data( + struct sensor_data *pdata, + int index, enum attributes attr_type) +{ + struct sensor_entry *p; + list_for_each_entry(p, &pdata->sensor_list, list) + if ((p->sensor_data->sensor_index == index) && + (attr_type == p->sensor_data->attr_type)) + return p->sensor_data; + + return NULL; +} + +static ssize_t show_name(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + + return sprintf(buf, "%s\n", pdev->name); +} + +/* Note: Data from the sensors for each sensor type needs to be converted to + * the dimension appropriate. + */ +static ssize_t show_sensor(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(devattr); + struct platform_device *pdev = to_platform_device(dev); + struct sensor_data *pdata = platform_get_drvdata(pdev); + struct sensor_specific_data *tdata = NULL; + enum sensors sensor_type = pdev->id; + u32 x = -1; + int ret; + + if (sd_attr && sd_attr->dev_attr.attr.name) { + char *pos = strchr(sd_attr->dev_attr.attr.name, '_'); + int i; + + for (i = 0; i < MAX_ATTR_TYPES; i++) { + if (strcmp(pos+1, attribute_type_table[i]) == 0) { + tdata = powernv_sensor_get_sensor_data(pdata, + sd_attr->index, i); + break; + } + } + } + + if (tdata) { + ret = opal_get_sensor_data(tdata->sensor_id, &x); + if (ret) + x = -1; + } + + if (sensor_type == TEMPERATURE && x > 0) { + /* Temperature comes in Degrees and convert it to + * milli-degrees. + */ + x = x*1000; + } else if (sensor_type == POWER && x > 0) { + /* Power value comes in watts, convert to micro-watts */ + x = x * 1000000; + } + + return sprintf(buf, "%d\n", x); +} + +static u32 get_sensor_index_from_name(const char *name) +{ + char *hash_position = strchr(name, '#'); + u32 index = 0, copy_length; + char newbuf[8]; + + if (hash_position) { + copy_length = strchr(hash_position, '-') - hash_position - 1; + if (copy_length < sizeof(newbuf)) { + strncpy(newbuf, hash_position + 1, copy_length); + sscanf(newbuf, "%d", &index); + } + } + + return index; +} + +static inline void get_sensor_suffix_from_name(const char *name, char *suffix) +{ + char *dash_position = strrchr(name, '-'); + if (dash_position) + strncpy(suffix, dash_position+1, MAX_ATTR_LENGTH); + else + strcpy(suffix,""); +} + +static int get_sensor_attr_properties(const char *sensor_name, + enum sensors sensor_type, enum attributes *attr_type, + u32 *sensor_index) +{ + char suffix[MAX_ATTR_LENGTH]; + + *attr_type = MAX_ATTR_TYPES; + *sensor_index = get_sensor_index_from_name(sensor_name); + if (*sensor_index == 0) + return -EINVAL; + + get_sensor_suffix_from_name(sensor_name, suffix); + if (strcmp(suffix, "") == 0) + return -EINVAL; + + if (strcmp(suffix, DT_FAULT_ATTR_SUFFIX) == 0) + *attr_type = FAULT; + else if (strcmp(suffix, DT_DATA_ATTR_SUFFIX) == 0) + *attr_type = INPUT; + else if ((sensor_type == TEMPERATURE) && + (strcmp(suffix, DT_THRESHOLD_ATTR_SUFFIX) == 0)) + *attr_type = MAXIMUM; + else if ((sensor_type == FAN) && + (strcmp(suffix, DT_THRESHOLD_ATTR_SUFFIX) == 0)) + *attr_type = MINIMUM; + else + return -ENOENT; + + if (((sensor_type == FAN) && ((*attr_type == INPUT) || + (*attr_type == MINIMUM))) + || ((sensor_type == TEMPERATURE) && ((*attr_type == INPUT) || + (*attr_type == MAXIMUM))) + || ((sensor_type == POWER) && ((*attr_type == INPUT)))) + return 0; + + return -ENOENT; +} + +static int create_sensor_attr(struct sensor_specific_data *tdata, + struct device *dev, enum sensors sensor_type, + enum attributes attr_type) +{ + int err = 0; + char temp_file_prefix[50]; + static const char *const file_name_format = "%s%d_%s"; + + tdata->attr_type = attr_type; + + if (sensor_type == FAN) + strcpy(temp_file_prefix, "fan"); + else if (sensor_type == TEMPERATURE) + strcpy(temp_file_prefix, "temp"); + else if (sensor_type == POWERSUPPLY) + strcpy(temp_file_prefix, "powersupply"); + else if (sensor_type == POWER) + strcpy(temp_file_prefix, "power"); + + snprintf(tdata->attr_name, sizeof(tdata->attr_name), file_name_format, + temp_file_prefix, tdata->sensor_index, + attribute_type_table[tdata->attr_type]); + + sysfs_attr_init(&tdata->sd_attr.dev_attr.attr); + tdata->sd_attr.dev_attr.attr.name = tdata->attr_name; + tdata->sd_attr.dev_attr.attr.mode = S_IRUGO; + tdata->sd_attr.dev_attr.show = show_sensor; + + tdata->sd_attr.index = tdata->sensor_index; + err = device_create_file(dev, &tdata->sd_attr.dev_attr); + + return err; +} + +static int create_name_attr(struct sensor_data *pdata, + struct device *dev) +{ + sysfs_attr_init(&pdata->name_attr.attr); + pdata->name_attr.attr.name = "name"; + pdata->name_attr.attr.mode = S_IRUGO; + pdata->name_attr.show = show_name; + return device_create_file(dev, &pdata->name_attr); +} + +static int create_platform_device(enum sensors sensor_type, + struct platform_device **pdev) +{ + struct pdev_entry *pdev_entry = NULL; + int err; + + *pdev = platform_device_alloc(sensor_names[sensor_type].name, + sensor_type); + if (!*pdev) { + pr_err("Device allocation failed\n"); + err = -ENOMEM; + goto exit; + } + + pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL); + if (!pdev_entry) { + pr_err("Device allocation failed\n"); + err = -ENOMEM; + goto exit_device_put; + } + + err = platform_device_add(*pdev); + if (err) { + pr_err("Device addition failed (%d)\n", err); + goto exit_device_free; + } + + pdev_entry->pdev = *pdev; + pdev_entry->type = (*pdev)->id; + + list_add_tail(&pdev_entry->list, &pdev_list); + + return 0; +exit_device_free: + kfree(pdev_entry); +exit_device_put: + platform_device_put(*pdev); +exit: + return err; +} + +static int create_sensor_data(struct platform_device *pdev) +{ + struct sensor_data *pdata = NULL; + int err = 0; + + pdata = kzalloc(sizeof(struct sensor_data), GFP_KERNEL); + if (!pdata) { + err = -ENOMEM; + goto exit; + } + + err = create_name_attr(pdata, &pdev->dev); + if (err) + goto exit_free; + + pdata->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(pdata->hwmon_dev)) { + err = PTR_ERR(pdata->hwmon_dev); + dev_err(&pdev->dev, "Class registration failed (%d)\n", + err); + goto exit_name; + } + + INIT_LIST_HEAD(&pdata->sensor_list); + platform_set_drvdata(pdev, pdata); + + return 0; + +exit_name: + device_remove_file(&pdev->dev, &pdata->name_attr); +exit_free: + kfree(pdata); +exit: + return err; +} + +static void delete_sensor_attr(struct sensor_data *pdata) +{ + struct sensor_entry *s, *l; + + list_for_each_entry_safe(s, l, &pdata->sensor_list, list) { + struct sensor_specific_data *tdata = s->sensor_data; + kfree(tdata); + list_del(&s->list); + kfree(s); + } +} + +static int powernv_sensor_init(u32 sensor_id, const struct device_node *np, + enum sensors sensor_type, enum attributes attr_type, + u32 sensor_index) +{ + struct platform_device *pdev = powernv_sensor_get_pdev(sensor_type); + struct sensor_specific_data *tdata; + struct sensor_entry *sensor_entry; + struct sensor_data *pdata; + int err = 0; + + if (!pdev) { + err = create_platform_device(sensor_type, &pdev); + if (err) + goto exit; + + err = create_sensor_data(pdev); + if (err) + goto exit; + } + + pdata = platform_get_drvdata(pdev); + if (!pdata) { + err = -ENOMEM; + goto exit; + } + + tdata = kzalloc(sizeof(struct sensor_specific_data), GFP_KERNEL); + if (!tdata) { + err = -ENOMEM; + goto exit; + } + + tdata->sensor_id = sensor_id; + tdata->sensor_index = sensor_index; + + err = create_sensor_attr(tdata, &pdev->dev, sensor_type, attr_type); + if (err) + goto exit_free; + + sensor_entry = kzalloc(sizeof(struct sensor_entry), GFP_KERNEL); + if (!sensor_entry) { + err = -ENOMEM; + goto exit_attr; + } + + sensor_entry->sensor_data = tdata; + + list_add_tail(&sensor_entry->list, &pdata->sensor_list); + + return 0; +exit_attr: + device_remove_file(&pdev->dev, &tdata->sd_attr.dev_attr); +exit_free: + kfree(tdata); +exit: + return err; +} + +static void delete_unregister_sensors(void) +{ + struct pdev_entry *p, *n; + + list_for_each_entry_safe(p, n, &pdev_list, list) { + struct sensor_data *pdata = platform_get_drvdata(p->pdev); + if (pdata) { + delete_sensor_attr(pdata); + + hwmon_device_unregister(pdata->hwmon_dev); + kfree(pdata); + } + platform_device_unregister(p->pdev); + list_del(&p->list); + kfree(p); + } +} + +static int __init powernv_hwmon_init(void) +{ + struct device_node *opal, *np = NULL; + enum attributes attr_type; + enum sensors type; + const u32 *sensor_id; + u32 sensor_index; + int err; + + opal = of_find_node_by_path("/ibm,opal/sensors"); + if (!opal) { + pr_err("%s: Opal 'sensors' node not found\n", __func__); + return -ENXIO; + } + + for_each_child_of_node(opal, np) { + if (np->name == NULL) + continue; + + for (type = 0; type < MAX_SENSOR_TYPE; type++) + if (of_device_is_compatible(np, + sensor_names[type].compaible)) + break; + + if (type == MAX_SENSOR_TYPE) + continue; + + if (get_sensor_attr_properties(np->name, type, &attr_type, + &sensor_index)) + continue; + + sensor_id = of_get_property(np, "sensor-id", NULL); + if (!sensor_id) { + pr_info("%s: %s doesn't have sensor-id\n", __func__, + np->name); + continue; + } + + err = powernv_sensor_init(*sensor_id, np, type, attr_type, + sensor_index); + if (err) { + of_node_put(opal); + goto exit; + } + } + of_node_put(opal); + + return 0; +exit: + delete_unregister_sensors(); + return err; + +} + +static void powernv_hwmon_exit(void) +{ + delete_unregister_sensors(); +} + +module_init(powernv_hwmon_init); +module_exit(powernv_hwmon_exit); diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index 04a5049..9e9c5675 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -38,7 +38,7 @@ #include <linux/platform_device.h> #include <linux/mutex.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #ifdef CONFIG_PPC #include <asm/prom.h> #include <asm/machdep.h> @@ -193,8 +193,7 @@ static int adb_scan_bus(void) break; noMovement = 0; - } - else { + } else { /* * No devices left at address i; move the * one(s) we moved to `highFree' back to i. @@ -263,7 +262,7 @@ adb_reset_bus(void) /* * notify clients before sleep */ -static int adb_suspend(struct platform_device *dev, pm_message_t state) +static int __adb_suspend(struct platform_device *dev, pm_message_t state) { adb_got_sleep = 1; /* We need to get a lock on the probe thread */ @@ -276,10 +275,25 @@ static int adb_suspend(struct platform_device *dev, pm_message_t state) return 0; } +static int adb_suspend(struct device *dev) +{ + return __adb_suspend(to_platform_device(dev), PMSG_SUSPEND); +} + +static int adb_freeze(struct device *dev) +{ + return __adb_suspend(to_platform_device(dev), PMSG_FREEZE); +} + +static int adb_poweroff(struct device *dev) +{ + return __adb_suspend(to_platform_device(dev), PMSG_HIBERNATE); +} + /* * reset bus after sleep */ -static int adb_resume(struct platform_device *dev) +static int __adb_resume(struct platform_device *dev) { adb_got_sleep = 0; up(&adb_probe_mutex); @@ -287,6 +301,11 @@ static int adb_resume(struct platform_device *dev) return 0; } + +static int adb_resume(struct device *dev) +{ + return __adb_resume(to_platform_device(dev)); +} #endif /* CONFIG_PM */ static int __init adb_init(void) @@ -502,7 +521,7 @@ void adb_input(unsigned char *buf, int nb, int autopoll) { int i, id; - static int dump_adb_input = 0; + static int dump_adb_input; unsigned long flags; void (*handler)(unsigned char *, int, int); @@ -624,8 +643,7 @@ do_adb_query(struct adb_request *req) { int ret = -EINVAL; - switch(req->data[1]) - { + switch(req->data[1]) { case ADB_QUERY_GETDEVINFO: if (req->nbytes < 3) break; @@ -697,7 +715,7 @@ static ssize_t adb_read(struct file *file, char __user *buf, int ret = 0; struct adbdev_state *state = file->private_data; struct adb_request *req; - DECLARE_WAITQUEUE(wait,current); + DECLARE_WAITQUEUE(wait, current); unsigned long flags; if (count < 2) @@ -794,8 +812,8 @@ static ssize_t adb_write(struct file *file, const char __user *buf, } /* Special case for ADB_BUSRESET request, all others are sent to the controller */ - else if ((req->data[0] == ADB_PACKET)&&(count > 1) - &&(req->data[1] == ADB_BUSRESET)) { + else if ((req->data[0] == ADB_PACKET) && (count > 1) + && (req->data[1] == ADB_BUSRESET)) { ret = do_adb_reset_bus(); up(&adb_probe_mutex); atomic_dec(&state->n_pending); @@ -831,14 +849,25 @@ static const struct file_operations adb_fops = { .release = adb_release, }; +#ifdef CONFIG_PM +static const struct dev_pm_ops adb_dev_pm_ops = { + .suspend = adb_suspend, + .resume = adb_resume, + /* Hibernate hooks */ + .freeze = adb_freeze, + .thaw = adb_resume, + .poweroff = adb_poweroff, + .restore = adb_resume, +}; +#endif + static struct platform_driver adb_pfdrv = { .driver = { .name = "adb", - }, #ifdef CONFIG_PM - .suspend = adb_suspend, - .resume = adb_resume, + .pm = &adb_dev_pm_ops, #endif + }, }; static struct platform_device adb_pfdev = { diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index f1b8d55..a8dbceb3 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c @@ -138,14 +138,6 @@ static void __booke_wdt_enable(void *data) val &= ~WDTP_MASK; val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(booke_wdt_period)); -#ifdef CONFIG_PPC_BOOK3E_64 - /* - * Crit ints are currently broken on PPC64 Book-E, so - * just disable them for now. - */ - val &= ~TCR_WIE; -#endif - mtspr(SPRN_TCR, val); } |