152 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Hardware monitoring driver for Infineon PXE16104 *5 * Copyright (c) 2019 Facebook Inc6 *7 */8 9#include <linux/err.h>10#include <linux/i2c.h>11#include <linux/init.h>12#include <linux/kernel.h>13#include <linux/module.h>14#include "pmbus.h"15 16#define PXE1610_NUM_PAGES 317 18/* Identify chip parameters. */19static int pxe1610_identify(struct i2c_client *client,20 struct pmbus_driver_info *info)21{22 int i;23 24 for (i = 0; i < PXE1610_NUM_PAGES; i++) {25 if (pmbus_check_byte_register(client, i, PMBUS_VOUT_MODE)) {26 u8 vout_mode;27 int ret;28 29 /* Read the register with VOUT scaling value.*/30 ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE);31 if (ret < 0)32 return ret;33 34 vout_mode = ret & GENMASK(4, 0);35 36 switch (vout_mode) {37 case 1:38 info->vrm_version[i] = vr12;39 break;40 case 2:41 info->vrm_version[i] = vr13;42 break;43 default:44 /*45 * If prior pages are available limit operation46 * to them47 */48 if (i != 0) {49 info->pages = i;50 return 0;51 }52 53 return -ENODEV;54 }55 }56 }57 58 return 0;59}60 61static struct pmbus_driver_info pxe1610_info = {62 .pages = PXE1610_NUM_PAGES,63 .format[PSC_VOLTAGE_IN] = linear,64 .format[PSC_VOLTAGE_OUT] = vid,65 .format[PSC_CURRENT_IN] = linear,66 .format[PSC_CURRENT_OUT] = linear,67 .format[PSC_TEMPERATURE] = linear,68 .format[PSC_POWER] = linear,69 .func[0] = PMBUS_HAVE_VIN70 | PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN71 | PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN72 | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP73 | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT74 | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,75 .func[1] = PMBUS_HAVE_VIN76 | PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN77 | PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN78 | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP79 | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT80 | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,81 .func[2] = PMBUS_HAVE_VIN82 | PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN83 | PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN84 | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP85 | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT86 | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,87 .identify = pxe1610_identify,88};89 90static int pxe1610_probe(struct i2c_client *client)91{92 struct pmbus_driver_info *info;93 u8 buf[I2C_SMBUS_BLOCK_MAX];94 int ret;95 96 if (!i2c_check_functionality(97 client->adapter,98 I2C_FUNC_SMBUS_READ_BYTE_DATA99 | I2C_FUNC_SMBUS_READ_WORD_DATA100 | I2C_FUNC_SMBUS_READ_BLOCK_DATA))101 return -ENODEV;102 103 /*104 * By default this device doesn't boot to page 0, so set page 0105 * to access all pmbus registers.106 */107 i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);108 109 /* Read Manufacturer id */110 ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);111 if (ret < 0) {112 dev_err(&client->dev, "Failed to read PMBUS_MFR_ID\n");113 return ret;114 }115 if (ret != 2 || strncmp(buf, "XP", 2)) {116 dev_err(&client->dev, "MFR_ID unrecognized\n");117 return -ENODEV;118 }119 120 info = devm_kmemdup(&client->dev, &pxe1610_info,121 sizeof(struct pmbus_driver_info),122 GFP_KERNEL);123 if (!info)124 return -ENOMEM;125 126 return pmbus_do_probe(client, info);127}128 129static const struct i2c_device_id pxe1610_id[] = {130 {"pxe1610"},131 {"pxe1110"},132 {"pxm1310"},133 {}134};135 136MODULE_DEVICE_TABLE(i2c, pxe1610_id);137 138static struct i2c_driver pxe1610_driver = {139 .driver = {140 .name = "pxe1610",141 },142 .probe = pxe1610_probe,143 .id_table = pxe1610_id,144};145 146module_i2c_driver(pxe1610_driver);147 148MODULE_AUTHOR("Vijay Khemka <vijaykhemka@fb.com>");149MODULE_DESCRIPTION("PMBus driver for Infineon PXE1610, PXE1110 and PXM1310");150MODULE_LICENSE("GPL");151MODULE_IMPORT_NS(PMBUS);152