[PATCH linux v5 4/5] drivers: Add hwmon OCC version specific functions
eajames.ibm at gmail.com
eajames.ibm at gmail.com
Tue Nov 8 10:16:10 AEDT 2016
From: "Edward A. James" <eajames at us.ibm.com>
Added functions for the P8 OCC and P9 OCC, as well as interfacing to hook
into common code.
Signed-off-by: Edward A. James <eajames at us.ibm.com>
---
drivers/hwmon/occ/occ.c | 69 +++++++++++++++-
drivers/hwmon/occ/occ.h | 30 ++++++-
drivers/hwmon/occ/p8.c | 185 ++++++++++++++++++++++++++++++++++++++++-
drivers/hwmon/occ/p9.c | 216 +++++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 494 insertions(+), 6 deletions(-)
diff --git a/drivers/hwmon/occ/occ.c b/drivers/hwmon/occ/occ.c
index 733b935c..b0443ea 100644
--- a/drivers/hwmon/occ/occ.c
+++ b/drivers/hwmon/occ/occ.c
@@ -33,9 +33,64 @@
struct occ_driver {
void *bus;
struct occ_bus_ops bus_ops;
+ struct occ_protocol_ops ops;
+ struct occ_protocol protocol;
+ struct device *dev;
+ bool occ_online;
};
-int occ_start(struct device *dev, void *bus, struct occ_bus_ops bus_ops)
+void * occ_get_sensor(struct occ_driver *driver, int sensor_type)
+{
+ return NULL;
+}
+
+static ssize_t show_occ_online(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct occ_driver *driver = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE - 1, "%u\n", driver->occ_online);
+}
+
+static ssize_t store_occ_online(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct occ_driver *driver = dev_get_drvdata(dev);
+ unsigned long val;
+ int rc;
+
+ rc = kstrtoul(buf, 10, &val);
+ if (rc)
+ return rc;
+
+ if (val == 1) {
+ if (driver->occ_online)
+ return count;
+
+ driver->dev = hwmon_device_register(dev);
+ if (IS_ERR(driver->dev))
+ return PTR_ERR(driver->dev);
+
+ dev_set_drvdata(driver->dev, driver);
+ } else if (val == 0) {
+ if (!driver->occ_online)
+ return count;
+
+ hwmon_device_unregister(driver->dev);
+ driver->dev = NULL;
+ } else
+ return -EINVAL;
+
+ driver->occ_online = val;
+ return count;
+}
+
+static DEVICE_ATTR(online, S_IWUSR | S_IRUGO, show_occ_online,
+ store_occ_online);
+
+int occ_start(struct device *dev, void *bus, struct occ_bus_ops bus_ops,
+ struct occ_protocol_ops ops, struct occ_protocol protocol)
{
struct occ_driver *driver = devm_kzalloc(dev,
sizeof(struct occ_driver),
@@ -47,16 +102,24 @@ int occ_start(struct device *dev, void *bus, struct occ_bus_ops bus_ops)
driver->bus = bus;
driver->bus_ops = bus_ops;
+ driver->ops = ops;
+ driver->protocol = protocol;
- return 0;
+ return device_create_file(dev, &dev_attr_online);
}
int occ_stop(struct device *dev)
{
struct occ_driver *driver = dev_get_drvdata(dev);
- if (driver)
+ device_remove_file(dev, &dev_attr_online);
+
+ if (driver) {
+ if (driver->dev)
+ hwmon_device_unregister(driver->dev);
+
devm_kfree(dev, driver);
+ }
return 0;
}
diff --git a/drivers/hwmon/occ/occ.h b/drivers/hwmon/occ/occ.h
index 3232251..1d6f130 100644
--- a/drivers/hwmon/occ/occ.h
+++ b/drivers/hwmon/occ/occ.h
@@ -23,8 +23,36 @@
#include "scom.h"
struct device;
+struct occ_driver;
-int occ_start(struct device *dev, void *bus, struct occ_bus_ops bus_ops);
+enum sensor_type {
+ FREQ = 0,
+ TEMP,
+ POWER,
+ CAPS,
+ MAX_OCC_SENSOR_TYPE
+};
+
+struct occ_protocol_ops {
+ void (*parse_sensor)(u8 *data, void *sensor, int sensor_type, int off,
+ int snum);
+ void *(*alloc_sensor)(int sensor_type, int num_sensors);
+ int (*get_sensor_value)(void *driver, int sensor_type, int snum);
+ int (*get_sensor_id)(void *driver, int sensor_type, int snum);
+ int (*get_caps_value)(void *sensor, int snum, int caps_field);
+};
+
+struct occ_protocol {
+ unsigned int num_caps_fields;
+ char **caps_names;
+ u32 command_addr;
+ u32 response_addr;
+};
+
+int occ_start(struct device *dev, void *bus, struct occ_bus_ops bus_ops,
+ struct occ_protocol_ops ops, struct occ_protocol protocol);
int occ_stop(struct device *dev);
+void *occ_get_sensor(struct occ_driver *driver, int sensor_type);
+
#endif /* __OCC_H__ */
diff --git a/drivers/hwmon/occ/p8.c b/drivers/hwmon/occ/p8.c
index 6ff6764..e8d8d98 100644
--- a/drivers/hwmon/occ/p8.c
+++ b/drivers/hwmon/occ/p8.c
@@ -28,9 +28,192 @@
#include "occ.h"
+/* P8 OCC sensor data format */
+struct occ_sensor_p8 {
+ u16 sensor_id;
+ u16 value;
+};
+
+struct power_sensor_p8 {
+ u16 sensor_id;
+ u32 update_tag;
+ u32 accumulator;
+ u16 value;
+};
+
+struct caps_sensor_p8 {
+ u16 curr_powercap;
+ u16 curr_powerreading;
+ u16 norm_powercap;
+ u16 max_powercap;
+ u16 min_powercap;
+ u16 user_powerlimit;
+};
+
+static char *caps_sensor_names[] = {
+ "curr_powercap",
+ "curr_powerreading",
+ "norm_powercap",
+ "max_powercap",
+ "min_powercap",
+ "user_powerlimit"
+};
+
+void p8_parse_sensor(u8 *data, void *sensor, int sensor_type, int off,
+ int snum)
+{
+ switch (sensor_type) {
+ case FREQ:
+ case TEMP:
+ {
+ struct occ_sensor_p8 *os =
+ &(((struct occ_sensor_p8 *)sensor)[snum]);
+ os->sensor_id =
+ be16_to_cpup((const __be16 *)&data[off]);
+ os->value =
+ be16_to_cpup((const __be16 *)&data[off + 2]);
+ }
+ break;
+ case POWER:
+ {
+ struct power_sensor_p8 *ps =
+ &(((struct power_sensor_p8 *)sensor)[snum]);
+ ps->sensor_id =
+ be16_to_cpup((const __be16 *)&data[off]);
+ ps->update_tag =
+ be32_to_cpup((const __be32 *)&data[off + 2]);
+ ps->accumulator =
+ be32_to_cpup((const __be32 *)&data[off + 6]);
+ ps->value =
+ be16_to_cpup((const __be16 *)&data[off + 10]);
+ }
+ break;
+ case CAPS:
+ {
+ struct caps_sensor_p8 *cs =
+ &(((struct caps_sensor_p8 *)sensor)[snum]);
+ cs->curr_powercap =
+ be16_to_cpup((const __be16 *)&data[off]);
+ cs->curr_powerreading =
+ be16_to_cpup((const __be16 *)&data[off + 2]);
+ cs->norm_powercap =
+ be16_to_cpup((const __be16 *)&data[off + 4]);
+ cs->max_powercap =
+ be16_to_cpup((const __be16 *)&data[off + 6]);
+ cs->min_powercap =
+ be16_to_cpup((const __be16 *)&data[off + 8]);
+ cs->user_powerlimit =
+ be16_to_cpup((const __be16 *)&data[off + 10]);
+ }
+ break;
+ };
+}
+
+void * p8_alloc_sensor(int sensor_type, int num_sensors)
+{
+ switch (sensor_type) {
+ case FREQ:
+ case TEMP:
+ return kcalloc(num_sensors,
+ sizeof(struct occ_sensor_p8),
+ GFP_KERNEL);
+ case POWER:
+ return kcalloc(num_sensors,
+ sizeof(struct power_sensor_p8),
+ GFP_KERNEL);
+ case CAPS:
+ return kcalloc(num_sensors,
+ sizeof(struct caps_sensor_p8),
+ GFP_KERNEL);
+ default:
+ return NULL;
+ }
+}
+
+int p8_get_sensor_value(void *driver, int sensor_type, int snum)
+{
+ void *sensor;
+
+ if (sensor_type == CAPS)
+ return -1;
+
+ sensor = occ_get_sensor(driver, sensor_type);
+ if (!sensor)
+ return -1;
+
+ switch (sensor_type) {
+ case FREQ:
+ case TEMP:
+ return ((struct occ_sensor_p8 *)sensor)[snum].value;
+ case POWER:
+ return ((struct power_sensor_p8 *)sensor)[snum].value;
+ default:
+ return -1;
+ }
+}
+
+int p8_get_sensor_id(void *driver, int sensor_type, int snum)
+{
+ void *sensor;
+ int i = snum;
+
+ if (sensor_type == CAPS)
+ return -1;
+
+ sensor = occ_get_sensor(driver, sensor_type);
+ if (!sensor)
+ return -1;
+
+ switch (sensor_type) {
+ case FREQ:
+ case TEMP:
+ return ((struct occ_sensor_p8 *)sensor)[i].sensor_id;
+ case POWER:
+ return ((struct power_sensor_p8 *)sensor)[i].sensor_id;
+ default:
+ return -1;
+ }
+}
+
+int p8_get_caps_value(void *sensor, int snum, int caps_field)
+{
+ struct caps_sensor_p8 *caps_sensor = sensor;
+
+ switch (caps_field) {
+ case 0:
+ return caps_sensor[snum].curr_powercap;
+ case 1:
+ return caps_sensor[snum].curr_powerreading;
+ case 2:
+ return caps_sensor[snum].norm_powercap;
+ case 3:
+ return caps_sensor[snum].max_powercap;
+ case 4:
+ return caps_sensor[snum].min_powercap;
+ case 5:
+ return caps_sensor[snum].user_powerlimit;
+ default:
+ return -1;
+ }
+}
+
int p8_occ_start(struct device *dev, void *bus, struct occ_bus_ops bus_ops)
{
- return occ_start(dev, bus, bus_ops);
+ struct occ_protocol_ops protocol_ops;
+ struct occ_protocol protocol;
+
+ protocol_ops.parse_sensor = p8_parse_sensor;
+ protocol_ops.alloc_sensor = p8_alloc_sensor;
+ protocol_ops.get_sensor_value = p8_get_sensor_value;
+ protocol_ops.get_sensor_id = p8_get_sensor_id;
+ protocol_ops.get_caps_value = p8_get_caps_value;
+
+ protocol.num_caps_fields = 6;
+ protocol.caps_names = caps_sensor_names;
+ protocol.command_addr = 0xFFFF6000;
+ protocol.response_addr = 0xFFFF7000;
+
+ return occ_start(dev, bus, bus_ops, protocol_ops, protocol);
}
int p8_occ_stop(struct device *dev)
diff --git a/drivers/hwmon/occ/p9.c b/drivers/hwmon/occ/p9.c
index f1aa8ac..907bc11 100644
--- a/drivers/hwmon/occ/p9.c
+++ b/drivers/hwmon/occ/p9.c
@@ -28,9 +28,223 @@
#include "occ.h"
+/* P9 OCC sensor data format */
+struct temp_sensor_p9 {
+ u32 sensor_id;
+ u8 fru_type;
+ u8 value;
+};
+
+struct freq_sensor_p9 {
+ u32 sensor_id;
+ u16 value;
+};
+
+struct power_sensor_p9 {
+ u32 sensor_id;
+ u8 function_id;
+ u8 apss_channel;
+ u16 reserved;
+ u32 update_tag;
+ u64 accumulator;
+ u16 value;
+};
+
+struct caps_sensor_p9 {
+ u16 curr_powercap;
+ u16 curr_powerreading;
+ u16 norm_powercap;
+ u16 max_powercap;
+ u16 min_powercap;
+ u16 user_powerlimit;
+ u8 user_powerlimit_source;
+};
+
+static char *caps_sensor_names[] = {
+ "curr_powercap",
+ "curr_powerreading",
+ "norm_powercap",
+ "max_powercap",
+ "min_powercap",
+ "user_powerlimit",
+ "user_powerlimit_source"
+};
+
+void p9_parse_sensor(u8 *data, void *sensor, int sensor_type, int off,
+ int snum)
+{
+ switch (sensor_type) {
+ case FREQ:
+ {
+ struct freq_sensor_p9 *fs =
+ &(((struct freq_sensor_p9 *)sensor)[snum]);
+ fs->sensor_id =
+ be32_to_cpup((const __be32 *)&data[off]);
+ fs->value =
+ be16_to_cpup((const __be16 *)&data[off + 4]);
+ }
+ break;
+ case TEMP:
+ {
+ struct temp_sensor_p9 *ts =
+ &(((struct temp_sensor_p9 *)sensor)[snum]);
+ ts->sensor_id =
+ be32_to_cpup((const __be32 *)&data[off]);
+ ts->fru_type = data[off + 4];
+ ts->value = data[off + 5];
+ }
+ break;
+ case POWER:
+ {
+ struct power_sensor_p9 *ps =
+ &(((struct power_sensor_p9 *)sensor)[snum]);
+ ps->sensor_id =
+ be32_to_cpup((const __be32 *)&data[off]);
+ ps->function_id = data[off + 4];
+ ps->apss_channel = data[off + 5];
+ /* two bytes reserved */
+ ps->update_tag =
+ be32_to_cpup((const __be32 *)&data[off + 8]);
+ ps->accumulator =
+ be64_to_cpup((const __be64 *)&data[off + 12]);
+ ps->value =
+ be16_to_cpup((const __be16 *)&data[off + 20]);
+ }
+ break;
+ case CAPS:
+ {
+ struct caps_sensor_p9 *cs =
+ &(((struct caps_sensor_p9 *)sensor)[snum]);
+ cs->curr_powercap =
+ be16_to_cpup((const __be16 *)&data[off]);
+ cs->curr_powerreading =
+ be16_to_cpup((const __be16 *)&data[off + 2]);
+ cs->norm_powercap =
+ be16_to_cpup((const __be16 *)&data[off + 4]);
+ cs->max_powercap =
+ be16_to_cpup((const __be16 *)&data[off + 6]);
+ cs->min_powercap =
+ be16_to_cpup((const __be16 *)&data[off + 8]);
+ cs->user_powerlimit =
+ be16_to_cpup((const __be16 *)&data[off + 10]);
+ cs->user_powerlimit_source = data[off + 12];
+ }
+ break;
+ };
+}
+
+void * p9_alloc_sensor(int sensor_type, int num_sensors)
+{
+ switch (sensor_type) {
+ case FREQ:
+ return kcalloc(num_sensors,
+ sizeof(struct freq_sensor_p9),
+ GFP_KERNEL);
+ case TEMP:
+ return kcalloc(num_sensors,
+ sizeof(struct temp_sensor_p9),
+ GFP_KERNEL);
+ case POWER:
+ return kcalloc(num_sensors,
+ sizeof(struct power_sensor_p9),
+ GFP_KERNEL);
+ case CAPS:
+ return kcalloc(num_sensors,
+ sizeof(struct caps_sensor_p9),
+ GFP_KERNEL);
+ default:
+ return NULL;
+ }
+}
+
+int p9_get_sensor_value(void *driver, int sensor_type, int snum)
+{
+ void *sensor;
+
+ if (sensor_type == CAPS)
+ return -1;
+
+ sensor = occ_get_sensor(driver, sensor_type);
+ if (!sensor)
+ return -1;
+
+ switch (sensor_type) {
+ case FREQ:
+ return ((struct freq_sensor_p9 *)sensor)[snum].value;
+ case TEMP:
+ return ((struct temp_sensor_p9 *)sensor)[snum].value;
+ case POWER:
+ return ((struct power_sensor_p9 *)sensor)[snum].value;
+ default:
+ return -1;
+ }
+}
+
+int p9_get_sensor_id(void *driver, int sensor_type, int snum)
+{
+ void *sensor;
+ int i = snum;
+
+ if (sensor_type == CAPS)
+ return -1;
+
+ sensor = occ_get_sensor(driver, sensor_type);
+ if (!sensor)
+ return -1;
+
+ switch (sensor_type) {
+ case FREQ:
+ return ((struct freq_sensor_p9 *)sensor)[i].sensor_id;
+ case TEMP:
+ return ((struct temp_sensor_p9 *)sensor)[i].sensor_id;
+ case POWER:
+ return ((struct power_sensor_p9 *)sensor)[i].sensor_id;
+ default:
+ return -1;
+ }
+}
+
+int p9_get_caps_value(void *sensor, int snum, int caps_field)
+{
+ struct caps_sensor_p9 *caps_sensor = sensor;
+
+ switch (caps_field) {
+ case 0:
+ return caps_sensor[snum].curr_powercap;
+ case 1:
+ return caps_sensor[snum].curr_powerreading;
+ case 2:
+ return caps_sensor[snum].norm_powercap;
+ case 3:
+ return caps_sensor[snum].max_powercap;
+ case 4:
+ return caps_sensor[snum].min_powercap;
+ case 5:
+ return caps_sensor[snum].user_powerlimit;
+ case 6:
+ return caps_sensor[snum].user_powerlimit_source;
+ default:
+ return -1;
+ }
+}
+
int p9_occ_start(struct device *dev, void *bus, struct occ_bus_ops bus_ops)
{
- return occ_start(dev, bus, bus_ops);
+ struct occ_protocol_ops protocol_ops;
+ struct occ_protocol protocol;
+
+ protocol_ops.parse_sensor = p9_parse_sensor;
+ protocol_ops.alloc_sensor = p9_alloc_sensor;
+ protocol_ops.get_sensor_value = p9_get_sensor_value;
+ protocol_ops.get_sensor_id = p9_get_sensor_id;
+ protocol_ops.get_caps_value = p9_get_caps_value;
+
+ protocol.num_caps_fields = 7;
+ protocol.caps_names = caps_sensor_names;
+ protocol.command_addr = 0xFFFBE000;
+ protocol.response_addr = 0xFFFBF000;
+
+ return occ_start(dev, bus, bus_ops, protocol_ops, protocol);
}
int p9_occ_stop(struct device *dev)
--
1.9.1
More information about the openbmc
mailing list