#include "pmbus.h"
/*
- * Constants needed to determine number of sensors, booleans, and labels.
+ * Number of additional attribute pointers to allocate
+ * with each call to krealloc
*/
-#define PMBUS_MAX_INPUT_SENSORS 22 /* 10*volt, 7*curr, 5*power */
-#define PMBUS_VOUT_SENSORS_PER_PAGE 9 /* input, min, max, lcrit,
- crit, lowest, highest, avg,
- reset */
-#define PMBUS_IOUT_SENSORS_PER_PAGE 8 /* input, min, max, crit,
- lowest, highest, avg,
- reset */
-#define PMBUS_POUT_SENSORS_PER_PAGE 7 /* input, cap, max, crit,
- * highest, avg, reset
- */
-#define PMBUS_MAX_SENSORS_PER_FAN 1 /* input */
-#define PMBUS_MAX_SENSORS_PER_TEMP 9 /* input, min, max, lcrit,
- * crit, lowest, highest, avg,
- * reset
- */
-
-#define PMBUS_MAX_INPUT_BOOLEANS 7 /* v: min_alarm, max_alarm,
- lcrit_alarm, crit_alarm;
- c: alarm, crit_alarm;
- p: crit_alarm */
-#define PMBUS_VOUT_BOOLEANS_PER_PAGE 4 /* min_alarm, max_alarm,
- lcrit_alarm, crit_alarm */
-#define PMBUS_IOUT_BOOLEANS_PER_PAGE 3 /* alarm, lcrit_alarm,
- crit_alarm */
-#define PMBUS_POUT_BOOLEANS_PER_PAGE 3 /* cap_alarm, alarm, crit_alarm
- */
-#define PMBUS_MAX_BOOLEANS_PER_FAN 2 /* alarm, fault */
-#define PMBUS_MAX_BOOLEANS_PER_TEMP 4 /* min_alarm, max_alarm,
- lcrit_alarm, crit_alarm */
-
-#define PMBUS_MAX_INPUT_LABELS 4 /* vin, vcap, iin, pin */
+#define PMBUS_ATTR_ALLOC_SIZE 32
/*
* status, status_vout, status_iout, status_fans, status_fan34, and status_temp
int max_attributes;
int num_attributes;
- struct attribute **attributes;
struct attribute_group group;
struct pmbus_sensor *sensors;
return snprintf(buf, PAGE_SIZE, "%s\n", label->label);
}
+static int pmbus_add_attribute(struct pmbus_data *data, struct attribute *attr)
+{
+ if (data->num_attributes >= data->max_attributes - 1) {
+ data->max_attributes += PMBUS_ATTR_ALLOC_SIZE;
+ data->group.attrs = krealloc(data->group.attrs,
+ sizeof(struct attribute *) *
+ data->max_attributes, GFP_KERNEL);
+ if (data->group.attrs == NULL)
+ return -ENOMEM;
+ }
+
+ data->group.attrs[data->num_attributes++] = attr;
+ data->group.attrs[data->num_attributes] = NULL;
+ return 0;
+}
+
static void pmbus_dev_attr_init(struct device_attribute *dev_attr,
const char *name,
umode_t mode,
struct pmbus_boolean *boolean;
struct sensor_device_attribute *a;
- BUG_ON(data->num_attributes >= data->max_attributes);
-
boolean = devm_kzalloc(data->dev, sizeof(*boolean), GFP_KERNEL);
if (!boolean)
return -ENOMEM;
boolean->s2 = s2;
pmbus_attr_init(a, boolean->name, S_IRUGO, pmbus_show_boolean, NULL,
(reg << 8) | mask);
- data->attributes[data->num_attributes++] = &a->dev_attr.attr;
- return 0;
+ return pmbus_add_attribute(data, &a->dev_attr.attr);
}
static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
struct pmbus_sensor *sensor;
struct device_attribute *a;
- BUG_ON(data->num_attributes >= data->max_attributes);
-
sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor)
return NULL;
readonly ? S_IRUGO : S_IRUGO | S_IWUSR,
pmbus_show_sensor, pmbus_set_sensor);
- data->attributes[data->num_attributes++] = &a->attr;
+ if (pmbus_add_attribute(data, &a->attr))
+ return NULL;
+
sensor->next = data->sensors;
data->sensors = sensor;
struct pmbus_label *label;
struct device_attribute *a;
- BUG_ON(data->num_attributes >= data->max_attributes);
-
label = devm_kzalloc(data->dev, sizeof(*label), GFP_KERNEL);
if (!label)
return -ENOMEM;
index);
pmbus_dev_attr_init(a, label->name, S_IRUGO, pmbus_show_label, NULL);
- data->attributes[data->num_attributes++] = &a->attr;
- return 0;
-}
-
-/*
- * Determine maximum number of sensors, booleans, and labels.
- * To keep things simple, only make a rough high estimate.
- */
-static void pmbus_find_max_attr(struct i2c_client *client,
- struct pmbus_data *data)
-{
- const struct pmbus_driver_info *info = data->info;
- int page, max_sensors, max_booleans, max_labels;
-
- max_sensors = PMBUS_MAX_INPUT_SENSORS;
- max_booleans = PMBUS_MAX_INPUT_BOOLEANS;
- max_labels = PMBUS_MAX_INPUT_LABELS;
-
- for (page = 0; page < info->pages; page++) {
- if (info->func[page] & PMBUS_HAVE_VOUT) {
- max_sensors += PMBUS_VOUT_SENSORS_PER_PAGE;
- max_booleans += PMBUS_VOUT_BOOLEANS_PER_PAGE;
- max_labels++;
- }
- if (info->func[page] & PMBUS_HAVE_IOUT) {
- max_sensors += PMBUS_IOUT_SENSORS_PER_PAGE;
- max_booleans += PMBUS_IOUT_BOOLEANS_PER_PAGE;
- max_labels++;
- }
- if (info->func[page] & PMBUS_HAVE_POUT) {
- max_sensors += PMBUS_POUT_SENSORS_PER_PAGE;
- max_booleans += PMBUS_POUT_BOOLEANS_PER_PAGE;
- max_labels++;
- }
- if (info->func[page] & PMBUS_HAVE_FAN12) {
- max_sensors += 2 * PMBUS_MAX_SENSORS_PER_FAN;
- max_booleans += 2 * PMBUS_MAX_BOOLEANS_PER_FAN;
- }
- if (info->func[page] & PMBUS_HAVE_FAN34) {
- max_sensors += 2 * PMBUS_MAX_SENSORS_PER_FAN;
- max_booleans += 2 * PMBUS_MAX_BOOLEANS_PER_FAN;
- }
- if (info->func[page] & PMBUS_HAVE_TEMP) {
- max_sensors += PMBUS_MAX_SENSORS_PER_TEMP;
- max_booleans += PMBUS_MAX_BOOLEANS_PER_TEMP;
- }
- if (info->func[page] & PMBUS_HAVE_TEMP2) {
- max_sensors += PMBUS_MAX_SENSORS_PER_TEMP;
- max_booleans += PMBUS_MAX_BOOLEANS_PER_TEMP;
- }
- if (info->func[page] & PMBUS_HAVE_TEMP3) {
- max_sensors += PMBUS_MAX_SENSORS_PER_TEMP;
- max_booleans += PMBUS_MAX_BOOLEANS_PER_TEMP;
- }
- }
- data->max_attributes = max_sensors + max_booleans + max_labels;
+ return pmbus_add_attribute(data, &a->attr);
}
/*
}
}
- /* Determine maximum number of sensors, booleans, and labels */
- pmbus_find_max_attr(client, data);
pmbus_clear_fault_page(client, 0);
return 0;
}
return ret;
}
- data->attributes = devm_kzalloc(dev, sizeof(struct attribute *)
- * data->max_attributes, GFP_KERNEL);
- if (!data->attributes)
- return -ENOMEM;
-
ret = pmbus_find_attributes(client, data);
if (ret)
- return ret;
+ goto out_kfree;
/*
* If there are no attributes, something is wrong.
*/
if (!data->num_attributes) {
dev_err(dev, "No attributes found\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto out_kfree;
}
/* Register sysfs hooks */
- data->group.attrs = data->attributes;
ret = sysfs_create_group(&dev->kobj, &data->group);
if (ret) {
dev_err(dev, "Failed to create sysfs entries\n");
- return ret;
+ goto out_kfree;
}
data->hwmon_dev = hwmon_device_register(dev);
if (IS_ERR(data->hwmon_dev)) {
out_hwmon_device_register:
sysfs_remove_group(&dev->kobj, &data->group);
+out_kfree:
+ kfree(data->group.attrs);
return ret;
}
EXPORT_SYMBOL_GPL(pmbus_do_probe);
struct pmbus_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &data->group);
+ kfree(data->group.attrs);
return 0;
}
EXPORT_SYMBOL_GPL(pmbus_do_remove);