18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Ira W. Snyder <iws@ovro.caltech.edu> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This driver is based on the ds1621 and ina209 drivers. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Datasheet: 108c2ecf20Sopenharmony_ci * http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1140,P19392,D13517 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/bitops.h> 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/i2c.h> 208c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 218c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 228c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_data/ltc4245.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Here are names of the chip's registers (a.k.a. commands) */ 268c2ecf20Sopenharmony_cienum ltc4245_cmd { 278c2ecf20Sopenharmony_ci LTC4245_STATUS = 0x00, /* readonly */ 288c2ecf20Sopenharmony_ci LTC4245_ALERT = 0x01, 298c2ecf20Sopenharmony_ci LTC4245_CONTROL = 0x02, 308c2ecf20Sopenharmony_ci LTC4245_ON = 0x03, 318c2ecf20Sopenharmony_ci LTC4245_FAULT1 = 0x04, 328c2ecf20Sopenharmony_ci LTC4245_FAULT2 = 0x05, 338c2ecf20Sopenharmony_ci LTC4245_GPIO = 0x06, 348c2ecf20Sopenharmony_ci LTC4245_ADCADR = 0x07, 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci LTC4245_12VIN = 0x10, 378c2ecf20Sopenharmony_ci LTC4245_12VSENSE = 0x11, 388c2ecf20Sopenharmony_ci LTC4245_12VOUT = 0x12, 398c2ecf20Sopenharmony_ci LTC4245_5VIN = 0x13, 408c2ecf20Sopenharmony_ci LTC4245_5VSENSE = 0x14, 418c2ecf20Sopenharmony_ci LTC4245_5VOUT = 0x15, 428c2ecf20Sopenharmony_ci LTC4245_3VIN = 0x16, 438c2ecf20Sopenharmony_ci LTC4245_3VSENSE = 0x17, 448c2ecf20Sopenharmony_ci LTC4245_3VOUT = 0x18, 458c2ecf20Sopenharmony_ci LTC4245_VEEIN = 0x19, 468c2ecf20Sopenharmony_ci LTC4245_VEESENSE = 0x1a, 478c2ecf20Sopenharmony_ci LTC4245_VEEOUT = 0x1b, 488c2ecf20Sopenharmony_ci LTC4245_GPIOADC = 0x1c, 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct ltc4245_data { 528c2ecf20Sopenharmony_ci struct i2c_client *client; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci struct mutex update_lock; 558c2ecf20Sopenharmony_ci bool valid; 568c2ecf20Sopenharmony_ci unsigned long last_updated; /* in jiffies */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* Control registers */ 598c2ecf20Sopenharmony_ci u8 cregs[0x08]; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* Voltage registers */ 628c2ecf20Sopenharmony_ci u8 vregs[0x0d]; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* GPIO ADC registers */ 658c2ecf20Sopenharmony_ci bool use_extra_gpios; 668c2ecf20Sopenharmony_ci int gpios[3]; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * Update the readings from the GPIO pins. If the driver has been configured to 718c2ecf20Sopenharmony_ci * sample all GPIO's as analog voltages, a round-robin sampling method is used. 728c2ecf20Sopenharmony_ci * Otherwise, only the configured GPIO pin is sampled. 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * LOCKING: must hold data->update_lock 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_cistatic void ltc4245_update_gpios(struct device *dev) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct ltc4245_data *data = dev_get_drvdata(dev); 798c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 808c2ecf20Sopenharmony_ci u8 gpio_curr, gpio_next, gpio_reg; 818c2ecf20Sopenharmony_ci int i; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* no extra gpio support, we're basically done */ 848c2ecf20Sopenharmony_ci if (!data->use_extra_gpios) { 858c2ecf20Sopenharmony_ci data->gpios[0] = data->vregs[LTC4245_GPIOADC - 0x10]; 868c2ecf20Sopenharmony_ci return; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* 908c2ecf20Sopenharmony_ci * If the last reading was too long ago, then we mark all old GPIO 918c2ecf20Sopenharmony_ci * readings as stale by setting them to -EAGAIN 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ci if (time_after(jiffies, data->last_updated + 5 * HZ)) { 948c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->gpios); i++) 958c2ecf20Sopenharmony_ci data->gpios[i] = -EAGAIN; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* 998c2ecf20Sopenharmony_ci * Get the current GPIO pin 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * The datasheet calls these GPIO[1-3], but we'll calculate the zero 1028c2ecf20Sopenharmony_ci * based array index instead, and call them GPIO[0-2]. This is much 1038c2ecf20Sopenharmony_ci * easier to think about. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci gpio_curr = (data->cregs[LTC4245_GPIO] & 0xc0) >> 6; 1068c2ecf20Sopenharmony_ci if (gpio_curr > 0) 1078c2ecf20Sopenharmony_ci gpio_curr -= 1; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Read the GPIO voltage from the GPIOADC register */ 1108c2ecf20Sopenharmony_ci data->gpios[gpio_curr] = data->vregs[LTC4245_GPIOADC - 0x10]; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Find the next GPIO pin to read */ 1138c2ecf20Sopenharmony_ci gpio_next = (gpio_curr + 1) % ARRAY_SIZE(data->gpios); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* 1168c2ecf20Sopenharmony_ci * Calculate the correct setting for the GPIO register so it will 1178c2ecf20Sopenharmony_ci * sample the next GPIO pin 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci gpio_reg = (data->cregs[LTC4245_GPIO] & 0x3f) | ((gpio_next + 1) << 6); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* Update the GPIO register */ 1228c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, LTC4245_GPIO, gpio_reg); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* Update saved data */ 1258c2ecf20Sopenharmony_ci data->cregs[LTC4245_GPIO] = gpio_reg; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic struct ltc4245_data *ltc4245_update_device(struct device *dev) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct ltc4245_data *data = dev_get_drvdata(dev); 1318c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 1328c2ecf20Sopenharmony_ci s32 val; 1338c2ecf20Sopenharmony_ci int i; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Read control registers -- 0x00 to 0x07 */ 1408c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->cregs); i++) { 1418c2ecf20Sopenharmony_ci val = i2c_smbus_read_byte_data(client, i); 1428c2ecf20Sopenharmony_ci if (unlikely(val < 0)) 1438c2ecf20Sopenharmony_ci data->cregs[i] = 0; 1448c2ecf20Sopenharmony_ci else 1458c2ecf20Sopenharmony_ci data->cregs[i] = val; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* Read voltage registers -- 0x10 to 0x1c */ 1498c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->vregs); i++) { 1508c2ecf20Sopenharmony_ci val = i2c_smbus_read_byte_data(client, i+0x10); 1518c2ecf20Sopenharmony_ci if (unlikely(val < 0)) 1528c2ecf20Sopenharmony_ci data->vregs[i] = 0; 1538c2ecf20Sopenharmony_ci else 1548c2ecf20Sopenharmony_ci data->vregs[i] = val; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* Update GPIO readings */ 1588c2ecf20Sopenharmony_ci ltc4245_update_gpios(dev); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci data->last_updated = jiffies; 1618c2ecf20Sopenharmony_ci data->valid = true; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return data; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* Return the voltage from the given register in millivolts */ 1708c2ecf20Sopenharmony_cistatic int ltc4245_get_voltage(struct device *dev, u8 reg) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct ltc4245_data *data = ltc4245_update_device(dev); 1738c2ecf20Sopenharmony_ci const u8 regval = data->vregs[reg - 0x10]; 1748c2ecf20Sopenharmony_ci u32 voltage = 0; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci switch (reg) { 1778c2ecf20Sopenharmony_ci case LTC4245_12VIN: 1788c2ecf20Sopenharmony_ci case LTC4245_12VOUT: 1798c2ecf20Sopenharmony_ci voltage = regval * 55; 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci case LTC4245_5VIN: 1828c2ecf20Sopenharmony_ci case LTC4245_5VOUT: 1838c2ecf20Sopenharmony_ci voltage = regval * 22; 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci case LTC4245_3VIN: 1868c2ecf20Sopenharmony_ci case LTC4245_3VOUT: 1878c2ecf20Sopenharmony_ci voltage = regval * 15; 1888c2ecf20Sopenharmony_ci break; 1898c2ecf20Sopenharmony_ci case LTC4245_VEEIN: 1908c2ecf20Sopenharmony_ci case LTC4245_VEEOUT: 1918c2ecf20Sopenharmony_ci voltage = regval * -55; 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci case LTC4245_GPIOADC: 1948c2ecf20Sopenharmony_ci voltage = regval * 10; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci default: 1978c2ecf20Sopenharmony_ci /* If we get here, the developer messed up */ 1988c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return voltage; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/* Return the current in the given sense register in milliAmperes */ 2068c2ecf20Sopenharmony_cistatic unsigned int ltc4245_get_current(struct device *dev, u8 reg) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct ltc4245_data *data = ltc4245_update_device(dev); 2098c2ecf20Sopenharmony_ci const u8 regval = data->vregs[reg - 0x10]; 2108c2ecf20Sopenharmony_ci unsigned int voltage; 2118c2ecf20Sopenharmony_ci unsigned int curr; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* 2148c2ecf20Sopenharmony_ci * The strange looking conversions that follow are fixed-point 2158c2ecf20Sopenharmony_ci * math, since we cannot do floating point in the kernel. 2168c2ecf20Sopenharmony_ci * 2178c2ecf20Sopenharmony_ci * Step 1: convert sense register to microVolts 2188c2ecf20Sopenharmony_ci * Step 2: convert voltage to milliAmperes 2198c2ecf20Sopenharmony_ci * 2208c2ecf20Sopenharmony_ci * If you play around with the V=IR equation, you come up with 2218c2ecf20Sopenharmony_ci * the following: X uV / Y mOhm == Z mA 2228c2ecf20Sopenharmony_ci * 2238c2ecf20Sopenharmony_ci * With the resistors that are fractions of a milliOhm, we multiply 2248c2ecf20Sopenharmony_ci * the voltage and resistance by 10, to shift the decimal point. 2258c2ecf20Sopenharmony_ci * Now we can use the normal division operator again. 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci switch (reg) { 2298c2ecf20Sopenharmony_ci case LTC4245_12VSENSE: 2308c2ecf20Sopenharmony_ci voltage = regval * 250; /* voltage in uV */ 2318c2ecf20Sopenharmony_ci curr = voltage / 50; /* sense resistor 50 mOhm */ 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci case LTC4245_5VSENSE: 2348c2ecf20Sopenharmony_ci voltage = regval * 125; /* voltage in uV */ 2358c2ecf20Sopenharmony_ci curr = (voltage * 10) / 35; /* sense resistor 3.5 mOhm */ 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci case LTC4245_3VSENSE: 2388c2ecf20Sopenharmony_ci voltage = regval * 125; /* voltage in uV */ 2398c2ecf20Sopenharmony_ci curr = (voltage * 10) / 25; /* sense resistor 2.5 mOhm */ 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci case LTC4245_VEESENSE: 2428c2ecf20Sopenharmony_ci voltage = regval * 250; /* voltage in uV */ 2438c2ecf20Sopenharmony_ci curr = voltage / 100; /* sense resistor 100 mOhm */ 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci default: 2468c2ecf20Sopenharmony_ci /* If we get here, the developer messed up */ 2478c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 2488c2ecf20Sopenharmony_ci curr = 0; 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return curr; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/* Map from voltage channel index to voltage register */ 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic const s8 ltc4245_in_regs[] = { 2588c2ecf20Sopenharmony_ci LTC4245_12VIN, LTC4245_5VIN, LTC4245_3VIN, LTC4245_VEEIN, 2598c2ecf20Sopenharmony_ci LTC4245_12VOUT, LTC4245_5VOUT, LTC4245_3VOUT, LTC4245_VEEOUT, 2608c2ecf20Sopenharmony_ci}; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* Map from current channel index to current register */ 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic const s8 ltc4245_curr_regs[] = { 2658c2ecf20Sopenharmony_ci LTC4245_12VSENSE, LTC4245_5VSENSE, LTC4245_3VSENSE, LTC4245_VEESENSE, 2668c2ecf20Sopenharmony_ci}; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int ltc4245_read_curr(struct device *dev, u32 attr, int channel, 2698c2ecf20Sopenharmony_ci long *val) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct ltc4245_data *data = ltc4245_update_device(dev); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci switch (attr) { 2748c2ecf20Sopenharmony_ci case hwmon_curr_input: 2758c2ecf20Sopenharmony_ci *val = ltc4245_get_current(dev, ltc4245_curr_regs[channel]); 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci case hwmon_curr_max_alarm: 2788c2ecf20Sopenharmony_ci *val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel + 4)); 2798c2ecf20Sopenharmony_ci return 0; 2808c2ecf20Sopenharmony_ci default: 2818c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int ltc4245_read_in(struct device *dev, u32 attr, int channel, long *val) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct ltc4245_data *data = ltc4245_update_device(dev); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci switch (attr) { 2908c2ecf20Sopenharmony_ci case hwmon_in_input: 2918c2ecf20Sopenharmony_ci if (channel < 8) { 2928c2ecf20Sopenharmony_ci *val = ltc4245_get_voltage(dev, 2938c2ecf20Sopenharmony_ci ltc4245_in_regs[channel]); 2948c2ecf20Sopenharmony_ci } else { 2958c2ecf20Sopenharmony_ci int regval = data->gpios[channel - 8]; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (regval < 0) 2988c2ecf20Sopenharmony_ci return regval; 2998c2ecf20Sopenharmony_ci *val = regval * 10; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci case hwmon_in_min_alarm: 3038c2ecf20Sopenharmony_ci if (channel < 4) 3048c2ecf20Sopenharmony_ci *val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel)); 3058c2ecf20Sopenharmony_ci else 3068c2ecf20Sopenharmony_ci *val = !!(data->cregs[LTC4245_FAULT2] & 3078c2ecf20Sopenharmony_ci BIT(channel - 4)); 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci default: 3108c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int ltc4245_read_power(struct device *dev, u32 attr, int channel, 3158c2ecf20Sopenharmony_ci long *val) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci unsigned long curr; 3188c2ecf20Sopenharmony_ci long voltage; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci switch (attr) { 3218c2ecf20Sopenharmony_ci case hwmon_power_input: 3228c2ecf20Sopenharmony_ci (void)ltc4245_update_device(dev); 3238c2ecf20Sopenharmony_ci curr = ltc4245_get_current(dev, ltc4245_curr_regs[channel]); 3248c2ecf20Sopenharmony_ci voltage = ltc4245_get_voltage(dev, ltc4245_in_regs[channel]); 3258c2ecf20Sopenharmony_ci *val = abs(curr * voltage); 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci default: 3288c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int ltc4245_read(struct device *dev, enum hwmon_sensor_types type, 3338c2ecf20Sopenharmony_ci u32 attr, int channel, long *val) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci switch (type) { 3378c2ecf20Sopenharmony_ci case hwmon_curr: 3388c2ecf20Sopenharmony_ci return ltc4245_read_curr(dev, attr, channel, val); 3398c2ecf20Sopenharmony_ci case hwmon_power: 3408c2ecf20Sopenharmony_ci return ltc4245_read_power(dev, attr, channel, val); 3418c2ecf20Sopenharmony_ci case hwmon_in: 3428c2ecf20Sopenharmony_ci return ltc4245_read_in(dev, attr, channel - 1, val); 3438c2ecf20Sopenharmony_ci default: 3448c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic umode_t ltc4245_is_visible(const void *_data, 3498c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, 3508c2ecf20Sopenharmony_ci u32 attr, int channel) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci const struct ltc4245_data *data = _data; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci switch (type) { 3558c2ecf20Sopenharmony_ci case hwmon_in: 3568c2ecf20Sopenharmony_ci if (channel == 0) 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci switch (attr) { 3598c2ecf20Sopenharmony_ci case hwmon_in_input: 3608c2ecf20Sopenharmony_ci if (channel > 9 && !data->use_extra_gpios) 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci return 0444; 3638c2ecf20Sopenharmony_ci case hwmon_in_min_alarm: 3648c2ecf20Sopenharmony_ci if (channel > 8) 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci return 0444; 3678c2ecf20Sopenharmony_ci default: 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci case hwmon_curr: 3718c2ecf20Sopenharmony_ci switch (attr) { 3728c2ecf20Sopenharmony_ci case hwmon_curr_input: 3738c2ecf20Sopenharmony_ci case hwmon_curr_max_alarm: 3748c2ecf20Sopenharmony_ci return 0444; 3758c2ecf20Sopenharmony_ci default: 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci case hwmon_power: 3798c2ecf20Sopenharmony_ci switch (attr) { 3808c2ecf20Sopenharmony_ci case hwmon_power_input: 3818c2ecf20Sopenharmony_ci return 0444; 3828c2ecf20Sopenharmony_ci default: 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci default: 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info *ltc4245_info[] = { 3918c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(in, 3928c2ecf20Sopenharmony_ci HWMON_I_INPUT, 3938c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_MIN_ALARM, 3948c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_MIN_ALARM, 3958c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_MIN_ALARM, 3968c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_MIN_ALARM, 3978c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_MIN_ALARM, 3988c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_MIN_ALARM, 3998c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_MIN_ALARM, 4008c2ecf20Sopenharmony_ci HWMON_I_INPUT | HWMON_I_MIN_ALARM, 4018c2ecf20Sopenharmony_ci HWMON_I_INPUT, 4028c2ecf20Sopenharmony_ci HWMON_I_INPUT, 4038c2ecf20Sopenharmony_ci HWMON_I_INPUT), 4048c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(curr, 4058c2ecf20Sopenharmony_ci HWMON_C_INPUT | HWMON_C_MAX_ALARM, 4068c2ecf20Sopenharmony_ci HWMON_C_INPUT | HWMON_C_MAX_ALARM, 4078c2ecf20Sopenharmony_ci HWMON_C_INPUT | HWMON_C_MAX_ALARM, 4088c2ecf20Sopenharmony_ci HWMON_C_INPUT | HWMON_C_MAX_ALARM), 4098c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(power, 4108c2ecf20Sopenharmony_ci HWMON_P_INPUT, 4118c2ecf20Sopenharmony_ci HWMON_P_INPUT, 4128c2ecf20Sopenharmony_ci HWMON_P_INPUT, 4138c2ecf20Sopenharmony_ci HWMON_P_INPUT), 4148c2ecf20Sopenharmony_ci NULL 4158c2ecf20Sopenharmony_ci}; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic const struct hwmon_ops ltc4245_hwmon_ops = { 4188c2ecf20Sopenharmony_ci .is_visible = ltc4245_is_visible, 4198c2ecf20Sopenharmony_ci .read = ltc4245_read, 4208c2ecf20Sopenharmony_ci}; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic const struct hwmon_chip_info ltc4245_chip_info = { 4238c2ecf20Sopenharmony_ci .ops = <c4245_hwmon_ops, 4248c2ecf20Sopenharmony_ci .info = ltc4245_info, 4258c2ecf20Sopenharmony_ci}; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic bool ltc4245_use_extra_gpios(struct i2c_client *client) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct ltc4245_platform_data *pdata = dev_get_platdata(&client->dev); 4308c2ecf20Sopenharmony_ci struct device_node *np = client->dev.of_node; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* prefer platform data */ 4338c2ecf20Sopenharmony_ci if (pdata) 4348c2ecf20Sopenharmony_ci return pdata->use_extra_gpios; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* fallback on OF */ 4378c2ecf20Sopenharmony_ci if (of_find_property(np, "ltc4245,use-extra-gpios", NULL)) 4388c2ecf20Sopenharmony_ci return true; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return false; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int ltc4245_probe(struct i2c_client *client) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 4468c2ecf20Sopenharmony_ci struct ltc4245_data *data; 4478c2ecf20Sopenharmony_ci struct device *hwmon_dev; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 4508c2ecf20Sopenharmony_ci return -ENODEV; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 4538c2ecf20Sopenharmony_ci if (!data) 4548c2ecf20Sopenharmony_ci return -ENOMEM; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci data->client = client; 4578c2ecf20Sopenharmony_ci mutex_init(&data->update_lock); 4588c2ecf20Sopenharmony_ci data->use_extra_gpios = ltc4245_use_extra_gpios(client); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* Initialize the LTC4245 chip */ 4618c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00); 4628c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, 4658c2ecf20Sopenharmony_ci client->name, data, 4668c2ecf20Sopenharmony_ci <c4245_chip_info, 4678c2ecf20Sopenharmony_ci NULL); 4688c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic const struct i2c_device_id ltc4245_id[] = { 4728c2ecf20Sopenharmony_ci { "ltc4245", 0 }, 4738c2ecf20Sopenharmony_ci { } 4748c2ecf20Sopenharmony_ci}; 4758c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ltc4245_id); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci/* This is the driver that will be inserted */ 4788c2ecf20Sopenharmony_cistatic struct i2c_driver ltc4245_driver = { 4798c2ecf20Sopenharmony_ci .driver = { 4808c2ecf20Sopenharmony_ci .name = "ltc4245", 4818c2ecf20Sopenharmony_ci }, 4828c2ecf20Sopenharmony_ci .probe_new = ltc4245_probe, 4838c2ecf20Sopenharmony_ci .id_table = ltc4245_id, 4848c2ecf20Sopenharmony_ci}; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cimodule_i2c_driver(ltc4245_driver); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>"); 4898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LTC4245 driver"); 4908c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 491