225 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Hardware monitoring driver for Infineon TDA386404 *5 * Copyright (c) 2023 9elements GmbH6 *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 <linux/regulator/driver.h>15#include "pmbus.h"16 17static const struct regulator_desc __maybe_unused tda38640_reg_desc[] = {18 PMBUS_REGULATOR_ONE("vout"),19};20 21struct tda38640_data {22 struct pmbus_driver_info info;23 u32 en_pin_lvl;24};25 26#define to_tda38640_data(x) container_of(x, struct tda38640_data, info)27 28/*29 * Map PB_ON_OFF_CONFIG_POLARITY_HIGH to PB_OPERATION_CONTROL_ON.30 */31static int tda38640_read_byte_data(struct i2c_client *client, int page, int reg)32{33 const struct pmbus_driver_info *info = pmbus_get_driver_info(client);34 struct tda38640_data *data = to_tda38640_data(info);35 int ret, on_off_config, enabled;36 37 if (reg != PMBUS_OPERATION)38 return -ENODATA;39 40 ret = pmbus_read_byte_data(client, page, reg);41 if (ret < 0)42 return ret;43 44 on_off_config = pmbus_read_byte_data(client, page,45 PMBUS_ON_OFF_CONFIG);46 if (on_off_config < 0)47 return on_off_config;48 49 enabled = !!(on_off_config & PB_ON_OFF_CONFIG_POLARITY_HIGH);50 51 enabled ^= data->en_pin_lvl;52 if (enabled)53 ret &= ~PB_OPERATION_CONTROL_ON;54 else55 ret |= PB_OPERATION_CONTROL_ON;56 57 return ret;58}59 60/*61 * Map PB_OPERATION_CONTROL_ON to PB_ON_OFF_CONFIG_POLARITY_HIGH.62 */63static int tda38640_write_byte_data(struct i2c_client *client, int page,64 int reg, u8 byte)65{66 const struct pmbus_driver_info *info = pmbus_get_driver_info(client);67 struct tda38640_data *data = to_tda38640_data(info);68 int enable, ret;69 70 if (reg != PMBUS_OPERATION)71 return -ENODATA;72 73 enable = !!(byte & PB_OPERATION_CONTROL_ON);74 75 byte &= ~PB_OPERATION_CONTROL_ON;76 ret = pmbus_write_byte_data(client, page, reg, byte);77 if (ret < 0)78 return ret;79 80 enable ^= data->en_pin_lvl;81 82 return pmbus_update_byte_data(client, page, PMBUS_ON_OFF_CONFIG,83 PB_ON_OFF_CONFIG_POLARITY_HIGH,84 enable ? 0 : PB_ON_OFF_CONFIG_POLARITY_HIGH);85}86 87static int svid_mode(struct i2c_client *client, struct tda38640_data *data)88{89 /* PMBUS_MFR_READ(0xD0) + MTP Address offset */90 u8 write_buf[] = {0xd0, 0x44, 0x00};91 u8 read_buf[2];92 int ret, svid;93 bool off, reg_en_pin_pol;94 95 struct i2c_msg msgs[2] = {96 {97 .addr = client->addr,98 .flags = 0,99 .buf = write_buf,100 .len = sizeof(write_buf),101 },102 {103 .addr = client->addr,104 .flags = I2C_M_RD,105 .buf = read_buf,106 .len = sizeof(read_buf),107 }108 };109 110 ret = i2c_transfer(client->adapter, msgs, 2);111 if (ret < 0) {112 dev_err(&client->dev, "i2c_transfer failed. %d", ret);113 return ret;114 }115 116 /*117 * 0x44[15] determines PMBus Operating Mode118 * If bit is set then it is SVID mode.119 */120 svid = !!(read_buf[1] & BIT(7));121 122 /*123 * Determine EN pin level for use in SVID mode.124 * This is done with help of STATUS_BYTE bit 6(OFF) & ON_OFF_CONFIG bit 2(EN pin polarity).125 */126 if (svid) {127 ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE);128 if (ret < 0)129 return ret;130 off = !!(ret & PB_STATUS_OFF);131 132 ret = i2c_smbus_read_byte_data(client, PMBUS_ON_OFF_CONFIG);133 if (ret < 0)134 return ret;135 reg_en_pin_pol = !!(ret & PB_ON_OFF_CONFIG_POLARITY_HIGH);136 data->en_pin_lvl = off ^ reg_en_pin_pol;137 }138 139 return svid;140}141 142static struct pmbus_driver_info tda38640_info = {143 .pages = 1,144 .format[PSC_VOLTAGE_IN] = linear,145 .format[PSC_VOLTAGE_OUT] = linear,146 .format[PSC_CURRENT_OUT] = linear,147 .format[PSC_CURRENT_IN] = linear,148 .format[PSC_POWER] = linear,149 .format[PSC_TEMPERATURE] = linear,150 .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT151 | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP152 | PMBUS_HAVE_IIN153 | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT154 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT155 | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN,156#if IS_ENABLED(CONFIG_SENSORS_TDA38640_REGULATOR)157 .num_regulators = 1,158 .reg_desc = tda38640_reg_desc,159#endif160};161 162static int tda38640_probe(struct i2c_client *client)163{164 struct tda38640_data *data;165 int svid;166 167 data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);168 if (!data)169 return -ENOMEM;170 memcpy(&data->info, &tda38640_info, sizeof(tda38640_info));171 172 if (IS_ENABLED(CONFIG_SENSORS_TDA38640_REGULATOR) &&173 of_property_read_bool(client->dev.of_node, "infineon,en-pin-fixed-level")) {174 svid = svid_mode(client, data);175 if (svid < 0) {176 dev_err_probe(&client->dev, svid, "Could not determine operating mode.");177 return svid;178 }179 180 /*181 * Apply ON_OFF_CONFIG workaround as enabling the regulator using the182 * OPERATION register doesn't work in SVID mode.183 *184 * One should configure PMBUS_ON_OFF_CONFIG here, but185 * PB_ON_OFF_CONFIG_POWERUP_CONTROL and PB_ON_OFF_CONFIG_EN_PIN_REQ186 * are ignored by the device.187 * Only PB_ON_OFF_CONFIG_POLARITY_HIGH has an effect.188 */189 if (svid) {190 data->info.read_byte_data = tda38640_read_byte_data;191 data->info.write_byte_data = tda38640_write_byte_data;192 }193 }194 return pmbus_do_probe(client, &data->info);195}196 197static const struct i2c_device_id tda38640_id[] = {198 {"tda38640"},199 {}200};201MODULE_DEVICE_TABLE(i2c, tda38640_id);202 203static const struct of_device_id __maybe_unused tda38640_of_match[] = {204 { .compatible = "infineon,tda38640"},205 { },206};207MODULE_DEVICE_TABLE(of, tda38640_of_match);208 209/* This is the driver that will be inserted */210static struct i2c_driver tda38640_driver = {211 .driver = {212 .name = "tda38640",213 .of_match_table = of_match_ptr(tda38640_of_match),214 },215 .probe = tda38640_probe,216 .id_table = tda38640_id,217};218 219module_i2c_driver(tda38640_driver);220 221MODULE_AUTHOR("Patrick Rudolph <patrick.rudolph@9elements.com>");222MODULE_DESCRIPTION("PMBus driver for Infineon TDA38640");223MODULE_LICENSE("GPL");224MODULE_IMPORT_NS(PMBUS);225