hwmon: (pmbus) Improve support for paged fans
authorGuenter Roeck <guenter.roeck@ericsson.com>
Sat, 5 Mar 2011 15:55:10 +0000 (07:55 -0800)
committerGuenter Roeck <guenter.roeck@ericsson.com>
Tue, 15 Mar 2011 05:39:09 +0000 (22:39 -0700)
So far, it seemed like fans would either all be in page 0, or that
there would be one page per fan.

Turns out this was a wrong assumption. There is at least one PMBus
fan controller which supports three pages with four fans each.
Update code to handle this situation.

Reported-by: Greg Schnorr <gschnorr@cisco.com>
Tested-by: Greg Schnorr <gschnorr@cisco.com>
Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com>
Acked-by: Greg Schnorr <gschnorr@cisco.com>
drivers/hwmon/pmbus_core.c

index b7c64ba31f4c75ff9d039f165691f3c49ca520ae..d025a118bf92c4e9f0e28ec2de8ef052392e923e 100644 (file)
 #define PMBUS_MAX_INPUT_LABELS         4       /* vin, vcap, iin, pin */
 
 /*
- * status, status_vout, status_iout, status_fans, and status_temp
- * are paged. status_input and status_fan34 are unpaged.
- * status_fan34 is a special case to handle a second set of fans
- * on page 0.
+ * status, status_vout, status_iout, status_fans, status_fan34, and status_temp
+ * are paged. status_input is unpaged.
  */
-#define PB_NUM_STATUS_REG      (PMBUS_PAGES * 5 + 2)
+#define PB_NUM_STATUS_REG      (PMBUS_PAGES * 6 + 1)
 
 /*
  * Index into status register array, per status register group
@@ -73,7 +71,7 @@
 #define PB_STATUS_IOUT_BASE    (PB_STATUS_VOUT_BASE + PMBUS_PAGES)
 #define PB_STATUS_FAN_BASE     (PB_STATUS_IOUT_BASE + PMBUS_PAGES)
 #define PB_STATUS_FAN34_BASE   (PB_STATUS_FAN_BASE + PMBUS_PAGES)
-#define PB_STATUS_INPUT_BASE   (PB_STATUS_FAN34_BASE + 1)
+#define PB_STATUS_INPUT_BASE   (PB_STATUS_FAN34_BASE + PMBUS_PAGES)
 #define PB_STATUS_TEMP_BASE    (PB_STATUS_INPUT_BASE + 1)
 
 struct pmbus_sensor {
@@ -327,14 +325,17 @@ static struct pmbus_data *pmbus_update_device(struct device *dev)
                          = pmbus_get_status(client, i, PMBUS_STATUS_FAN_12);
                }
 
+               for (i = 0; i < info->pages; i++) {
+                       if (!(info->func[i] & PMBUS_HAVE_STATUS_FAN34))
+                               continue;
+                       data->status[PB_STATUS_FAN34_BASE + i]
+                         = pmbus_get_status(client, i, PMBUS_STATUS_FAN_34);
+               }
+
                if (info->func[0] & PMBUS_HAVE_STATUS_INPUT)
                        data->status[PB_STATUS_INPUT_BASE]
                          = pmbus_get_status(client, 0, PMBUS_STATUS_INPUT);
 
-               if (info->func[0] & PMBUS_HAVE_STATUS_FAN34)
-                       data->status[PB_STATUS_FAN34_BASE]
-                         = pmbus_get_status(client, 0, PMBUS_STATUS_FAN_34);
-
                for (i = 0; i < data->num_sensors; i++) {
                        struct pmbus_sensor *sensor = &data->sensors[i];
 
@@ -817,6 +818,20 @@ static const int pmbus_fan_status_registers[] = {
        PMBUS_STATUS_FAN_34
 };
 
+static const u32 pmbus_fan_flags[] = {
+       PMBUS_HAVE_FAN12,
+       PMBUS_HAVE_FAN12,
+       PMBUS_HAVE_FAN34,
+       PMBUS_HAVE_FAN34
+};
+
+static const u32 pmbus_fan_status_flags[] = {
+       PMBUS_HAVE_STATUS_FAN12,
+       PMBUS_HAVE_STATUS_FAN12,
+       PMBUS_HAVE_STATUS_FAN34,
+       PMBUS_HAVE_STATUS_FAN34
+};
+
 /*
  * Determine maximum number of sensors, booleans, and labels.
  * To keep things simple, only make a rough high estimate.
@@ -848,17 +863,12 @@ static void pmbus_find_max_attr(struct i2c_client *client,
                        max_labels++;
                }
                if (info->func[page] & PMBUS_HAVE_FAN12) {
-                       if (page == 0) {
-                               max_sensors +=
-                                   ARRAY_SIZE(pmbus_fan_registers) *
-                                   PMBUS_MAX_SENSORS_PER_FAN;
-                               max_booleans +=
-                                   ARRAY_SIZE(pmbus_fan_registers) *
-                                   PMBUS_MAX_BOOLEANS_PER_FAN;
-                       } else {
-                               max_sensors += PMBUS_MAX_SENSORS_PER_FAN;
-                               max_booleans += PMBUS_MAX_BOOLEANS_PER_FAN;
-                       }
+                       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) {
                        if (page == 0) {
@@ -1365,15 +1375,14 @@ static void pmbus_find_attributes(struct i2c_client *client,
         */
        in_index = 1;
        for (page = 0; page < info->pages; page++) {
-               int fans, f;
+               int f;
 
-               if (!(info->func[page] & PMBUS_HAVE_FAN12))
-                       continue;
-
-               fans = page ? 1 : ARRAY_SIZE(pmbus_fan_registers);
-               for (f = 0; f < fans; f++) {
+               for (f = 0; f < ARRAY_SIZE(pmbus_fan_registers); f++) {
                        int regval;
 
+                       if (!(info->func[page] & pmbus_fan_flags[f]))
+                               break;
+
                        if (!pmbus_check_word_register(client, page,
                                                       pmbus_fan_registers[f])
                            || !pmbus_check_byte_register(client, page,
@@ -1399,12 +1408,13 @@ static void pmbus_find_attributes(struct i2c_client *client,
                         * Each fan status register covers multiple fans,
                         * so we have to do some magic.
                         */
-                       if (pmbus_check_byte_register
-                           (client, page, pmbus_fan_status_registers[f])) {
+                       if ((info->func[page] & pmbus_fan_status_flags[f]) &&
+                           pmbus_check_byte_register(client,
+                                       page, pmbus_fan_status_registers[f])) {
                                int base;
 
                                if (f > 1)      /* fan 3, 4 */
-                                       base = PB_STATUS_FAN34_BASE;
+                                       base = PB_STATUS_FAN34_BASE + page;
                                else
                                        base = PB_STATUS_FAN_BASE + page;
                                pmbus_add_boolean_reg(data, "fan", "alarm",