195b80c48SPatrick Rudolph // SPDX-License-Identifier: GPL-2.0+ 295b80c48SPatrick Rudolph /* 395b80c48SPatrick Rudolph * Hardware monitoring driver for Infineon TDA38640 495b80c48SPatrick Rudolph * 595b80c48SPatrick Rudolph * Copyright (c) 2023 9elements GmbH 695b80c48SPatrick Rudolph * 795b80c48SPatrick Rudolph */ 895b80c48SPatrick Rudolph 995b80c48SPatrick Rudolph #include <linux/err.h> 1095b80c48SPatrick Rudolph #include <linux/i2c.h> 1195b80c48SPatrick Rudolph #include <linux/init.h> 1295b80c48SPatrick Rudolph #include <linux/kernel.h> 1395b80c48SPatrick Rudolph #include <linux/module.h> 1495b80c48SPatrick Rudolph #include <linux/regulator/driver.h> 1595b80c48SPatrick Rudolph #include "pmbus.h" 1695b80c48SPatrick Rudolph 1795b80c48SPatrick Rudolph static const struct regulator_desc __maybe_unused tda38640_reg_desc[] = { 18*8be143b9SGuenter Roeck PMBUS_REGULATOR_ONE("vout"), 1995b80c48SPatrick Rudolph }; 2095b80c48SPatrick Rudolph 21923774d7SPatrick Rudolph struct tda38640_data { 22923774d7SPatrick Rudolph struct pmbus_driver_info info; 23923774d7SPatrick Rudolph u32 en_pin_lvl; 24923774d7SPatrick Rudolph }; 25923774d7SPatrick Rudolph 26923774d7SPatrick Rudolph #define to_tda38640_data(x) container_of(x, struct tda38640_data, info) 27923774d7SPatrick Rudolph 28923774d7SPatrick Rudolph /* 29923774d7SPatrick Rudolph * Map PB_ON_OFF_CONFIG_POLARITY_HIGH to PB_OPERATION_CONTROL_ON. 30923774d7SPatrick Rudolph */ 31923774d7SPatrick Rudolph static int tda38640_read_byte_data(struct i2c_client *client, int page, int reg) 32923774d7SPatrick Rudolph { 33923774d7SPatrick Rudolph const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 34923774d7SPatrick Rudolph struct tda38640_data *data = to_tda38640_data(info); 35923774d7SPatrick Rudolph int ret, on_off_config, enabled; 36923774d7SPatrick Rudolph 37923774d7SPatrick Rudolph if (reg != PMBUS_OPERATION) 38923774d7SPatrick Rudolph return -ENODATA; 39923774d7SPatrick Rudolph 40923774d7SPatrick Rudolph ret = pmbus_read_byte_data(client, page, reg); 41923774d7SPatrick Rudolph if (ret < 0) 42923774d7SPatrick Rudolph return ret; 43923774d7SPatrick Rudolph 44923774d7SPatrick Rudolph on_off_config = pmbus_read_byte_data(client, page, 45923774d7SPatrick Rudolph PMBUS_ON_OFF_CONFIG); 46923774d7SPatrick Rudolph if (on_off_config < 0) 47923774d7SPatrick Rudolph return on_off_config; 48923774d7SPatrick Rudolph 49923774d7SPatrick Rudolph enabled = !!(on_off_config & PB_ON_OFF_CONFIG_POLARITY_HIGH); 50923774d7SPatrick Rudolph 51923774d7SPatrick Rudolph enabled ^= data->en_pin_lvl; 52923774d7SPatrick Rudolph if (enabled) 53923774d7SPatrick Rudolph ret &= ~PB_OPERATION_CONTROL_ON; 54923774d7SPatrick Rudolph else 55923774d7SPatrick Rudolph ret |= PB_OPERATION_CONTROL_ON; 56923774d7SPatrick Rudolph 57923774d7SPatrick Rudolph return ret; 58923774d7SPatrick Rudolph } 59923774d7SPatrick Rudolph 60923774d7SPatrick Rudolph /* 61923774d7SPatrick Rudolph * Map PB_OPERATION_CONTROL_ON to PB_ON_OFF_CONFIG_POLARITY_HIGH. 62923774d7SPatrick Rudolph */ 63923774d7SPatrick Rudolph static int tda38640_write_byte_data(struct i2c_client *client, int page, 64923774d7SPatrick Rudolph int reg, u8 byte) 65923774d7SPatrick Rudolph { 66923774d7SPatrick Rudolph const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 67923774d7SPatrick Rudolph struct tda38640_data *data = to_tda38640_data(info); 68923774d7SPatrick Rudolph int enable, ret; 69923774d7SPatrick Rudolph 70923774d7SPatrick Rudolph if (reg != PMBUS_OPERATION) 71923774d7SPatrick Rudolph return -ENODATA; 72923774d7SPatrick Rudolph 73923774d7SPatrick Rudolph enable = !!(byte & PB_OPERATION_CONTROL_ON); 74923774d7SPatrick Rudolph 75923774d7SPatrick Rudolph byte &= ~PB_OPERATION_CONTROL_ON; 76923774d7SPatrick Rudolph ret = pmbus_write_byte_data(client, page, reg, byte); 77923774d7SPatrick Rudolph if (ret < 0) 78923774d7SPatrick Rudolph return ret; 79923774d7SPatrick Rudolph 80923774d7SPatrick Rudolph enable ^= data->en_pin_lvl; 81923774d7SPatrick Rudolph 82923774d7SPatrick Rudolph return pmbus_update_byte_data(client, page, PMBUS_ON_OFF_CONFIG, 83923774d7SPatrick Rudolph PB_ON_OFF_CONFIG_POLARITY_HIGH, 84923774d7SPatrick Rudolph enable ? 0 : PB_ON_OFF_CONFIG_POLARITY_HIGH); 85923774d7SPatrick Rudolph } 86923774d7SPatrick Rudolph 87923774d7SPatrick Rudolph static int svid_mode(struct i2c_client *client, struct tda38640_data *data) 88923774d7SPatrick Rudolph { 89923774d7SPatrick Rudolph /* PMBUS_MFR_READ(0xD0) + MTP Address offset */ 90923774d7SPatrick Rudolph u8 write_buf[] = {0xd0, 0x44, 0x00}; 91923774d7SPatrick Rudolph u8 read_buf[2]; 92923774d7SPatrick Rudolph int ret, svid; 93923774d7SPatrick Rudolph bool off, reg_en_pin_pol; 94923774d7SPatrick Rudolph 95923774d7SPatrick Rudolph struct i2c_msg msgs[2] = { 96923774d7SPatrick Rudolph { 97923774d7SPatrick Rudolph .addr = client->addr, 98923774d7SPatrick Rudolph .flags = 0, 99923774d7SPatrick Rudolph .buf = write_buf, 100923774d7SPatrick Rudolph .len = sizeof(write_buf), 101923774d7SPatrick Rudolph }, 102923774d7SPatrick Rudolph { 103923774d7SPatrick Rudolph .addr = client->addr, 104923774d7SPatrick Rudolph .flags = I2C_M_RD, 105923774d7SPatrick Rudolph .buf = read_buf, 106923774d7SPatrick Rudolph .len = sizeof(read_buf), 107923774d7SPatrick Rudolph } 108923774d7SPatrick Rudolph }; 109923774d7SPatrick Rudolph 110923774d7SPatrick Rudolph ret = i2c_transfer(client->adapter, msgs, 2); 111923774d7SPatrick Rudolph if (ret < 0) { 112923774d7SPatrick Rudolph dev_err(&client->dev, "i2c_transfer failed. %d", ret); 113923774d7SPatrick Rudolph return ret; 114923774d7SPatrick Rudolph } 115923774d7SPatrick Rudolph 116923774d7SPatrick Rudolph /* 117923774d7SPatrick Rudolph * 0x44[15] determines PMBus Operating Mode 118923774d7SPatrick Rudolph * If bit is set then it is SVID mode. 119923774d7SPatrick Rudolph */ 120923774d7SPatrick Rudolph svid = !!(read_buf[1] & BIT(7)); 121923774d7SPatrick Rudolph 122923774d7SPatrick Rudolph /* 123923774d7SPatrick Rudolph * Determine EN pin level for use in SVID mode. 124923774d7SPatrick Rudolph * This is done with help of STATUS_BYTE bit 6(OFF) & ON_OFF_CONFIG bit 2(EN pin polarity). 125923774d7SPatrick Rudolph */ 126923774d7SPatrick Rudolph if (svid) { 127923774d7SPatrick Rudolph ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE); 128923774d7SPatrick Rudolph if (ret < 0) 129923774d7SPatrick Rudolph return ret; 130923774d7SPatrick Rudolph off = !!(ret & PB_STATUS_OFF); 131923774d7SPatrick Rudolph 132923774d7SPatrick Rudolph ret = i2c_smbus_read_byte_data(client, PMBUS_ON_OFF_CONFIG); 133923774d7SPatrick Rudolph if (ret < 0) 134923774d7SPatrick Rudolph return ret; 135923774d7SPatrick Rudolph reg_en_pin_pol = !!(ret & PB_ON_OFF_CONFIG_POLARITY_HIGH); 136923774d7SPatrick Rudolph data->en_pin_lvl = off ^ reg_en_pin_pol; 137923774d7SPatrick Rudolph } 138923774d7SPatrick Rudolph 139923774d7SPatrick Rudolph return svid; 140923774d7SPatrick Rudolph } 141923774d7SPatrick Rudolph 14295b80c48SPatrick Rudolph static struct pmbus_driver_info tda38640_info = { 14395b80c48SPatrick Rudolph .pages = 1, 14495b80c48SPatrick Rudolph .format[PSC_VOLTAGE_IN] = linear, 14595b80c48SPatrick Rudolph .format[PSC_VOLTAGE_OUT] = linear, 14695b80c48SPatrick Rudolph .format[PSC_CURRENT_OUT] = linear, 14795b80c48SPatrick Rudolph .format[PSC_CURRENT_IN] = linear, 14895b80c48SPatrick Rudolph .format[PSC_POWER] = linear, 14995b80c48SPatrick Rudolph .format[PSC_TEMPERATURE] = linear, 15095b80c48SPatrick Rudolph .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT 15195b80c48SPatrick Rudolph | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP 15295b80c48SPatrick Rudolph | PMBUS_HAVE_IIN 15395b80c48SPatrick Rudolph | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 15495b80c48SPatrick Rudolph | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT 15595b80c48SPatrick Rudolph | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN, 15695b80c48SPatrick Rudolph #if IS_ENABLED(CONFIG_SENSORS_TDA38640_REGULATOR) 15795b80c48SPatrick Rudolph .num_regulators = 1, 15895b80c48SPatrick Rudolph .reg_desc = tda38640_reg_desc, 15995b80c48SPatrick Rudolph #endif 16095b80c48SPatrick Rudolph }; 16195b80c48SPatrick Rudolph 16295b80c48SPatrick Rudolph static int tda38640_probe(struct i2c_client *client) 16395b80c48SPatrick Rudolph { 164923774d7SPatrick Rudolph struct tda38640_data *data; 165923774d7SPatrick Rudolph int svid; 166923774d7SPatrick Rudolph 167923774d7SPatrick Rudolph data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 168923774d7SPatrick Rudolph if (!data) 169923774d7SPatrick Rudolph return -ENOMEM; 170923774d7SPatrick Rudolph memcpy(&data->info, &tda38640_info, sizeof(tda38640_info)); 171923774d7SPatrick Rudolph 172923774d7SPatrick Rudolph if (IS_ENABLED(CONFIG_SENSORS_TDA38640_REGULATOR) && 173923774d7SPatrick Rudolph of_property_read_bool(client->dev.of_node, "infineon,en-pin-fixed-level")) { 174923774d7SPatrick Rudolph svid = svid_mode(client, data); 175923774d7SPatrick Rudolph if (svid < 0) { 176923774d7SPatrick Rudolph dev_err_probe(&client->dev, svid, "Could not determine operating mode."); 177923774d7SPatrick Rudolph return svid; 178923774d7SPatrick Rudolph } 179923774d7SPatrick Rudolph 180923774d7SPatrick Rudolph /* 181923774d7SPatrick Rudolph * Apply ON_OFF_CONFIG workaround as enabling the regulator using the 182923774d7SPatrick Rudolph * OPERATION register doesn't work in SVID mode. 183923774d7SPatrick Rudolph * 184923774d7SPatrick Rudolph * One should configure PMBUS_ON_OFF_CONFIG here, but 185923774d7SPatrick Rudolph * PB_ON_OFF_CONFIG_POWERUP_CONTROL and PB_ON_OFF_CONFIG_EN_PIN_REQ 186923774d7SPatrick Rudolph * are ignored by the device. 187923774d7SPatrick Rudolph * Only PB_ON_OFF_CONFIG_POLARITY_HIGH has an effect. 188923774d7SPatrick Rudolph */ 189923774d7SPatrick Rudolph if (svid) { 190923774d7SPatrick Rudolph data->info.read_byte_data = tda38640_read_byte_data; 191923774d7SPatrick Rudolph data->info.write_byte_data = tda38640_write_byte_data; 192923774d7SPatrick Rudolph } 193923774d7SPatrick Rudolph } 194923774d7SPatrick Rudolph return pmbus_do_probe(client, &data->info); 19595b80c48SPatrick Rudolph } 19695b80c48SPatrick Rudolph 19795b80c48SPatrick Rudolph static const struct i2c_device_id tda38640_id[] = { 19895b80c48SPatrick Rudolph {"tda38640", 0}, 19995b80c48SPatrick Rudolph {} 20095b80c48SPatrick Rudolph }; 20195b80c48SPatrick Rudolph MODULE_DEVICE_TABLE(i2c, tda38640_id); 20295b80c48SPatrick Rudolph 20395b80c48SPatrick Rudolph static const struct of_device_id __maybe_unused tda38640_of_match[] = { 20495b80c48SPatrick Rudolph { .compatible = "infineon,tda38640"}, 20595b80c48SPatrick Rudolph { }, 20695b80c48SPatrick Rudolph }; 20795b80c48SPatrick Rudolph MODULE_DEVICE_TABLE(of, tda38640_of_match); 20895b80c48SPatrick Rudolph 20995b80c48SPatrick Rudolph /* This is the driver that will be inserted */ 21095b80c48SPatrick Rudolph static struct i2c_driver tda38640_driver = { 21195b80c48SPatrick Rudolph .driver = { 21295b80c48SPatrick Rudolph .name = "tda38640", 21395b80c48SPatrick Rudolph .of_match_table = of_match_ptr(tda38640_of_match), 21495b80c48SPatrick Rudolph }, 2151975d167SUwe Kleine-König .probe = tda38640_probe, 21695b80c48SPatrick Rudolph .id_table = tda38640_id, 21795b80c48SPatrick Rudolph }; 21895b80c48SPatrick Rudolph 21995b80c48SPatrick Rudolph module_i2c_driver(tda38640_driver); 22095b80c48SPatrick Rudolph 22195b80c48SPatrick Rudolph MODULE_AUTHOR("Patrick Rudolph <patrick.rudolph@9elements.com>"); 22295b80c48SPatrick Rudolph MODULE_DESCRIPTION("PMBus driver for Infineon TDA38640"); 22395b80c48SPatrick Rudolph MODULE_LICENSE("GPL"); 22495b80c48SPatrick Rudolph MODULE_IMPORT_NS(PMBUS); 225