162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Hardware monitoring driver for UCD90xxx Sequencer and System Health 462306a36Sopenharmony_ci * Controller series 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2011 Ericsson AB. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/debugfs.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/i2c.h> 1862306a36Sopenharmony_ci#include <linux/pmbus.h> 1962306a36Sopenharmony_ci#include <linux/gpio/driver.h> 2062306a36Sopenharmony_ci#include <linux/timekeeping.h> 2162306a36Sopenharmony_ci#include "pmbus.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cienum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd90320, ucd9090, 2462306a36Sopenharmony_ci ucd90910 }; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define UCD9000_MONITOR_CONFIG 0xd5 2762306a36Sopenharmony_ci#define UCD9000_NUM_PAGES 0xd6 2862306a36Sopenharmony_ci#define UCD9000_FAN_CONFIG_INDEX 0xe7 2962306a36Sopenharmony_ci#define UCD9000_FAN_CONFIG 0xe8 3062306a36Sopenharmony_ci#define UCD9000_MFR_STATUS 0xf3 3162306a36Sopenharmony_ci#define UCD9000_GPIO_SELECT 0xfa 3262306a36Sopenharmony_ci#define UCD9000_GPIO_CONFIG 0xfb 3362306a36Sopenharmony_ci#define UCD9000_DEVICE_ID 0xfd 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* GPIO CONFIG bits */ 3662306a36Sopenharmony_ci#define UCD9000_GPIO_CONFIG_ENABLE BIT(0) 3762306a36Sopenharmony_ci#define UCD9000_GPIO_CONFIG_OUT_ENABLE BIT(1) 3862306a36Sopenharmony_ci#define UCD9000_GPIO_CONFIG_OUT_VALUE BIT(2) 3962306a36Sopenharmony_ci#define UCD9000_GPIO_CONFIG_STATUS BIT(3) 4062306a36Sopenharmony_ci#define UCD9000_GPIO_INPUT 0 4162306a36Sopenharmony_ci#define UCD9000_GPIO_OUTPUT 1 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define UCD9000_MON_TYPE(x) (((x) >> 5) & 0x07) 4462306a36Sopenharmony_ci#define UCD9000_MON_PAGE(x) ((x) & 0x1f) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define UCD9000_MON_VOLTAGE 1 4762306a36Sopenharmony_ci#define UCD9000_MON_TEMPERATURE 2 4862306a36Sopenharmony_ci#define UCD9000_MON_CURRENT 3 4962306a36Sopenharmony_ci#define UCD9000_MON_VOLTAGE_HW 4 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define UCD9000_NUM_FAN 4 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define UCD9000_GPIO_NAME_LEN 16 5462306a36Sopenharmony_ci#define UCD9090_NUM_GPIOS 23 5562306a36Sopenharmony_ci#define UCD901XX_NUM_GPIOS 26 5662306a36Sopenharmony_ci#define UCD90320_NUM_GPIOS 84 5762306a36Sopenharmony_ci#define UCD90910_NUM_GPIOS 26 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define UCD9000_DEBUGFS_NAME_LEN 24 6062306a36Sopenharmony_ci#define UCD9000_GPI_COUNT 8 6162306a36Sopenharmony_ci#define UCD90320_GPI_COUNT 32 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistruct ucd9000_data { 6462306a36Sopenharmony_ci u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX]; 6562306a36Sopenharmony_ci struct pmbus_driver_info info; 6662306a36Sopenharmony_ci#ifdef CONFIG_GPIOLIB 6762306a36Sopenharmony_ci struct gpio_chip gpio; 6862306a36Sopenharmony_ci#endif 6962306a36Sopenharmony_ci struct dentry *debugfs; 7062306a36Sopenharmony_ci ktime_t write_time; 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci#define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info) 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistruct ucd9000_debugfs_entry { 7562306a36Sopenharmony_ci struct i2c_client *client; 7662306a36Sopenharmony_ci u8 index; 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* 8062306a36Sopenharmony_ci * It has been observed that the UCD90320 randomly fails register access when 8162306a36Sopenharmony_ci * doing another access right on the back of a register write. To mitigate this 8262306a36Sopenharmony_ci * make sure that there is a minimum delay between a write access and the 8362306a36Sopenharmony_ci * following access. The 250us is based on experimental data. At a delay of 8462306a36Sopenharmony_ci * 200us the issue seems to go away. Add a bit of extra margin to allow for 8562306a36Sopenharmony_ci * system to system differences. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci#define UCD90320_WAIT_DELAY_US 250 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic inline void ucd90320_wait(const struct ucd9000_data *data) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci s64 delta = ktime_us_delta(ktime_get(), data->write_time); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (delta < UCD90320_WAIT_DELAY_US) 9462306a36Sopenharmony_ci udelay(UCD90320_WAIT_DELAY_US - delta); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int ucd90320_read_word_data(struct i2c_client *client, int page, 9862306a36Sopenharmony_ci int phase, int reg) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 10162306a36Sopenharmony_ci struct ucd9000_data *data = to_ucd9000_data(info); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (reg >= PMBUS_VIRT_BASE) 10462306a36Sopenharmony_ci return -ENXIO; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci ucd90320_wait(data); 10762306a36Sopenharmony_ci return pmbus_read_word_data(client, page, phase, reg); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int ucd90320_read_byte_data(struct i2c_client *client, int page, int reg) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 11362306a36Sopenharmony_ci struct ucd9000_data *data = to_ucd9000_data(info); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci ucd90320_wait(data); 11662306a36Sopenharmony_ci return pmbus_read_byte_data(client, page, reg); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int ucd90320_write_word_data(struct i2c_client *client, int page, 12062306a36Sopenharmony_ci int reg, u16 word) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 12362306a36Sopenharmony_ci struct ucd9000_data *data = to_ucd9000_data(info); 12462306a36Sopenharmony_ci int ret; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci ucd90320_wait(data); 12762306a36Sopenharmony_ci ret = pmbus_write_word_data(client, page, reg, word); 12862306a36Sopenharmony_ci data->write_time = ktime_get(); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return ret; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int ucd90320_write_byte(struct i2c_client *client, int page, u8 value) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 13662306a36Sopenharmony_ci struct ucd9000_data *data = to_ucd9000_data(info); 13762306a36Sopenharmony_ci int ret; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci ucd90320_wait(data); 14062306a36Sopenharmony_ci ret = pmbus_write_byte(client, page, value); 14162306a36Sopenharmony_ci data->write_time = ktime_get(); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return ret; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int ucd9000_get_fan_config(struct i2c_client *client, int fan) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci int fan_config = 0; 14962306a36Sopenharmony_ci struct ucd9000_data *data 15062306a36Sopenharmony_ci = to_ucd9000_data(pmbus_get_driver_info(client)); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (data->fan_data[fan][3] & 1) 15362306a36Sopenharmony_ci fan_config |= PB_FAN_2_INSTALLED; /* Use lower bit position */ 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Pulses/revolution */ 15662306a36Sopenharmony_ci fan_config |= (data->fan_data[fan][3] & 0x06) >> 1; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return fan_config; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci int ret = 0; 16462306a36Sopenharmony_ci int fan_config; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci switch (reg) { 16762306a36Sopenharmony_ci case PMBUS_FAN_CONFIG_12: 16862306a36Sopenharmony_ci if (page > 0) 16962306a36Sopenharmony_ci return -ENXIO; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci ret = ucd9000_get_fan_config(client, 0); 17262306a36Sopenharmony_ci if (ret < 0) 17362306a36Sopenharmony_ci return ret; 17462306a36Sopenharmony_ci fan_config = ret << 4; 17562306a36Sopenharmony_ci ret = ucd9000_get_fan_config(client, 1); 17662306a36Sopenharmony_ci if (ret < 0) 17762306a36Sopenharmony_ci return ret; 17862306a36Sopenharmony_ci fan_config |= ret; 17962306a36Sopenharmony_ci ret = fan_config; 18062306a36Sopenharmony_ci break; 18162306a36Sopenharmony_ci case PMBUS_FAN_CONFIG_34: 18262306a36Sopenharmony_ci if (page > 0) 18362306a36Sopenharmony_ci return -ENXIO; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ret = ucd9000_get_fan_config(client, 2); 18662306a36Sopenharmony_ci if (ret < 0) 18762306a36Sopenharmony_ci return ret; 18862306a36Sopenharmony_ci fan_config = ret << 4; 18962306a36Sopenharmony_ci ret = ucd9000_get_fan_config(client, 3); 19062306a36Sopenharmony_ci if (ret < 0) 19162306a36Sopenharmony_ci return ret; 19262306a36Sopenharmony_ci fan_config |= ret; 19362306a36Sopenharmony_ci ret = fan_config; 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci default: 19662306a36Sopenharmony_ci ret = -ENODATA; 19762306a36Sopenharmony_ci break; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci return ret; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic const struct i2c_device_id ucd9000_id[] = { 20362306a36Sopenharmony_ci {"ucd9000", ucd9000}, 20462306a36Sopenharmony_ci {"ucd90120", ucd90120}, 20562306a36Sopenharmony_ci {"ucd90124", ucd90124}, 20662306a36Sopenharmony_ci {"ucd90160", ucd90160}, 20762306a36Sopenharmony_ci {"ucd90320", ucd90320}, 20862306a36Sopenharmony_ci {"ucd9090", ucd9090}, 20962306a36Sopenharmony_ci {"ucd90910", ucd90910}, 21062306a36Sopenharmony_ci {} 21162306a36Sopenharmony_ci}; 21262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ucd9000_id); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic const struct of_device_id __maybe_unused ucd9000_of_match[] = { 21562306a36Sopenharmony_ci { 21662306a36Sopenharmony_ci .compatible = "ti,ucd9000", 21762306a36Sopenharmony_ci .data = (void *)ucd9000 21862306a36Sopenharmony_ci }, 21962306a36Sopenharmony_ci { 22062306a36Sopenharmony_ci .compatible = "ti,ucd90120", 22162306a36Sopenharmony_ci .data = (void *)ucd90120 22262306a36Sopenharmony_ci }, 22362306a36Sopenharmony_ci { 22462306a36Sopenharmony_ci .compatible = "ti,ucd90124", 22562306a36Sopenharmony_ci .data = (void *)ucd90124 22662306a36Sopenharmony_ci }, 22762306a36Sopenharmony_ci { 22862306a36Sopenharmony_ci .compatible = "ti,ucd90160", 22962306a36Sopenharmony_ci .data = (void *)ucd90160 23062306a36Sopenharmony_ci }, 23162306a36Sopenharmony_ci { 23262306a36Sopenharmony_ci .compatible = "ti,ucd90320", 23362306a36Sopenharmony_ci .data = (void *)ucd90320 23462306a36Sopenharmony_ci }, 23562306a36Sopenharmony_ci { 23662306a36Sopenharmony_ci .compatible = "ti,ucd9090", 23762306a36Sopenharmony_ci .data = (void *)ucd9090 23862306a36Sopenharmony_ci }, 23962306a36Sopenharmony_ci { 24062306a36Sopenharmony_ci .compatible = "ti,ucd90910", 24162306a36Sopenharmony_ci .data = (void *)ucd90910 24262306a36Sopenharmony_ci }, 24362306a36Sopenharmony_ci { }, 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ucd9000_of_match); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci#ifdef CONFIG_GPIOLIB 24862306a36Sopenharmony_cistatic int ucd9000_gpio_read_config(struct i2c_client *client, 24962306a36Sopenharmony_ci unsigned int offset) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci int ret; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* No page set required */ 25462306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_SELECT, offset); 25562306a36Sopenharmony_ci if (ret < 0) 25662306a36Sopenharmony_ci return ret; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci return i2c_smbus_read_byte_data(client, UCD9000_GPIO_CONFIG); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct i2c_client *client = gpiochip_get_data(gc); 26462306a36Sopenharmony_ci int ret; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci ret = ucd9000_gpio_read_config(client, offset); 26762306a36Sopenharmony_ci if (ret < 0) 26862306a36Sopenharmony_ci return ret; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return !!(ret & UCD9000_GPIO_CONFIG_STATUS); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset, 27462306a36Sopenharmony_ci int value) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct i2c_client *client = gpiochip_get_data(gc); 27762306a36Sopenharmony_ci int ret; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci ret = ucd9000_gpio_read_config(client, offset); 28062306a36Sopenharmony_ci if (ret < 0) { 28162306a36Sopenharmony_ci dev_dbg(&client->dev, "failed to read GPIO %d config: %d\n", 28262306a36Sopenharmony_ci offset, ret); 28362306a36Sopenharmony_ci return; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (value) { 28762306a36Sopenharmony_ci if (ret & UCD9000_GPIO_CONFIG_STATUS) 28862306a36Sopenharmony_ci return; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci ret |= UCD9000_GPIO_CONFIG_STATUS; 29162306a36Sopenharmony_ci } else { 29262306a36Sopenharmony_ci if (!(ret & UCD9000_GPIO_CONFIG_STATUS)) 29362306a36Sopenharmony_ci return; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ret &= ~UCD9000_GPIO_CONFIG_STATUS; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci ret |= UCD9000_GPIO_CONFIG_ENABLE; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* Page set not required */ 30162306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret); 30262306a36Sopenharmony_ci if (ret < 0) { 30362306a36Sopenharmony_ci dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n", 30462306a36Sopenharmony_ci offset, ret); 30562306a36Sopenharmony_ci return; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci ret &= ~UCD9000_GPIO_CONFIG_ENABLE; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret); 31162306a36Sopenharmony_ci if (ret < 0) 31262306a36Sopenharmony_ci dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n", 31362306a36Sopenharmony_ci offset, ret); 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int ucd9000_gpio_get_direction(struct gpio_chip *gc, 31762306a36Sopenharmony_ci unsigned int offset) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct i2c_client *client = gpiochip_get_data(gc); 32062306a36Sopenharmony_ci int ret; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci ret = ucd9000_gpio_read_config(client, offset); 32362306a36Sopenharmony_ci if (ret < 0) 32462306a36Sopenharmony_ci return ret; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return !(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic int ucd9000_gpio_set_direction(struct gpio_chip *gc, 33062306a36Sopenharmony_ci unsigned int offset, bool direction_out, 33162306a36Sopenharmony_ci int requested_out) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct i2c_client *client = gpiochip_get_data(gc); 33462306a36Sopenharmony_ci int ret, config, out_val; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci ret = ucd9000_gpio_read_config(client, offset); 33762306a36Sopenharmony_ci if (ret < 0) 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (direction_out) { 34162306a36Sopenharmony_ci out_val = requested_out ? UCD9000_GPIO_CONFIG_OUT_VALUE : 0; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (ret & UCD9000_GPIO_CONFIG_OUT_ENABLE) { 34462306a36Sopenharmony_ci if ((ret & UCD9000_GPIO_CONFIG_OUT_VALUE) == out_val) 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci } else { 34762306a36Sopenharmony_ci ret |= UCD9000_GPIO_CONFIG_OUT_ENABLE; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (out_val) 35162306a36Sopenharmony_ci ret |= UCD9000_GPIO_CONFIG_OUT_VALUE; 35262306a36Sopenharmony_ci else 35362306a36Sopenharmony_ci ret &= ~UCD9000_GPIO_CONFIG_OUT_VALUE; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci } else { 35662306a36Sopenharmony_ci if (!(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE)) 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ret &= ~UCD9000_GPIO_CONFIG_OUT_ENABLE; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ret |= UCD9000_GPIO_CONFIG_ENABLE; 36362306a36Sopenharmony_ci config = ret; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* Page set not required */ 36662306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, config); 36762306a36Sopenharmony_ci if (ret < 0) 36862306a36Sopenharmony_ci return ret; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci config &= ~UCD9000_GPIO_CONFIG_ENABLE; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, config); 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic int ucd9000_gpio_direction_input(struct gpio_chip *gc, 37662306a36Sopenharmony_ci unsigned int offset) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci return ucd9000_gpio_set_direction(gc, offset, UCD9000_GPIO_INPUT, 0); 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int ucd9000_gpio_direction_output(struct gpio_chip *gc, 38262306a36Sopenharmony_ci unsigned int offset, int val) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci return ucd9000_gpio_set_direction(gc, offset, UCD9000_GPIO_OUTPUT, 38562306a36Sopenharmony_ci val); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic void ucd9000_probe_gpio(struct i2c_client *client, 38962306a36Sopenharmony_ci const struct i2c_device_id *mid, 39062306a36Sopenharmony_ci struct ucd9000_data *data) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci int rc; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci switch (mid->driver_data) { 39562306a36Sopenharmony_ci case ucd9090: 39662306a36Sopenharmony_ci data->gpio.ngpio = UCD9090_NUM_GPIOS; 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci case ucd90120: 39962306a36Sopenharmony_ci case ucd90124: 40062306a36Sopenharmony_ci case ucd90160: 40162306a36Sopenharmony_ci data->gpio.ngpio = UCD901XX_NUM_GPIOS; 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci case ucd90320: 40462306a36Sopenharmony_ci data->gpio.ngpio = UCD90320_NUM_GPIOS; 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci case ucd90910: 40762306a36Sopenharmony_ci data->gpio.ngpio = UCD90910_NUM_GPIOS; 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci default: 41062306a36Sopenharmony_ci return; /* GPIO support is optional. */ 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* 41462306a36Sopenharmony_ci * Pinmux support has not been added to the new gpio_chip. 41562306a36Sopenharmony_ci * This support should be added when possible given the mux 41662306a36Sopenharmony_ci * behavior of these IO devices. 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ci data->gpio.label = client->name; 41962306a36Sopenharmony_ci data->gpio.get_direction = ucd9000_gpio_get_direction; 42062306a36Sopenharmony_ci data->gpio.direction_input = ucd9000_gpio_direction_input; 42162306a36Sopenharmony_ci data->gpio.direction_output = ucd9000_gpio_direction_output; 42262306a36Sopenharmony_ci data->gpio.get = ucd9000_gpio_get; 42362306a36Sopenharmony_ci data->gpio.set = ucd9000_gpio_set; 42462306a36Sopenharmony_ci data->gpio.can_sleep = true; 42562306a36Sopenharmony_ci data->gpio.base = -1; 42662306a36Sopenharmony_ci data->gpio.parent = &client->dev; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci rc = devm_gpiochip_add_data(&client->dev, &data->gpio, client); 42962306a36Sopenharmony_ci if (rc) 43062306a36Sopenharmony_ci dev_warn(&client->dev, "Could not add gpiochip: %d\n", rc); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci#else 43362306a36Sopenharmony_cistatic void ucd9000_probe_gpio(struct i2c_client *client, 43462306a36Sopenharmony_ci const struct i2c_device_id *mid, 43562306a36Sopenharmony_ci struct ucd9000_data *data) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci#endif /* CONFIG_GPIOLIB */ 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 44162306a36Sopenharmony_cistatic int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci int ret = pmbus_set_page(client, 0, 0xff); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (ret < 0) 44662306a36Sopenharmony_ci return ret; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, buffer); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct ucd9000_debugfs_entry *entry = data; 45462306a36Sopenharmony_ci struct i2c_client *client = entry->client; 45562306a36Sopenharmony_ci u8 buffer[I2C_SMBUS_BLOCK_MAX]; 45662306a36Sopenharmony_ci int ret, i; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci ret = ucd9000_get_mfr_status(client, buffer); 45962306a36Sopenharmony_ci if (ret < 0) 46062306a36Sopenharmony_ci return ret; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* 46362306a36Sopenharmony_ci * GPI fault bits are in sets of 8, two bytes from end of response. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci i = ret - 3 - entry->index / 8; 46662306a36Sopenharmony_ci if (i >= 0) 46762306a36Sopenharmony_ci *val = !!(buffer[i] & BIT(entry->index % 8)); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return 0; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit, 47262306a36Sopenharmony_ci ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n"); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic ssize_t ucd9000_debugfs_read_mfr_status(struct file *file, 47562306a36Sopenharmony_ci char __user *buf, size_t count, 47662306a36Sopenharmony_ci loff_t *ppos) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct i2c_client *client = file->private_data; 47962306a36Sopenharmony_ci u8 buffer[I2C_SMBUS_BLOCK_MAX]; 48062306a36Sopenharmony_ci char str[(I2C_SMBUS_BLOCK_MAX * 2) + 2]; 48162306a36Sopenharmony_ci char *res; 48262306a36Sopenharmony_ci int rc; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci rc = ucd9000_get_mfr_status(client, buffer); 48562306a36Sopenharmony_ci if (rc < 0) 48662306a36Sopenharmony_ci return rc; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci res = bin2hex(str, buffer, min(rc, I2C_SMBUS_BLOCK_MAX)); 48962306a36Sopenharmony_ci *res++ = '\n'; 49062306a36Sopenharmony_ci *res = 0; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, str, res - str); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic const struct file_operations ucd9000_debugfs_show_mfr_status_fops = { 49662306a36Sopenharmony_ci .llseek = noop_llseek, 49762306a36Sopenharmony_ci .read = ucd9000_debugfs_read_mfr_status, 49862306a36Sopenharmony_ci .open = simple_open, 49962306a36Sopenharmony_ci}; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic int ucd9000_init_debugfs(struct i2c_client *client, 50262306a36Sopenharmony_ci const struct i2c_device_id *mid, 50362306a36Sopenharmony_ci struct ucd9000_data *data) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct dentry *debugfs; 50662306a36Sopenharmony_ci struct ucd9000_debugfs_entry *entries; 50762306a36Sopenharmony_ci int i, gpi_count; 50862306a36Sopenharmony_ci char name[UCD9000_DEBUGFS_NAME_LEN]; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci debugfs = pmbus_get_debugfs_dir(client); 51162306a36Sopenharmony_ci if (!debugfs) 51262306a36Sopenharmony_ci return -ENOENT; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci data->debugfs = debugfs_create_dir(client->name, debugfs); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* 51762306a36Sopenharmony_ci * Of the chips this driver supports, only the UCD9090, UCD90160, 51862306a36Sopenharmony_ci * UCD90320, and UCD90910 report GPI faults in their MFR_STATUS 51962306a36Sopenharmony_ci * register, so only create the GPI fault debugfs attributes for those 52062306a36Sopenharmony_ci * chips. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ci if (mid->driver_data == ucd9090 || mid->driver_data == ucd90160 || 52362306a36Sopenharmony_ci mid->driver_data == ucd90320 || mid->driver_data == ucd90910) { 52462306a36Sopenharmony_ci gpi_count = mid->driver_data == ucd90320 ? UCD90320_GPI_COUNT 52562306a36Sopenharmony_ci : UCD9000_GPI_COUNT; 52662306a36Sopenharmony_ci entries = devm_kcalloc(&client->dev, 52762306a36Sopenharmony_ci gpi_count, sizeof(*entries), 52862306a36Sopenharmony_ci GFP_KERNEL); 52962306a36Sopenharmony_ci if (!entries) 53062306a36Sopenharmony_ci return -ENOMEM; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci for (i = 0; i < gpi_count; i++) { 53362306a36Sopenharmony_ci entries[i].client = client; 53462306a36Sopenharmony_ci entries[i].index = i; 53562306a36Sopenharmony_ci scnprintf(name, UCD9000_DEBUGFS_NAME_LEN, 53662306a36Sopenharmony_ci "gpi%d_alarm", i + 1); 53762306a36Sopenharmony_ci debugfs_create_file(name, 0444, data->debugfs, 53862306a36Sopenharmony_ci &entries[i], 53962306a36Sopenharmony_ci &ucd9000_debugfs_mfr_status_bit); 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci scnprintf(name, UCD9000_DEBUGFS_NAME_LEN, "mfr_status"); 54462306a36Sopenharmony_ci debugfs_create_file(name, 0444, data->debugfs, client, 54562306a36Sopenharmony_ci &ucd9000_debugfs_show_mfr_status_fops); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci return 0; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci#else 55062306a36Sopenharmony_cistatic int ucd9000_init_debugfs(struct i2c_client *client, 55162306a36Sopenharmony_ci const struct i2c_device_id *mid, 55262306a36Sopenharmony_ci struct ucd9000_data *data) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci return 0; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci#endif /* CONFIG_DEBUG_FS */ 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic int ucd9000_probe(struct i2c_client *client) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; 56162306a36Sopenharmony_ci struct ucd9000_data *data; 56262306a36Sopenharmony_ci struct pmbus_driver_info *info; 56362306a36Sopenharmony_ci const struct i2c_device_id *mid; 56462306a36Sopenharmony_ci enum chips chip; 56562306a36Sopenharmony_ci int i, ret; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (!i2c_check_functionality(client->adapter, 56862306a36Sopenharmony_ci I2C_FUNC_SMBUS_BYTE_DATA | 56962306a36Sopenharmony_ci I2C_FUNC_SMBUS_BLOCK_DATA)) 57062306a36Sopenharmony_ci return -ENODEV; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci ret = i2c_smbus_read_block_data(client, UCD9000_DEVICE_ID, 57362306a36Sopenharmony_ci block_buffer); 57462306a36Sopenharmony_ci if (ret < 0) { 57562306a36Sopenharmony_ci dev_err(&client->dev, "Failed to read device ID\n"); 57662306a36Sopenharmony_ci return ret; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci block_buffer[ret] = '\0'; 57962306a36Sopenharmony_ci dev_info(&client->dev, "Device ID %s\n", block_buffer); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci for (mid = ucd9000_id; mid->name[0]; mid++) { 58262306a36Sopenharmony_ci if (!strncasecmp(mid->name, block_buffer, strlen(mid->name))) 58362306a36Sopenharmony_ci break; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci if (!mid->name[0]) { 58662306a36Sopenharmony_ci dev_err(&client->dev, "Unsupported device\n"); 58762306a36Sopenharmony_ci return -ENODEV; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (client->dev.of_node) 59162306a36Sopenharmony_ci chip = (uintptr_t)of_device_get_match_data(&client->dev); 59262306a36Sopenharmony_ci else 59362306a36Sopenharmony_ci chip = mid->driver_data; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (chip != ucd9000 && strcmp(client->name, mid->name) != 0) 59662306a36Sopenharmony_ci dev_notice(&client->dev, 59762306a36Sopenharmony_ci "Device mismatch: Configured %s, detected %s\n", 59862306a36Sopenharmony_ci client->name, mid->name); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci data = devm_kzalloc(&client->dev, sizeof(struct ucd9000_data), 60162306a36Sopenharmony_ci GFP_KERNEL); 60262306a36Sopenharmony_ci if (!data) 60362306a36Sopenharmony_ci return -ENOMEM; 60462306a36Sopenharmony_ci info = &data->info; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, UCD9000_NUM_PAGES); 60762306a36Sopenharmony_ci if (ret < 0) { 60862306a36Sopenharmony_ci dev_err(&client->dev, 60962306a36Sopenharmony_ci "Failed to read number of active pages\n"); 61062306a36Sopenharmony_ci return ret; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci info->pages = ret; 61362306a36Sopenharmony_ci if (!info->pages) { 61462306a36Sopenharmony_ci dev_err(&client->dev, "No pages configured\n"); 61562306a36Sopenharmony_ci return -ENODEV; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci /* The internal temperature sensor is always active */ 61962306a36Sopenharmony_ci info->func[0] = PMBUS_HAVE_TEMP; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* Everything else is configurable */ 62262306a36Sopenharmony_ci ret = i2c_smbus_read_block_data(client, UCD9000_MONITOR_CONFIG, 62362306a36Sopenharmony_ci block_buffer); 62462306a36Sopenharmony_ci if (ret <= 0) { 62562306a36Sopenharmony_ci dev_err(&client->dev, "Failed to read configuration data\n"); 62662306a36Sopenharmony_ci return -ENODEV; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci for (i = 0; i < ret; i++) { 62962306a36Sopenharmony_ci int page = UCD9000_MON_PAGE(block_buffer[i]); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (page >= info->pages) 63262306a36Sopenharmony_ci continue; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci switch (UCD9000_MON_TYPE(block_buffer[i])) { 63562306a36Sopenharmony_ci case UCD9000_MON_VOLTAGE: 63662306a36Sopenharmony_ci case UCD9000_MON_VOLTAGE_HW: 63762306a36Sopenharmony_ci info->func[page] |= PMBUS_HAVE_VOUT 63862306a36Sopenharmony_ci | PMBUS_HAVE_STATUS_VOUT; 63962306a36Sopenharmony_ci break; 64062306a36Sopenharmony_ci case UCD9000_MON_TEMPERATURE: 64162306a36Sopenharmony_ci info->func[page] |= PMBUS_HAVE_TEMP2 64262306a36Sopenharmony_ci | PMBUS_HAVE_STATUS_TEMP; 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci case UCD9000_MON_CURRENT: 64562306a36Sopenharmony_ci info->func[page] |= PMBUS_HAVE_IOUT 64662306a36Sopenharmony_ci | PMBUS_HAVE_STATUS_IOUT; 64762306a36Sopenharmony_ci break; 64862306a36Sopenharmony_ci default: 64962306a36Sopenharmony_ci break; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* Fan configuration */ 65462306a36Sopenharmony_ci if (mid->driver_data == ucd90124) { 65562306a36Sopenharmony_ci for (i = 0; i < UCD9000_NUM_FAN; i++) { 65662306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, 65762306a36Sopenharmony_ci UCD9000_FAN_CONFIG_INDEX, i); 65862306a36Sopenharmony_ci ret = i2c_smbus_read_block_data(client, 65962306a36Sopenharmony_ci UCD9000_FAN_CONFIG, 66062306a36Sopenharmony_ci data->fan_data[i]); 66162306a36Sopenharmony_ci if (ret < 0) 66262306a36Sopenharmony_ci return ret; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, UCD9000_FAN_CONFIG_INDEX, 0); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci info->read_byte_data = ucd9000_read_byte_data; 66762306a36Sopenharmony_ci info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 66862306a36Sopenharmony_ci | PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34; 66962306a36Sopenharmony_ci } else if (mid->driver_data == ucd90320) { 67062306a36Sopenharmony_ci info->read_byte_data = ucd90320_read_byte_data; 67162306a36Sopenharmony_ci info->read_word_data = ucd90320_read_word_data; 67262306a36Sopenharmony_ci info->write_byte = ucd90320_write_byte; 67362306a36Sopenharmony_ci info->write_word_data = ucd90320_write_word_data; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci ucd9000_probe_gpio(client, mid, data); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci ret = pmbus_do_probe(client, info); 67962306a36Sopenharmony_ci if (ret) 68062306a36Sopenharmony_ci return ret; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci ret = ucd9000_init_debugfs(client, mid, data); 68362306a36Sopenharmony_ci if (ret) 68462306a36Sopenharmony_ci dev_warn(&client->dev, "Failed to register debugfs: %d\n", 68562306a36Sopenharmony_ci ret); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci return 0; 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci/* This is the driver that will be inserted */ 69162306a36Sopenharmony_cistatic struct i2c_driver ucd9000_driver = { 69262306a36Sopenharmony_ci .driver = { 69362306a36Sopenharmony_ci .name = "ucd9000", 69462306a36Sopenharmony_ci .of_match_table = of_match_ptr(ucd9000_of_match), 69562306a36Sopenharmony_ci }, 69662306a36Sopenharmony_ci .probe = ucd9000_probe, 69762306a36Sopenharmony_ci .id_table = ucd9000_id, 69862306a36Sopenharmony_ci}; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cimodule_i2c_driver(ucd9000_driver); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ciMODULE_AUTHOR("Guenter Roeck"); 70362306a36Sopenharmony_ciMODULE_DESCRIPTION("PMBus driver for TI UCD90xxx"); 70462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 70562306a36Sopenharmony_ciMODULE_IMPORT_NS(PMBUS); 706