18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel CHT Whiskey Cove PMIC I2C Master driver 48c2ecf20Sopenharmony_ci * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on various non upstream patches to support the CHT Whiskey Cove PMIC: 78c2ecf20Sopenharmony_ci * Copyright (C) 2011 - 2014 Intel Corporation. All rights reserved. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/acpi.h> 118c2ecf20Sopenharmony_ci#include <linux/completion.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/i2c.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/irq.h> 168c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 178c2ecf20Sopenharmony_ci#include <linux/mfd/intel_soc_pmic.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/power/bq24190_charger.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define CHT_WC_I2C_CTRL 0x5e24 248c2ecf20Sopenharmony_ci#define CHT_WC_I2C_CTRL_WR BIT(0) 258c2ecf20Sopenharmony_ci#define CHT_WC_I2C_CTRL_RD BIT(1) 268c2ecf20Sopenharmony_ci#define CHT_WC_I2C_CLIENT_ADDR 0x5e25 278c2ecf20Sopenharmony_ci#define CHT_WC_I2C_REG_OFFSET 0x5e26 288c2ecf20Sopenharmony_ci#define CHT_WC_I2C_WRDATA 0x5e27 298c2ecf20Sopenharmony_ci#define CHT_WC_I2C_RDDATA 0x5e28 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define CHT_WC_EXTCHGRIRQ 0x6e0a 328c2ecf20Sopenharmony_ci#define CHT_WC_EXTCHGRIRQ_CLIENT_IRQ BIT(0) 338c2ecf20Sopenharmony_ci#define CHT_WC_EXTCHGRIRQ_WRITE_IRQ BIT(1) 348c2ecf20Sopenharmony_ci#define CHT_WC_EXTCHGRIRQ_READ_IRQ BIT(2) 358c2ecf20Sopenharmony_ci#define CHT_WC_EXTCHGRIRQ_NACK_IRQ BIT(3) 368c2ecf20Sopenharmony_ci#define CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK ((u8)GENMASK(3, 1)) 378c2ecf20Sopenharmony_ci#define CHT_WC_EXTCHGRIRQ_MSK 0x6e17 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct cht_wc_i2c_adap { 408c2ecf20Sopenharmony_ci struct i2c_adapter adapter; 418c2ecf20Sopenharmony_ci wait_queue_head_t wait; 428c2ecf20Sopenharmony_ci struct irq_chip irqchip; 438c2ecf20Sopenharmony_ci struct mutex adap_lock; 448c2ecf20Sopenharmony_ci struct mutex irqchip_lock; 458c2ecf20Sopenharmony_ci struct regmap *regmap; 468c2ecf20Sopenharmony_ci struct irq_domain *irq_domain; 478c2ecf20Sopenharmony_ci struct i2c_client *client; 488c2ecf20Sopenharmony_ci int client_irq; 498c2ecf20Sopenharmony_ci u8 irq_mask; 508c2ecf20Sopenharmony_ci u8 old_irq_mask; 518c2ecf20Sopenharmony_ci int read_data; 528c2ecf20Sopenharmony_ci bool io_error; 538c2ecf20Sopenharmony_ci bool done; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic irqreturn_t cht_wc_i2c_adap_thread_handler(int id, void *data) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct cht_wc_i2c_adap *adap = data; 598c2ecf20Sopenharmony_ci int ret, reg; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci mutex_lock(&adap->adap_lock); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci /* Read IRQs */ 648c2ecf20Sopenharmony_ci ret = regmap_read(adap->regmap, CHT_WC_EXTCHGRIRQ, ®); 658c2ecf20Sopenharmony_ci if (ret) { 668c2ecf20Sopenharmony_ci dev_err(&adap->adapter.dev, "Error reading extchgrirq reg\n"); 678c2ecf20Sopenharmony_ci mutex_unlock(&adap->adap_lock); 688c2ecf20Sopenharmony_ci return IRQ_NONE; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci reg &= ~adap->irq_mask; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* Reads must be acked after reading the received data. */ 748c2ecf20Sopenharmony_ci ret = regmap_read(adap->regmap, CHT_WC_I2C_RDDATA, &adap->read_data); 758c2ecf20Sopenharmony_ci if (ret) 768c2ecf20Sopenharmony_ci adap->io_error = true; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* 798c2ecf20Sopenharmony_ci * Immediately ack IRQs, so that if new IRQs arrives while we're 808c2ecf20Sopenharmony_ci * handling the previous ones our irq will re-trigger when we're done. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ, reg); 838c2ecf20Sopenharmony_ci if (ret) 848c2ecf20Sopenharmony_ci dev_err(&adap->adapter.dev, "Error writing extchgrirq reg\n"); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (reg & CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK) { 878c2ecf20Sopenharmony_ci adap->io_error |= !!(reg & CHT_WC_EXTCHGRIRQ_NACK_IRQ); 888c2ecf20Sopenharmony_ci adap->done = true; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci mutex_unlock(&adap->adap_lock); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (reg & CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK) 948c2ecf20Sopenharmony_ci wake_up(&adap->wait); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* 978c2ecf20Sopenharmony_ci * Do NOT use handle_nested_irq here, the client irq handler will 988c2ecf20Sopenharmony_ci * likely want to do i2c transfers and the i2c controller uses this 998c2ecf20Sopenharmony_ci * interrupt handler as well, so running the client irq handler from 1008c2ecf20Sopenharmony_ci * this thread will cause things to lock up. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci if (reg & CHT_WC_EXTCHGRIRQ_CLIENT_IRQ) { 1038c2ecf20Sopenharmony_ci /* 1048c2ecf20Sopenharmony_ci * generic_handle_irq expects local IRQs to be disabled 1058c2ecf20Sopenharmony_ci * as normally it is called from interrupt context. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci local_irq_disable(); 1088c2ecf20Sopenharmony_ci generic_handle_irq(adap->client_irq); 1098c2ecf20Sopenharmony_ci local_irq_enable(); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic u32 cht_wc_i2c_adap_master_func(struct i2c_adapter *adap) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci /* This i2c adapter only supports SMBUS byte transfers */ 1188c2ecf20Sopenharmony_ci return I2C_FUNC_SMBUS_BYTE_DATA; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int cht_wc_i2c_adap_smbus_xfer(struct i2c_adapter *_adap, u16 addr, 1228c2ecf20Sopenharmony_ci unsigned short flags, char read_write, 1238c2ecf20Sopenharmony_ci u8 command, int size, 1248c2ecf20Sopenharmony_ci union i2c_smbus_data *data) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct cht_wc_i2c_adap *adap = i2c_get_adapdata(_adap); 1278c2ecf20Sopenharmony_ci int ret; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci mutex_lock(&adap->adap_lock); 1308c2ecf20Sopenharmony_ci adap->io_error = false; 1318c2ecf20Sopenharmony_ci adap->done = false; 1328c2ecf20Sopenharmony_ci mutex_unlock(&adap->adap_lock); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ret = regmap_write(adap->regmap, CHT_WC_I2C_CLIENT_ADDR, addr); 1358c2ecf20Sopenharmony_ci if (ret) 1368c2ecf20Sopenharmony_ci return ret; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 1398c2ecf20Sopenharmony_ci ret = regmap_write(adap->regmap, CHT_WC_I2C_WRDATA, data->byte); 1408c2ecf20Sopenharmony_ci if (ret) 1418c2ecf20Sopenharmony_ci return ret; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci ret = regmap_write(adap->regmap, CHT_WC_I2C_REG_OFFSET, command); 1458c2ecf20Sopenharmony_ci if (ret) 1468c2ecf20Sopenharmony_ci return ret; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci ret = regmap_write(adap->regmap, CHT_WC_I2C_CTRL, 1498c2ecf20Sopenharmony_ci (read_write == I2C_SMBUS_WRITE) ? 1508c2ecf20Sopenharmony_ci CHT_WC_I2C_CTRL_WR : CHT_WC_I2C_CTRL_RD); 1518c2ecf20Sopenharmony_ci if (ret) 1528c2ecf20Sopenharmony_ci return ret; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci ret = wait_event_timeout(adap->wait, adap->done, msecs_to_jiffies(30)); 1558c2ecf20Sopenharmony_ci if (ret == 0) { 1568c2ecf20Sopenharmony_ci /* 1578c2ecf20Sopenharmony_ci * The CHT GPIO controller serializes all IRQs, sometimes 1588c2ecf20Sopenharmony_ci * causing significant delays, check status manually. 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci cht_wc_i2c_adap_thread_handler(0, adap); 1618c2ecf20Sopenharmony_ci if (!adap->done) 1628c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ret = 0; 1668c2ecf20Sopenharmony_ci mutex_lock(&adap->adap_lock); 1678c2ecf20Sopenharmony_ci if (adap->io_error) 1688c2ecf20Sopenharmony_ci ret = -EIO; 1698c2ecf20Sopenharmony_ci else if (read_write == I2C_SMBUS_READ) 1708c2ecf20Sopenharmony_ci data->byte = adap->read_data; 1718c2ecf20Sopenharmony_ci mutex_unlock(&adap->adap_lock); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic const struct i2c_algorithm cht_wc_i2c_adap_algo = { 1778c2ecf20Sopenharmony_ci .functionality = cht_wc_i2c_adap_master_func, 1788c2ecf20Sopenharmony_ci .smbus_xfer = cht_wc_i2c_adap_smbus_xfer, 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* 1828c2ecf20Sopenharmony_ci * We are an i2c-adapter which itself is part of an i2c-client. This means that 1838c2ecf20Sopenharmony_ci * transfers done through us take adapter->bus_lock twice, once for our parent 1848c2ecf20Sopenharmony_ci * i2c-adapter and once to take our own bus_lock. Lockdep does not like this 1858c2ecf20Sopenharmony_ci * nested locking, to make lockdep happy in the case of busses with muxes, the 1868c2ecf20Sopenharmony_ci * i2c-core's i2c_adapter_lock_bus function calls: 1878c2ecf20Sopenharmony_ci * rt_mutex_lock_nested(&adapter->bus_lock, i2c_adapter_depth(adapter)); 1888c2ecf20Sopenharmony_ci * 1898c2ecf20Sopenharmony_ci * But i2c_adapter_depth only works when the direct parent of the adapter is 1908c2ecf20Sopenharmony_ci * another adapter, as it is only meant for muxes. In our case there is an 1918c2ecf20Sopenharmony_ci * i2c-client and MFD instantiated platform_device in the parent->child chain 1928c2ecf20Sopenharmony_ci * between the 2 devices. 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * So we override the default i2c_lock_operations and pass a hardcoded 1958c2ecf20Sopenharmony_ci * depth of 1 to rt_mutex_lock_nested, to make lockdep happy. 1968c2ecf20Sopenharmony_ci * 1978c2ecf20Sopenharmony_ci * Note that if there were to be a mux attached to our adapter, this would 1988c2ecf20Sopenharmony_ci * break things again since the i2c-mux code expects the root-adapter to have 1998c2ecf20Sopenharmony_ci * a locking depth of 0. But we always have only 1 client directly attached 2008c2ecf20Sopenharmony_ci * in the form of the Charger IC paired with the CHT Whiskey Cove PMIC. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_cistatic void cht_wc_i2c_adap_lock_bus(struct i2c_adapter *adapter, 2038c2ecf20Sopenharmony_ci unsigned int flags) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci rt_mutex_lock_nested(&adapter->bus_lock, 1); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int cht_wc_i2c_adap_trylock_bus(struct i2c_adapter *adapter, 2098c2ecf20Sopenharmony_ci unsigned int flags) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci return rt_mutex_trylock(&adapter->bus_lock); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic void cht_wc_i2c_adap_unlock_bus(struct i2c_adapter *adapter, 2158c2ecf20Sopenharmony_ci unsigned int flags) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci rt_mutex_unlock(&adapter->bus_lock); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic const struct i2c_lock_operations cht_wc_i2c_adap_lock_ops = { 2218c2ecf20Sopenharmony_ci .lock_bus = cht_wc_i2c_adap_lock_bus, 2228c2ecf20Sopenharmony_ci .trylock_bus = cht_wc_i2c_adap_trylock_bus, 2238c2ecf20Sopenharmony_ci .unlock_bus = cht_wc_i2c_adap_unlock_bus, 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/**** irqchip for the client connected to the extchgr i2c adapter ****/ 2278c2ecf20Sopenharmony_cistatic void cht_wc_i2c_irq_lock(struct irq_data *data) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci mutex_lock(&adap->irqchip_lock); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic void cht_wc_i2c_irq_sync_unlock(struct irq_data *data) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); 2378c2ecf20Sopenharmony_ci int ret; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (adap->irq_mask != adap->old_irq_mask) { 2408c2ecf20Sopenharmony_ci ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ_MSK, 2418c2ecf20Sopenharmony_ci adap->irq_mask); 2428c2ecf20Sopenharmony_ci if (ret == 0) 2438c2ecf20Sopenharmony_ci adap->old_irq_mask = adap->irq_mask; 2448c2ecf20Sopenharmony_ci else 2458c2ecf20Sopenharmony_ci dev_err(&adap->adapter.dev, "Error writing EXTCHGRIRQ_MSK\n"); 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci mutex_unlock(&adap->irqchip_lock); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic void cht_wc_i2c_irq_enable(struct irq_data *data) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci adap->irq_mask &= ~CHT_WC_EXTCHGRIRQ_CLIENT_IRQ; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic void cht_wc_i2c_irq_disable(struct irq_data *data) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci adap->irq_mask |= CHT_WC_EXTCHGRIRQ_CLIENT_IRQ; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic const struct irq_chip cht_wc_i2c_irq_chip = { 2668c2ecf20Sopenharmony_ci .irq_bus_lock = cht_wc_i2c_irq_lock, 2678c2ecf20Sopenharmony_ci .irq_bus_sync_unlock = cht_wc_i2c_irq_sync_unlock, 2688c2ecf20Sopenharmony_ci .irq_disable = cht_wc_i2c_irq_disable, 2698c2ecf20Sopenharmony_ci .irq_enable = cht_wc_i2c_irq_enable, 2708c2ecf20Sopenharmony_ci .name = "cht_wc_ext_chrg_irq_chip", 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic const char * const bq24190_suppliers[] = { 2748c2ecf20Sopenharmony_ci "tcpm-source-psy-i2c-fusb302" }; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic const struct property_entry bq24190_props[] = { 2778c2ecf20Sopenharmony_ci PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_suppliers), 2788c2ecf20Sopenharmony_ci PROPERTY_ENTRY_BOOL("omit-battery-class"), 2798c2ecf20Sopenharmony_ci PROPERTY_ENTRY_BOOL("disable-reset"), 2808c2ecf20Sopenharmony_ci { } 2818c2ecf20Sopenharmony_ci}; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic struct regulator_consumer_supply fusb302_consumer = { 2848c2ecf20Sopenharmony_ci .supply = "vbus", 2858c2ecf20Sopenharmony_ci /* Must match fusb302 dev_name in intel_cht_int33fe.c */ 2868c2ecf20Sopenharmony_ci .dev_name = "i2c-fusb302", 2878c2ecf20Sopenharmony_ci}; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic const struct regulator_init_data bq24190_vbus_init_data = { 2908c2ecf20Sopenharmony_ci .constraints = { 2918c2ecf20Sopenharmony_ci /* The name is used in intel_cht_int33fe.c do not change. */ 2928c2ecf20Sopenharmony_ci .name = "cht_wc_usb_typec_vbus", 2938c2ecf20Sopenharmony_ci .valid_ops_mask = REGULATOR_CHANGE_STATUS, 2948c2ecf20Sopenharmony_ci }, 2958c2ecf20Sopenharmony_ci .consumer_supplies = &fusb302_consumer, 2968c2ecf20Sopenharmony_ci .num_consumer_supplies = 1, 2978c2ecf20Sopenharmony_ci}; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic struct bq24190_platform_data bq24190_pdata = { 3008c2ecf20Sopenharmony_ci .regulator_init_data = &bq24190_vbus_init_data, 3018c2ecf20Sopenharmony_ci}; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); 3068c2ecf20Sopenharmony_ci struct cht_wc_i2c_adap *adap; 3078c2ecf20Sopenharmony_ci struct i2c_board_info board_info = { 3088c2ecf20Sopenharmony_ci .type = "bq24190", 3098c2ecf20Sopenharmony_ci .addr = 0x6b, 3108c2ecf20Sopenharmony_ci .dev_name = "bq24190", 3118c2ecf20Sopenharmony_ci .properties = bq24190_props, 3128c2ecf20Sopenharmony_ci .platform_data = &bq24190_pdata, 3138c2ecf20Sopenharmony_ci }; 3148c2ecf20Sopenharmony_ci int ret, reg, irq; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 3178c2ecf20Sopenharmony_ci if (irq < 0) 3188c2ecf20Sopenharmony_ci return irq; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci adap = devm_kzalloc(&pdev->dev, sizeof(*adap), GFP_KERNEL); 3218c2ecf20Sopenharmony_ci if (!adap) 3228c2ecf20Sopenharmony_ci return -ENOMEM; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci init_waitqueue_head(&adap->wait); 3258c2ecf20Sopenharmony_ci mutex_init(&adap->adap_lock); 3268c2ecf20Sopenharmony_ci mutex_init(&adap->irqchip_lock); 3278c2ecf20Sopenharmony_ci adap->irqchip = cht_wc_i2c_irq_chip; 3288c2ecf20Sopenharmony_ci adap->regmap = pmic->regmap; 3298c2ecf20Sopenharmony_ci adap->adapter.owner = THIS_MODULE; 3308c2ecf20Sopenharmony_ci adap->adapter.class = I2C_CLASS_HWMON; 3318c2ecf20Sopenharmony_ci adap->adapter.algo = &cht_wc_i2c_adap_algo; 3328c2ecf20Sopenharmony_ci adap->adapter.lock_ops = &cht_wc_i2c_adap_lock_ops; 3338c2ecf20Sopenharmony_ci strlcpy(adap->adapter.name, "PMIC I2C Adapter", 3348c2ecf20Sopenharmony_ci sizeof(adap->adapter.name)); 3358c2ecf20Sopenharmony_ci adap->adapter.dev.parent = &pdev->dev; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* Clear and activate i2c-adapter interrupts, disable client IRQ */ 3388c2ecf20Sopenharmony_ci adap->old_irq_mask = adap->irq_mask = ~CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci ret = regmap_read(adap->regmap, CHT_WC_I2C_RDDATA, ®); 3418c2ecf20Sopenharmony_ci if (ret) 3428c2ecf20Sopenharmony_ci return ret; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ, ~adap->irq_mask); 3458c2ecf20Sopenharmony_ci if (ret) 3468c2ecf20Sopenharmony_ci return ret; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ_MSK, adap->irq_mask); 3498c2ecf20Sopenharmony_ci if (ret) 3508c2ecf20Sopenharmony_ci return ret; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* Alloc and register client IRQ */ 3538c2ecf20Sopenharmony_ci adap->irq_domain = irq_domain_add_linear(pdev->dev.of_node, 1, 3548c2ecf20Sopenharmony_ci &irq_domain_simple_ops, NULL); 3558c2ecf20Sopenharmony_ci if (!adap->irq_domain) 3568c2ecf20Sopenharmony_ci return -ENOMEM; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci adap->client_irq = irq_create_mapping(adap->irq_domain, 0); 3598c2ecf20Sopenharmony_ci if (!adap->client_irq) { 3608c2ecf20Sopenharmony_ci ret = -ENOMEM; 3618c2ecf20Sopenharmony_ci goto remove_irq_domain; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci irq_set_chip_data(adap->client_irq, adap); 3658c2ecf20Sopenharmony_ci irq_set_chip_and_handler(adap->client_irq, &adap->irqchip, 3668c2ecf20Sopenharmony_ci handle_simple_irq); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, 3698c2ecf20Sopenharmony_ci cht_wc_i2c_adap_thread_handler, 3708c2ecf20Sopenharmony_ci IRQF_ONESHOT, "PMIC I2C Adapter", adap); 3718c2ecf20Sopenharmony_ci if (ret) 3728c2ecf20Sopenharmony_ci goto remove_irq_domain; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci i2c_set_adapdata(&adap->adapter, adap); 3758c2ecf20Sopenharmony_ci ret = i2c_add_adapter(&adap->adapter); 3768c2ecf20Sopenharmony_ci if (ret) 3778c2ecf20Sopenharmony_ci goto remove_irq_domain; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* 3808c2ecf20Sopenharmony_ci * Normally the Whiskey Cove PMIC is paired with a TI bq24292i charger, 3818c2ecf20Sopenharmony_ci * connected to this i2c bus, and a max17047 fuel-gauge and a fusb302 3828c2ecf20Sopenharmony_ci * USB Type-C controller connected to another i2c bus. In this setup 3838c2ecf20Sopenharmony_ci * the max17047 and fusb302 devices are enumerated through an INT33FE 3848c2ecf20Sopenharmony_ci * ACPI device. If this device is present register an i2c-client for 3858c2ecf20Sopenharmony_ci * the TI bq24292i charger. 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci if (acpi_dev_present("INT33FE", NULL, -1)) { 3888c2ecf20Sopenharmony_ci board_info.irq = adap->client_irq; 3898c2ecf20Sopenharmony_ci adap->client = i2c_new_client_device(&adap->adapter, &board_info); 3908c2ecf20Sopenharmony_ci if (IS_ERR(adap->client)) { 3918c2ecf20Sopenharmony_ci ret = PTR_ERR(adap->client); 3928c2ecf20Sopenharmony_ci goto del_adapter; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, adap); 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cidel_adapter: 4008c2ecf20Sopenharmony_ci i2c_del_adapter(&adap->adapter); 4018c2ecf20Sopenharmony_ciremove_irq_domain: 4028c2ecf20Sopenharmony_ci irq_domain_remove(adap->irq_domain); 4038c2ecf20Sopenharmony_ci return ret; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic int cht_wc_i2c_adap_i2c_remove(struct platform_device *pdev) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct cht_wc_i2c_adap *adap = platform_get_drvdata(pdev); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci i2c_unregister_device(adap->client); 4118c2ecf20Sopenharmony_ci i2c_del_adapter(&adap->adapter); 4128c2ecf20Sopenharmony_ci irq_domain_remove(adap->irq_domain); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return 0; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic const struct platform_device_id cht_wc_i2c_adap_id_table[] = { 4188c2ecf20Sopenharmony_ci { .name = "cht_wcove_ext_chgr" }, 4198c2ecf20Sopenharmony_ci {}, 4208c2ecf20Sopenharmony_ci}; 4218c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, cht_wc_i2c_adap_id_table); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic struct platform_driver cht_wc_i2c_adap_driver = { 4248c2ecf20Sopenharmony_ci .probe = cht_wc_i2c_adap_i2c_probe, 4258c2ecf20Sopenharmony_ci .remove = cht_wc_i2c_adap_i2c_remove, 4268c2ecf20Sopenharmony_ci .driver = { 4278c2ecf20Sopenharmony_ci .name = "cht_wcove_ext_chgr", 4288c2ecf20Sopenharmony_ci }, 4298c2ecf20Sopenharmony_ci .id_table = cht_wc_i2c_adap_id_table, 4308c2ecf20Sopenharmony_ci}; 4318c2ecf20Sopenharmony_cimodule_platform_driver(cht_wc_i2c_adap_driver); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel CHT Whiskey Cove PMIC I2C Master driver"); 4348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 4358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 436