1*9d2ecfb7SGuenter Roeck /* 2*9d2ecfb7SGuenter Roeck * Hardware monitoring driver for Maxim MAX34440/MAX34441 3*9d2ecfb7SGuenter Roeck * 4*9d2ecfb7SGuenter Roeck * Copyright (c) 2011 Ericsson AB. 5*9d2ecfb7SGuenter Roeck * 6*9d2ecfb7SGuenter Roeck * This program is free software; you can redistribute it and/or modify 7*9d2ecfb7SGuenter Roeck * it under the terms of the GNU General Public License as published by 8*9d2ecfb7SGuenter Roeck * the Free Software Foundation; either version 2 of the License, or 9*9d2ecfb7SGuenter Roeck * (at your option) any later version. 10*9d2ecfb7SGuenter Roeck * 11*9d2ecfb7SGuenter Roeck * This program is distributed in the hope that it will be useful, 12*9d2ecfb7SGuenter Roeck * but WITHOUT ANY WARRANTY; without even the implied warranty of 13*9d2ecfb7SGuenter Roeck * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14*9d2ecfb7SGuenter Roeck * GNU General Public License for more details. 15*9d2ecfb7SGuenter Roeck * 16*9d2ecfb7SGuenter Roeck * You should have received a copy of the GNU General Public License 17*9d2ecfb7SGuenter Roeck * along with this program; if not, write to the Free Software 18*9d2ecfb7SGuenter Roeck * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19*9d2ecfb7SGuenter Roeck */ 20*9d2ecfb7SGuenter Roeck 21*9d2ecfb7SGuenter Roeck #include <linux/kernel.h> 22*9d2ecfb7SGuenter Roeck #include <linux/module.h> 23*9d2ecfb7SGuenter Roeck #include <linux/init.h> 24*9d2ecfb7SGuenter Roeck #include <linux/err.h> 25*9d2ecfb7SGuenter Roeck #include <linux/i2c.h> 26*9d2ecfb7SGuenter Roeck #include "pmbus.h" 27*9d2ecfb7SGuenter Roeck 28*9d2ecfb7SGuenter Roeck enum chips { max34440, max34441 }; 29*9d2ecfb7SGuenter Roeck 30*9d2ecfb7SGuenter Roeck #define MAX34440_STATUS_OC_WARN (1 << 0) 31*9d2ecfb7SGuenter Roeck #define MAX34440_STATUS_OC_FAULT (1 << 1) 32*9d2ecfb7SGuenter Roeck #define MAX34440_STATUS_OT_FAULT (1 << 5) 33*9d2ecfb7SGuenter Roeck #define MAX34440_STATUS_OT_WARN (1 << 6) 34*9d2ecfb7SGuenter Roeck 35*9d2ecfb7SGuenter Roeck static int max34440_read_byte_data(struct i2c_client *client, int page, int reg) 36*9d2ecfb7SGuenter Roeck { 37*9d2ecfb7SGuenter Roeck int ret; 38*9d2ecfb7SGuenter Roeck int mfg_status; 39*9d2ecfb7SGuenter Roeck 40*9d2ecfb7SGuenter Roeck ret = pmbus_set_page(client, page); 41*9d2ecfb7SGuenter Roeck if (ret < 0) 42*9d2ecfb7SGuenter Roeck return ret; 43*9d2ecfb7SGuenter Roeck 44*9d2ecfb7SGuenter Roeck switch (reg) { 45*9d2ecfb7SGuenter Roeck case PMBUS_STATUS_IOUT: 46*9d2ecfb7SGuenter Roeck mfg_status = pmbus_read_word_data(client, 0, 47*9d2ecfb7SGuenter Roeck PMBUS_STATUS_MFR_SPECIFIC); 48*9d2ecfb7SGuenter Roeck if (mfg_status < 0) 49*9d2ecfb7SGuenter Roeck return mfg_status; 50*9d2ecfb7SGuenter Roeck if (mfg_status & MAX34440_STATUS_OC_WARN) 51*9d2ecfb7SGuenter Roeck ret |= PB_IOUT_OC_WARNING; 52*9d2ecfb7SGuenter Roeck if (mfg_status & MAX34440_STATUS_OC_FAULT) 53*9d2ecfb7SGuenter Roeck ret |= PB_IOUT_OC_FAULT; 54*9d2ecfb7SGuenter Roeck break; 55*9d2ecfb7SGuenter Roeck case PMBUS_STATUS_TEMPERATURE: 56*9d2ecfb7SGuenter Roeck mfg_status = pmbus_read_word_data(client, 0, 57*9d2ecfb7SGuenter Roeck PMBUS_STATUS_MFR_SPECIFIC); 58*9d2ecfb7SGuenter Roeck if (mfg_status < 0) 59*9d2ecfb7SGuenter Roeck return mfg_status; 60*9d2ecfb7SGuenter Roeck if (mfg_status & MAX34440_STATUS_OT_WARN) 61*9d2ecfb7SGuenter Roeck ret |= PB_TEMP_OT_WARNING; 62*9d2ecfb7SGuenter Roeck if (mfg_status & MAX34440_STATUS_OT_FAULT) 63*9d2ecfb7SGuenter Roeck ret |= PB_TEMP_OT_FAULT; 64*9d2ecfb7SGuenter Roeck break; 65*9d2ecfb7SGuenter Roeck default: 66*9d2ecfb7SGuenter Roeck ret = -ENODATA; 67*9d2ecfb7SGuenter Roeck break; 68*9d2ecfb7SGuenter Roeck } 69*9d2ecfb7SGuenter Roeck return ret; 70*9d2ecfb7SGuenter Roeck } 71*9d2ecfb7SGuenter Roeck 72*9d2ecfb7SGuenter Roeck static struct pmbus_driver_info max34440_info[] = { 73*9d2ecfb7SGuenter Roeck [max34440] = { 74*9d2ecfb7SGuenter Roeck .pages = 14, 75*9d2ecfb7SGuenter Roeck .direct[PSC_VOLTAGE_IN] = true, 76*9d2ecfb7SGuenter Roeck .direct[PSC_VOLTAGE_OUT] = true, 77*9d2ecfb7SGuenter Roeck .direct[PSC_TEMPERATURE] = true, 78*9d2ecfb7SGuenter Roeck .direct[PSC_CURRENT_OUT] = true, 79*9d2ecfb7SGuenter Roeck .m[PSC_VOLTAGE_IN] = 1, 80*9d2ecfb7SGuenter Roeck .b[PSC_VOLTAGE_IN] = 0, 81*9d2ecfb7SGuenter Roeck .R[PSC_VOLTAGE_IN] = 3, /* R = 0 in datasheet reflects mV */ 82*9d2ecfb7SGuenter Roeck .m[PSC_VOLTAGE_OUT] = 1, 83*9d2ecfb7SGuenter Roeck .b[PSC_VOLTAGE_OUT] = 0, 84*9d2ecfb7SGuenter Roeck .R[PSC_VOLTAGE_OUT] = 3, /* R = 0 in datasheet reflects mV */ 85*9d2ecfb7SGuenter Roeck .m[PSC_CURRENT_OUT] = 1, 86*9d2ecfb7SGuenter Roeck .b[PSC_CURRENT_OUT] = 0, 87*9d2ecfb7SGuenter Roeck .R[PSC_CURRENT_OUT] = 3, /* R = 0 in datasheet reflects mA */ 88*9d2ecfb7SGuenter Roeck .m[PSC_TEMPERATURE] = 1, 89*9d2ecfb7SGuenter Roeck .b[PSC_TEMPERATURE] = 0, 90*9d2ecfb7SGuenter Roeck .R[PSC_TEMPERATURE] = 2, 91*9d2ecfb7SGuenter Roeck .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 92*9d2ecfb7SGuenter Roeck | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, 93*9d2ecfb7SGuenter Roeck .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 94*9d2ecfb7SGuenter Roeck | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, 95*9d2ecfb7SGuenter Roeck .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 96*9d2ecfb7SGuenter Roeck | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, 97*9d2ecfb7SGuenter Roeck .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 98*9d2ecfb7SGuenter Roeck | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, 99*9d2ecfb7SGuenter Roeck .func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 100*9d2ecfb7SGuenter Roeck | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, 101*9d2ecfb7SGuenter Roeck .func[5] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 102*9d2ecfb7SGuenter Roeck | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, 103*9d2ecfb7SGuenter Roeck .func[6] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, 104*9d2ecfb7SGuenter Roeck .func[7] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, 105*9d2ecfb7SGuenter Roeck .func[8] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, 106*9d2ecfb7SGuenter Roeck .func[9] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, 107*9d2ecfb7SGuenter Roeck .func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, 108*9d2ecfb7SGuenter Roeck .func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, 109*9d2ecfb7SGuenter Roeck .func[12] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, 110*9d2ecfb7SGuenter Roeck .func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, 111*9d2ecfb7SGuenter Roeck .read_byte_data = max34440_read_byte_data, 112*9d2ecfb7SGuenter Roeck }, 113*9d2ecfb7SGuenter Roeck [max34441] = { 114*9d2ecfb7SGuenter Roeck .pages = 12, 115*9d2ecfb7SGuenter Roeck .direct[PSC_VOLTAGE_IN] = true, 116*9d2ecfb7SGuenter Roeck .direct[PSC_VOLTAGE_OUT] = true, 117*9d2ecfb7SGuenter Roeck .direct[PSC_TEMPERATURE] = true, 118*9d2ecfb7SGuenter Roeck .direct[PSC_CURRENT_OUT] = true, 119*9d2ecfb7SGuenter Roeck .direct[PSC_FAN] = true, 120*9d2ecfb7SGuenter Roeck .m[PSC_VOLTAGE_IN] = 1, 121*9d2ecfb7SGuenter Roeck .b[PSC_VOLTAGE_IN] = 0, 122*9d2ecfb7SGuenter Roeck .R[PSC_VOLTAGE_IN] = 3, 123*9d2ecfb7SGuenter Roeck .m[PSC_VOLTAGE_OUT] = 1, 124*9d2ecfb7SGuenter Roeck .b[PSC_VOLTAGE_OUT] = 0, 125*9d2ecfb7SGuenter Roeck .R[PSC_VOLTAGE_OUT] = 3, 126*9d2ecfb7SGuenter Roeck .m[PSC_CURRENT_OUT] = 1, 127*9d2ecfb7SGuenter Roeck .b[PSC_CURRENT_OUT] = 0, 128*9d2ecfb7SGuenter Roeck .R[PSC_CURRENT_OUT] = 3, 129*9d2ecfb7SGuenter Roeck .m[PSC_TEMPERATURE] = 1, 130*9d2ecfb7SGuenter Roeck .b[PSC_TEMPERATURE] = 0, 131*9d2ecfb7SGuenter Roeck .R[PSC_TEMPERATURE] = 2, 132*9d2ecfb7SGuenter Roeck .m[PSC_FAN] = 1, 133*9d2ecfb7SGuenter Roeck .b[PSC_FAN] = 0, 134*9d2ecfb7SGuenter Roeck .R[PSC_FAN] = 0, 135*9d2ecfb7SGuenter Roeck .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 136*9d2ecfb7SGuenter Roeck | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, 137*9d2ecfb7SGuenter Roeck .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 138*9d2ecfb7SGuenter Roeck | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, 139*9d2ecfb7SGuenter Roeck .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 140*9d2ecfb7SGuenter Roeck | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, 141*9d2ecfb7SGuenter Roeck .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 142*9d2ecfb7SGuenter Roeck | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, 143*9d2ecfb7SGuenter Roeck .func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 144*9d2ecfb7SGuenter Roeck | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, 145*9d2ecfb7SGuenter Roeck .func[5] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12, 146*9d2ecfb7SGuenter Roeck .func[6] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, 147*9d2ecfb7SGuenter Roeck .func[7] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, 148*9d2ecfb7SGuenter Roeck .func[8] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, 149*9d2ecfb7SGuenter Roeck .func[9] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, 150*9d2ecfb7SGuenter Roeck .func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, 151*9d2ecfb7SGuenter Roeck .func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, 152*9d2ecfb7SGuenter Roeck .read_byte_data = max34440_read_byte_data, 153*9d2ecfb7SGuenter Roeck }, 154*9d2ecfb7SGuenter Roeck }; 155*9d2ecfb7SGuenter Roeck 156*9d2ecfb7SGuenter Roeck static int max34440_probe(struct i2c_client *client, 157*9d2ecfb7SGuenter Roeck const struct i2c_device_id *id) 158*9d2ecfb7SGuenter Roeck { 159*9d2ecfb7SGuenter Roeck return pmbus_do_probe(client, id, &max34440_info[id->driver_data]); 160*9d2ecfb7SGuenter Roeck } 161*9d2ecfb7SGuenter Roeck 162*9d2ecfb7SGuenter Roeck static int max34440_remove(struct i2c_client *client) 163*9d2ecfb7SGuenter Roeck { 164*9d2ecfb7SGuenter Roeck return pmbus_do_remove(client); 165*9d2ecfb7SGuenter Roeck } 166*9d2ecfb7SGuenter Roeck 167*9d2ecfb7SGuenter Roeck static const struct i2c_device_id max34440_id[] = { 168*9d2ecfb7SGuenter Roeck {"max34440", max34440}, 169*9d2ecfb7SGuenter Roeck {"max34441", max34441}, 170*9d2ecfb7SGuenter Roeck {} 171*9d2ecfb7SGuenter Roeck }; 172*9d2ecfb7SGuenter Roeck 173*9d2ecfb7SGuenter Roeck MODULE_DEVICE_TABLE(i2c, max34440_id); 174*9d2ecfb7SGuenter Roeck 175*9d2ecfb7SGuenter Roeck /* This is the driver that will be inserted */ 176*9d2ecfb7SGuenter Roeck static struct i2c_driver max34440_driver = { 177*9d2ecfb7SGuenter Roeck .driver = { 178*9d2ecfb7SGuenter Roeck .name = "max34440", 179*9d2ecfb7SGuenter Roeck }, 180*9d2ecfb7SGuenter Roeck .probe = max34440_probe, 181*9d2ecfb7SGuenter Roeck .remove = max34440_remove, 182*9d2ecfb7SGuenter Roeck .id_table = max34440_id, 183*9d2ecfb7SGuenter Roeck }; 184*9d2ecfb7SGuenter Roeck 185*9d2ecfb7SGuenter Roeck static int __init max34440_init(void) 186*9d2ecfb7SGuenter Roeck { 187*9d2ecfb7SGuenter Roeck return i2c_add_driver(&max34440_driver); 188*9d2ecfb7SGuenter Roeck } 189*9d2ecfb7SGuenter Roeck 190*9d2ecfb7SGuenter Roeck static void __exit max34440_exit(void) 191*9d2ecfb7SGuenter Roeck { 192*9d2ecfb7SGuenter Roeck i2c_del_driver(&max34440_driver); 193*9d2ecfb7SGuenter Roeck } 194*9d2ecfb7SGuenter Roeck 195*9d2ecfb7SGuenter Roeck MODULE_AUTHOR("Guenter Roeck"); 196*9d2ecfb7SGuenter Roeck MODULE_DESCRIPTION("PMBus driver for Maxim MAX34440/MAX34441"); 197*9d2ecfb7SGuenter Roeck MODULE_LICENSE("GPL"); 198*9d2ecfb7SGuenter Roeck module_init(max34440_init); 199*9d2ecfb7SGuenter Roeck module_exit(max34440_exit); 200