103e9bd8dSGuenter Roeck /* 203e9bd8dSGuenter Roeck * Hardware monitoring driver for LM25066 / LM5064 / LM5066 303e9bd8dSGuenter Roeck * 403e9bd8dSGuenter Roeck * Copyright (c) 2011 Ericsson AB. 5*a7c69118SGuenter Roeck * Copyright (c) 2013 Guenter Roeck 603e9bd8dSGuenter Roeck * 703e9bd8dSGuenter Roeck * This program is free software; you can redistribute it and/or modify 803e9bd8dSGuenter Roeck * it under the terms of the GNU General Public License as published by 903e9bd8dSGuenter Roeck * the Free Software Foundation; either version 2 of the License, or 1003e9bd8dSGuenter Roeck * (at your option) any later version. 1103e9bd8dSGuenter Roeck * 1203e9bd8dSGuenter Roeck * This program is distributed in the hope that it will be useful, 1303e9bd8dSGuenter Roeck * but WITHOUT ANY WARRANTY; without even the implied warranty of 1403e9bd8dSGuenter Roeck * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1503e9bd8dSGuenter Roeck * GNU General Public License for more details. 1603e9bd8dSGuenter Roeck * 1703e9bd8dSGuenter Roeck * You should have received a copy of the GNU General Public License 1803e9bd8dSGuenter Roeck * along with this program; if not, write to the Free Software 1903e9bd8dSGuenter Roeck * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 2003e9bd8dSGuenter Roeck */ 2103e9bd8dSGuenter Roeck 2203e9bd8dSGuenter Roeck #include <linux/kernel.h> 2303e9bd8dSGuenter Roeck #include <linux/module.h> 2403e9bd8dSGuenter Roeck #include <linux/init.h> 2503e9bd8dSGuenter Roeck #include <linux/err.h> 2603e9bd8dSGuenter Roeck #include <linux/slab.h> 2703e9bd8dSGuenter Roeck #include <linux/i2c.h> 2803e9bd8dSGuenter Roeck #include "pmbus.h" 2903e9bd8dSGuenter Roeck 3003e9bd8dSGuenter Roeck enum chips { lm25066, lm5064, lm5066 }; 3103e9bd8dSGuenter Roeck 3203e9bd8dSGuenter Roeck #define LM25066_READ_VAUX 0xd0 3303e9bd8dSGuenter Roeck #define LM25066_MFR_READ_IIN 0xd1 3403e9bd8dSGuenter Roeck #define LM25066_MFR_READ_PIN 0xd2 3503e9bd8dSGuenter Roeck #define LM25066_MFR_IIN_OC_WARN_LIMIT 0xd3 3603e9bd8dSGuenter Roeck #define LM25066_MFR_PIN_OP_WARN_LIMIT 0xd4 3703e9bd8dSGuenter Roeck #define LM25066_READ_PIN_PEAK 0xd5 3803e9bd8dSGuenter Roeck #define LM25066_CLEAR_PIN_PEAK 0xd6 3903e9bd8dSGuenter Roeck #define LM25066_DEVICE_SETUP 0xd9 4003e9bd8dSGuenter Roeck #define LM25066_READ_AVG_VIN 0xdc 4103e9bd8dSGuenter Roeck #define LM25066_READ_AVG_VOUT 0xdd 4203e9bd8dSGuenter Roeck #define LM25066_READ_AVG_IIN 0xde 4303e9bd8dSGuenter Roeck #define LM25066_READ_AVG_PIN 0xdf 4403e9bd8dSGuenter Roeck 4503e9bd8dSGuenter Roeck #define LM25066_DEV_SETUP_CL (1 << 4) /* Current limit */ 4603e9bd8dSGuenter Roeck 4703e9bd8dSGuenter Roeck struct lm25066_data { 4803e9bd8dSGuenter Roeck int id; 4903e9bd8dSGuenter Roeck struct pmbus_driver_info info; 5003e9bd8dSGuenter Roeck }; 5103e9bd8dSGuenter Roeck 5203e9bd8dSGuenter Roeck #define to_lm25066_data(x) container_of(x, struct lm25066_data, info) 5303e9bd8dSGuenter Roeck 5403e9bd8dSGuenter Roeck static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) 5503e9bd8dSGuenter Roeck { 5603e9bd8dSGuenter Roeck const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 5703e9bd8dSGuenter Roeck const struct lm25066_data *data = to_lm25066_data(info); 5803e9bd8dSGuenter Roeck int ret; 5903e9bd8dSGuenter Roeck 6003e9bd8dSGuenter Roeck switch (reg) { 61*a7c69118SGuenter Roeck case PMBUS_VIRT_READ_VMON: 62*a7c69118SGuenter Roeck ret = pmbus_read_word_data(client, 0, LM25066_READ_VAUX); 6303e9bd8dSGuenter Roeck if (ret < 0) 6403e9bd8dSGuenter Roeck break; 65*a7c69118SGuenter Roeck /* Adjust returned value to match VIN coefficients */ 6603e9bd8dSGuenter Roeck switch (data->id) { 6703e9bd8dSGuenter Roeck case lm25066: 68*a7c69118SGuenter Roeck /* VIN: 4.54 mV VAUX: 283.2 uV LSB */ 6903e9bd8dSGuenter Roeck ret = DIV_ROUND_CLOSEST(ret * 2832, 45400); 7003e9bd8dSGuenter Roeck break; 7103e9bd8dSGuenter Roeck case lm5064: 72*a7c69118SGuenter Roeck /* VIN: 4.53 mV VAUX: 700 uV LSB */ 7303e9bd8dSGuenter Roeck ret = DIV_ROUND_CLOSEST(ret * 70, 453); 7403e9bd8dSGuenter Roeck break; 7503e9bd8dSGuenter Roeck case lm5066: 76*a7c69118SGuenter Roeck /* VIN: 2.18 mV VAUX: 725 uV LSB */ 7703e9bd8dSGuenter Roeck ret = DIV_ROUND_CLOSEST(ret * 725, 2180); 7803e9bd8dSGuenter Roeck break; 7903e9bd8dSGuenter Roeck } 8003e9bd8dSGuenter Roeck break; 8103e9bd8dSGuenter Roeck case PMBUS_READ_IIN: 8203e9bd8dSGuenter Roeck ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN); 8303e9bd8dSGuenter Roeck break; 8403e9bd8dSGuenter Roeck case PMBUS_READ_PIN: 8503e9bd8dSGuenter Roeck ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_PIN); 8603e9bd8dSGuenter Roeck break; 8703e9bd8dSGuenter Roeck case PMBUS_IIN_OC_WARN_LIMIT: 8803e9bd8dSGuenter Roeck ret = pmbus_read_word_data(client, 0, 8903e9bd8dSGuenter Roeck LM25066_MFR_IIN_OC_WARN_LIMIT); 9003e9bd8dSGuenter Roeck break; 9103e9bd8dSGuenter Roeck case PMBUS_PIN_OP_WARN_LIMIT: 9203e9bd8dSGuenter Roeck ret = pmbus_read_word_data(client, 0, 9303e9bd8dSGuenter Roeck LM25066_MFR_PIN_OP_WARN_LIMIT); 9403e9bd8dSGuenter Roeck break; 9503e9bd8dSGuenter Roeck case PMBUS_VIRT_READ_VIN_AVG: 9603e9bd8dSGuenter Roeck ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VIN); 9703e9bd8dSGuenter Roeck break; 9803e9bd8dSGuenter Roeck case PMBUS_VIRT_READ_VOUT_AVG: 9903e9bd8dSGuenter Roeck ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VOUT); 10003e9bd8dSGuenter Roeck break; 10103e9bd8dSGuenter Roeck case PMBUS_VIRT_READ_IIN_AVG: 10203e9bd8dSGuenter Roeck ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_IIN); 10303e9bd8dSGuenter Roeck break; 10403e9bd8dSGuenter Roeck case PMBUS_VIRT_READ_PIN_AVG: 10503e9bd8dSGuenter Roeck ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_PIN); 10603e9bd8dSGuenter Roeck break; 10703e9bd8dSGuenter Roeck case PMBUS_VIRT_READ_PIN_MAX: 10803e9bd8dSGuenter Roeck ret = pmbus_read_word_data(client, 0, LM25066_READ_PIN_PEAK); 10903e9bd8dSGuenter Roeck break; 11003e9bd8dSGuenter Roeck case PMBUS_VIRT_RESET_PIN_HISTORY: 11103e9bd8dSGuenter Roeck ret = 0; 11203e9bd8dSGuenter Roeck break; 11303e9bd8dSGuenter Roeck default: 11403e9bd8dSGuenter Roeck ret = -ENODATA; 11503e9bd8dSGuenter Roeck break; 11603e9bd8dSGuenter Roeck } 11703e9bd8dSGuenter Roeck return ret; 11803e9bd8dSGuenter Roeck } 11903e9bd8dSGuenter Roeck 12003e9bd8dSGuenter Roeck static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, 12103e9bd8dSGuenter Roeck u16 word) 12203e9bd8dSGuenter Roeck { 12303e9bd8dSGuenter Roeck int ret; 12403e9bd8dSGuenter Roeck 12503e9bd8dSGuenter Roeck switch (reg) { 12603e9bd8dSGuenter Roeck case PMBUS_IIN_OC_WARN_LIMIT: 12703e9bd8dSGuenter Roeck ret = pmbus_write_word_data(client, 0, 12803e9bd8dSGuenter Roeck LM25066_MFR_IIN_OC_WARN_LIMIT, 12903e9bd8dSGuenter Roeck word); 13003e9bd8dSGuenter Roeck break; 13103e9bd8dSGuenter Roeck case PMBUS_PIN_OP_WARN_LIMIT: 13203e9bd8dSGuenter Roeck ret = pmbus_write_word_data(client, 0, 13303e9bd8dSGuenter Roeck LM25066_MFR_PIN_OP_WARN_LIMIT, 13403e9bd8dSGuenter Roeck word); 13503e9bd8dSGuenter Roeck break; 13603e9bd8dSGuenter Roeck case PMBUS_VIRT_RESET_PIN_HISTORY: 13703e9bd8dSGuenter Roeck ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK); 13803e9bd8dSGuenter Roeck break; 13903e9bd8dSGuenter Roeck default: 14003e9bd8dSGuenter Roeck ret = -ENODATA; 14103e9bd8dSGuenter Roeck break; 14203e9bd8dSGuenter Roeck } 14303e9bd8dSGuenter Roeck return ret; 14403e9bd8dSGuenter Roeck } 14503e9bd8dSGuenter Roeck 14603e9bd8dSGuenter Roeck static int lm25066_probe(struct i2c_client *client, 14703e9bd8dSGuenter Roeck const struct i2c_device_id *id) 14803e9bd8dSGuenter Roeck { 14903e9bd8dSGuenter Roeck int config; 15003e9bd8dSGuenter Roeck struct lm25066_data *data; 15103e9bd8dSGuenter Roeck struct pmbus_driver_info *info; 15203e9bd8dSGuenter Roeck 15303e9bd8dSGuenter Roeck if (!i2c_check_functionality(client->adapter, 15403e9bd8dSGuenter Roeck I2C_FUNC_SMBUS_READ_BYTE_DATA)) 15503e9bd8dSGuenter Roeck return -ENODEV; 15603e9bd8dSGuenter Roeck 1578b313ca7SGuenter Roeck data = devm_kzalloc(&client->dev, sizeof(struct lm25066_data), 1588b313ca7SGuenter Roeck GFP_KERNEL); 15903e9bd8dSGuenter Roeck if (!data) 16003e9bd8dSGuenter Roeck return -ENOMEM; 16103e9bd8dSGuenter Roeck 16203e9bd8dSGuenter Roeck config = i2c_smbus_read_byte_data(client, LM25066_DEVICE_SETUP); 1638b313ca7SGuenter Roeck if (config < 0) 1648b313ca7SGuenter Roeck return config; 16503e9bd8dSGuenter Roeck 16603e9bd8dSGuenter Roeck data->id = id->driver_data; 16703e9bd8dSGuenter Roeck info = &data->info; 16803e9bd8dSGuenter Roeck 169*a7c69118SGuenter Roeck info->pages = 1; 17003e9bd8dSGuenter Roeck info->format[PSC_VOLTAGE_IN] = direct; 17103e9bd8dSGuenter Roeck info->format[PSC_VOLTAGE_OUT] = direct; 17203e9bd8dSGuenter Roeck info->format[PSC_CURRENT_IN] = direct; 17303e9bd8dSGuenter Roeck info->format[PSC_TEMPERATURE] = direct; 17403e9bd8dSGuenter Roeck info->format[PSC_POWER] = direct; 17503e9bd8dSGuenter Roeck 17603e9bd8dSGuenter Roeck info->m[PSC_TEMPERATURE] = 16; 17703e9bd8dSGuenter Roeck info->b[PSC_TEMPERATURE] = 0; 17803e9bd8dSGuenter Roeck info->R[PSC_TEMPERATURE] = 0; 17903e9bd8dSGuenter Roeck 180*a7c69118SGuenter Roeck info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VMON | PMBUS_HAVE_VOUT 18103e9bd8dSGuenter Roeck | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN 18203e9bd8dSGuenter Roeck | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; 18303e9bd8dSGuenter Roeck 18403e9bd8dSGuenter Roeck info->read_word_data = lm25066_read_word_data; 18503e9bd8dSGuenter Roeck info->write_word_data = lm25066_write_word_data; 18603e9bd8dSGuenter Roeck 18703e9bd8dSGuenter Roeck switch (id->driver_data) { 18803e9bd8dSGuenter Roeck case lm25066: 18903e9bd8dSGuenter Roeck info->m[PSC_VOLTAGE_IN] = 22070; 19003e9bd8dSGuenter Roeck info->b[PSC_VOLTAGE_IN] = 0; 19103e9bd8dSGuenter Roeck info->R[PSC_VOLTAGE_IN] = -2; 19203e9bd8dSGuenter Roeck info->m[PSC_VOLTAGE_OUT] = 22070; 19303e9bd8dSGuenter Roeck info->b[PSC_VOLTAGE_OUT] = 0; 19403e9bd8dSGuenter Roeck info->R[PSC_VOLTAGE_OUT] = -2; 19503e9bd8dSGuenter Roeck 19603e9bd8dSGuenter Roeck if (config & LM25066_DEV_SETUP_CL) { 19703e9bd8dSGuenter Roeck info->m[PSC_CURRENT_IN] = 6852; 19803e9bd8dSGuenter Roeck info->b[PSC_CURRENT_IN] = 0; 19903e9bd8dSGuenter Roeck info->R[PSC_CURRENT_IN] = -2; 20003e9bd8dSGuenter Roeck info->m[PSC_POWER] = 369; 20103e9bd8dSGuenter Roeck info->b[PSC_POWER] = 0; 20203e9bd8dSGuenter Roeck info->R[PSC_POWER] = -2; 20303e9bd8dSGuenter Roeck } else { 20403e9bd8dSGuenter Roeck info->m[PSC_CURRENT_IN] = 13661; 20503e9bd8dSGuenter Roeck info->b[PSC_CURRENT_IN] = 0; 20603e9bd8dSGuenter Roeck info->R[PSC_CURRENT_IN] = -2; 20703e9bd8dSGuenter Roeck info->m[PSC_POWER] = 736; 20803e9bd8dSGuenter Roeck info->b[PSC_POWER] = 0; 20903e9bd8dSGuenter Roeck info->R[PSC_POWER] = -2; 21003e9bd8dSGuenter Roeck } 21103e9bd8dSGuenter Roeck break; 21203e9bd8dSGuenter Roeck case lm5064: 21303e9bd8dSGuenter Roeck info->m[PSC_VOLTAGE_IN] = 22075; 21403e9bd8dSGuenter Roeck info->b[PSC_VOLTAGE_IN] = 0; 21503e9bd8dSGuenter Roeck info->R[PSC_VOLTAGE_IN] = -2; 21603e9bd8dSGuenter Roeck info->m[PSC_VOLTAGE_OUT] = 22075; 21703e9bd8dSGuenter Roeck info->b[PSC_VOLTAGE_OUT] = 0; 21803e9bd8dSGuenter Roeck info->R[PSC_VOLTAGE_OUT] = -2; 21903e9bd8dSGuenter Roeck 22003e9bd8dSGuenter Roeck if (config & LM25066_DEV_SETUP_CL) { 22103e9bd8dSGuenter Roeck info->m[PSC_CURRENT_IN] = 6713; 22203e9bd8dSGuenter Roeck info->b[PSC_CURRENT_IN] = 0; 22303e9bd8dSGuenter Roeck info->R[PSC_CURRENT_IN] = -2; 22403e9bd8dSGuenter Roeck info->m[PSC_POWER] = 3619; 22503e9bd8dSGuenter Roeck info->b[PSC_POWER] = 0; 22603e9bd8dSGuenter Roeck info->R[PSC_POWER] = -3; 22703e9bd8dSGuenter Roeck } else { 22803e9bd8dSGuenter Roeck info->m[PSC_CURRENT_IN] = 13426; 22903e9bd8dSGuenter Roeck info->b[PSC_CURRENT_IN] = 0; 23003e9bd8dSGuenter Roeck info->R[PSC_CURRENT_IN] = -2; 23103e9bd8dSGuenter Roeck info->m[PSC_POWER] = 7238; 23203e9bd8dSGuenter Roeck info->b[PSC_POWER] = 0; 23303e9bd8dSGuenter Roeck info->R[PSC_POWER] = -3; 23403e9bd8dSGuenter Roeck } 23503e9bd8dSGuenter Roeck break; 23603e9bd8dSGuenter Roeck case lm5066: 23703e9bd8dSGuenter Roeck info->m[PSC_VOLTAGE_IN] = 4587; 23803e9bd8dSGuenter Roeck info->b[PSC_VOLTAGE_IN] = 0; 23903e9bd8dSGuenter Roeck info->R[PSC_VOLTAGE_IN] = -2; 24003e9bd8dSGuenter Roeck info->m[PSC_VOLTAGE_OUT] = 4587; 24103e9bd8dSGuenter Roeck info->b[PSC_VOLTAGE_OUT] = 0; 24203e9bd8dSGuenter Roeck info->R[PSC_VOLTAGE_OUT] = -2; 24303e9bd8dSGuenter Roeck 24403e9bd8dSGuenter Roeck if (config & LM25066_DEV_SETUP_CL) { 24503e9bd8dSGuenter Roeck info->m[PSC_CURRENT_IN] = 10753; 24603e9bd8dSGuenter Roeck info->b[PSC_CURRENT_IN] = 0; 24703e9bd8dSGuenter Roeck info->R[PSC_CURRENT_IN] = -2; 24803e9bd8dSGuenter Roeck info->m[PSC_POWER] = 1204; 24903e9bd8dSGuenter Roeck info->b[PSC_POWER] = 0; 25003e9bd8dSGuenter Roeck info->R[PSC_POWER] = -3; 25103e9bd8dSGuenter Roeck } else { 25203e9bd8dSGuenter Roeck info->m[PSC_CURRENT_IN] = 5405; 25303e9bd8dSGuenter Roeck info->b[PSC_CURRENT_IN] = 0; 25403e9bd8dSGuenter Roeck info->R[PSC_CURRENT_IN] = -2; 25503e9bd8dSGuenter Roeck info->m[PSC_POWER] = 605; 25603e9bd8dSGuenter Roeck info->b[PSC_POWER] = 0; 25703e9bd8dSGuenter Roeck info->R[PSC_POWER] = -3; 25803e9bd8dSGuenter Roeck } 25903e9bd8dSGuenter Roeck break; 26003e9bd8dSGuenter Roeck default: 2618b313ca7SGuenter Roeck return -ENODEV; 26203e9bd8dSGuenter Roeck } 26303e9bd8dSGuenter Roeck 2648b313ca7SGuenter Roeck return pmbus_do_probe(client, id, info); 26503e9bd8dSGuenter Roeck } 26603e9bd8dSGuenter Roeck 26703e9bd8dSGuenter Roeck static const struct i2c_device_id lm25066_id[] = { 26803e9bd8dSGuenter Roeck {"lm25066", lm25066}, 26903e9bd8dSGuenter Roeck {"lm5064", lm5064}, 27003e9bd8dSGuenter Roeck {"lm5066", lm5066}, 27103e9bd8dSGuenter Roeck { } 27203e9bd8dSGuenter Roeck }; 27303e9bd8dSGuenter Roeck 27403e9bd8dSGuenter Roeck MODULE_DEVICE_TABLE(i2c, lm25066_id); 27503e9bd8dSGuenter Roeck 27603e9bd8dSGuenter Roeck /* This is the driver that will be inserted */ 27703e9bd8dSGuenter Roeck static struct i2c_driver lm25066_driver = { 27803e9bd8dSGuenter Roeck .driver = { 27903e9bd8dSGuenter Roeck .name = "lm25066", 28003e9bd8dSGuenter Roeck }, 28103e9bd8dSGuenter Roeck .probe = lm25066_probe, 282dd285ad7SGuenter Roeck .remove = pmbus_do_remove, 28303e9bd8dSGuenter Roeck .id_table = lm25066_id, 28403e9bd8dSGuenter Roeck }; 28503e9bd8dSGuenter Roeck 286f0967eeaSAxel Lin module_i2c_driver(lm25066_driver); 28703e9bd8dSGuenter Roeck 28803e9bd8dSGuenter Roeck MODULE_AUTHOR("Guenter Roeck"); 28903e9bd8dSGuenter Roeck MODULE_DESCRIPTION("PMBus driver for LM25066/LM5064/LM5066"); 29003e9bd8dSGuenter Roeck MODULE_LICENSE("GPL"); 291