Merge branch '3.2-without-smb2' of git://git.samba.org/sfrench/cifs-2.6
[pandora-kernel.git] / drivers / hwmon / pmbus / adm1275.c
index c936e27..980a4d9 100644 (file)
@@ -23,6 +23,8 @@
 #include <linux/i2c.h>
 #include "pmbus.h"
 
+enum chips { adm1275, adm1276 };
+
 #define ADM1275_PEAK_IOUT              0xd0
 #define ADM1275_PEAK_VIN               0xd1
 #define ADM1275_PEAK_VOUT              0xd2
 #define ADM1275_VIN_VOUT_SELECT                (1 << 6)
 #define ADM1275_VRANGE                 (1 << 5)
 
+#define ADM1275_IOUT_WARN2_LIMIT       0xd7
+#define ADM1275_DEVICE_CONFIG          0xd8
+
+#define ADM1275_IOUT_WARN2_SELECT      (1 << 4)
+
+#define ADM1276_PEAK_PIN               0xda
+
+#define ADM1275_MFR_STATUS_IOUT_WARN2  (1 << 0)
+
+struct adm1275_data {
+       int id;
+       bool have_oc_fault;
+       struct pmbus_driver_info info;
+};
+
+#define to_adm1275_data(x)  container_of(x, struct adm1275_data, info)
+
 static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
 {
-       int ret;
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       const struct adm1275_data *data = to_adm1275_data(info);
+       int ret = 0;
 
        if (page)
-               return -EINVAL;
+               return -ENXIO;
 
        switch (reg) {
+       case PMBUS_IOUT_UC_FAULT_LIMIT:
+               if (data->have_oc_fault) {
+                       ret = -ENXIO;
+                       break;
+               }
+               ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT);
+               break;
+       case PMBUS_IOUT_OC_FAULT_LIMIT:
+               if (!data->have_oc_fault) {
+                       ret = -ENXIO;
+                       break;
+               }
+               ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT);
+               break;
        case PMBUS_VIRT_READ_IOUT_MAX:
                ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT);
                break;
@@ -48,10 +83,20 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
        case PMBUS_VIRT_READ_VIN_MAX:
                ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN);
                break;
+       case PMBUS_VIRT_READ_PIN_MAX:
+               if (data->id != adm1276) {
+                       ret = -ENXIO;
+                       break;
+               }
+               ret = pmbus_read_word_data(client, 0, ADM1276_PEAK_PIN);
+               break;
        case PMBUS_VIRT_RESET_IOUT_HISTORY:
        case PMBUS_VIRT_RESET_VOUT_HISTORY:
        case PMBUS_VIRT_RESET_VIN_HISTORY:
-               ret = 0;
+               break;
+       case PMBUS_VIRT_RESET_PIN_HISTORY:
+               if (data->id != adm1276)
+                       ret = -ENXIO;
                break;
        default:
                ret = -ENODATA;
@@ -66,9 +111,14 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
        int ret;
 
        if (page)
-               return -EINVAL;
+               return -ENXIO;
 
        switch (reg) {
+       case PMBUS_IOUT_UC_FAULT_LIMIT:
+       case PMBUS_IOUT_OC_FAULT_LIMIT:
+               ret = pmbus_write_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT,
+                                           word);
+               break;
        case PMBUS_VIRT_RESET_IOUT_HISTORY:
                ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0);
                break;
@@ -78,6 +128,41 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
        case PMBUS_VIRT_RESET_VIN_HISTORY:
                ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0);
                break;
+       case PMBUS_VIRT_RESET_PIN_HISTORY:
+               ret = pmbus_write_word_data(client, 0, ADM1276_PEAK_PIN, 0);
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
+       return ret;
+}
+
+static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       const struct adm1275_data *data = to_adm1275_data(info);
+       int mfr_status, ret;
+
+       if (page > 0)
+               return -ENXIO;
+
+       switch (reg) {
+       case PMBUS_STATUS_IOUT:
+               ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_IOUT);
+               if (ret < 0)
+                       break;
+               mfr_status = pmbus_read_byte_data(client, page,
+                                                 PMBUS_STATUS_MFR_SPECIFIC);
+               if (mfr_status < 0) {
+                       ret = mfr_status;
+                       break;
+               }
+               if (mfr_status & ADM1275_MFR_STATUS_IOUT_WARN2) {
+                       ret |= data->have_oc_fault ?
+                         PB_IOUT_OC_FAULT : PB_IOUT_UC_FAULT;
+               }
+               break;
        default:
                ret = -ENODATA;
                break;
@@ -88,16 +173,17 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
 static int adm1275_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
-       int config;
+       int config, device_config;
        int ret;
        struct pmbus_driver_info *info;
+       struct adm1275_data *data;
 
        if (!i2c_check_functionality(client->adapter,
                                     I2C_FUNC_SMBUS_READ_BYTE_DATA))
                return -ENODEV;
 
-       info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL);
-       if (!info)
+       data = kzalloc(sizeof(struct adm1275_data), GFP_KERNEL);
+       if (!data)
                return -ENOMEM;
 
        config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG);
@@ -106,6 +192,15 @@ static int adm1275_probe(struct i2c_client *client,
                goto err_mem;
        }
 
+       device_config = i2c_smbus_read_byte_data(client, ADM1275_DEVICE_CONFIG);
+       if (device_config < 0) {
+               ret = device_config;
+               goto err_mem;
+       }
+
+       data->id = id->driver_data;
+       info = &data->info;
+
        info->pages = 1;
        info->format[PSC_VOLTAGE_IN] = direct;
        info->format[PSC_VOLTAGE_OUT] = direct;
@@ -116,6 +211,7 @@ static int adm1275_probe(struct i2c_client *client,
        info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
 
        info->read_word_data = adm1275_read_word_data;
+       info->read_byte_data = adm1275_read_byte_data;
        info->write_word_data = adm1275_write_word_data;
 
        if (config & ADM1275_VRANGE) {
@@ -134,10 +230,36 @@ static int adm1275_probe(struct i2c_client *client,
                info->R[PSC_VOLTAGE_OUT] = -1;
        }
 
-       if (config & ADM1275_VIN_VOUT_SELECT)
-               info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
-       else
-               info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT;
+       if (device_config & ADM1275_IOUT_WARN2_SELECT)
+               data->have_oc_fault = true;
+
+       switch (id->driver_data) {
+       case adm1275:
+               if (config & ADM1275_VIN_VOUT_SELECT)
+                       info->func[0] |=
+                         PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+               else
+                       info->func[0] |=
+                         PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT;
+               break;
+       case adm1276:
+               info->format[PSC_POWER] = direct;
+               info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN
+                 | PMBUS_HAVE_STATUS_INPUT;
+               if (config & ADM1275_VIN_VOUT_SELECT)
+                       info->func[0] |=
+                         PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+               if (config & ADM1275_VRANGE) {
+                       info->m[PSC_POWER] = 6043;
+                       info->b[PSC_POWER] = 0;
+                       info->R[PSC_POWER] = -2;
+               } else {
+                       info->m[PSC_POWER] = 2115;
+                       info->b[PSC_POWER] = 0;
+                       info->R[PSC_POWER] = -1;
+               }
+               break;
+       }
 
        ret = pmbus_do_probe(client, id, info);
        if (ret)
@@ -145,22 +267,23 @@ static int adm1275_probe(struct i2c_client *client,
        return 0;
 
 err_mem:
-       kfree(info);
+       kfree(data);
        return ret;
 }
 
 static int adm1275_remove(struct i2c_client *client)
 {
        const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
-       int ret;
+       const struct adm1275_data *data = to_adm1275_data(info);
 
-       ret = pmbus_do_remove(client);
-       kfree(info);
-       return ret;
+       pmbus_do_remove(client);
+       kfree(data);
+       return 0;
 }
 
 static const struct i2c_device_id adm1275_id[] = {
-       {"adm1275", 0},
+       { "adm1275", adm1275 },
+       { "adm1276", adm1276 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, adm1275_id);
@@ -185,7 +308,7 @@ static void __exit adm1275_exit(void)
 }
 
 MODULE_AUTHOR("Guenter Roeck");
-MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275");
+MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles");
 MODULE_LICENSE("GPL");
 module_init(adm1275_init);
 module_exit(adm1275_exit);