162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Core driver for TI TPS6586x PMIC family 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2010 CompuLab Ltd. 662306a36Sopenharmony_ci * Mike Rapoport <mike@compulab.co.il> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on da903x.c. 962306a36Sopenharmony_ci * Copyright (C) 2008 Compulab, Ltd. 1062306a36Sopenharmony_ci * Mike Rapoport <mike@compulab.co.il> 1162306a36Sopenharmony_ci * Copyright (C) 2006-2008 Marvell International Ltd. 1262306a36Sopenharmony_ci * Eric Miao <eric.miao@marvell.com> 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/irq.h> 1762306a36Sopenharmony_ci#include <linux/irqdomain.h> 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/mutex.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/err.h> 2362306a36Sopenharmony_ci#include <linux/i2c.h> 2462306a36Sopenharmony_ci#include <linux/platform_device.h> 2562306a36Sopenharmony_ci#include <linux/regmap.h> 2662306a36Sopenharmony_ci#include <linux/of.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/mfd/core.h> 2962306a36Sopenharmony_ci#include <linux/mfd/tps6586x.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define TPS6586X_SUPPLYENE 0x14 3262306a36Sopenharmony_ci#define EXITSLREQ_BIT BIT(1) 3362306a36Sopenharmony_ci#define SLEEP_MODE_BIT BIT(3) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* interrupt control registers */ 3662306a36Sopenharmony_ci#define TPS6586X_INT_ACK1 0xb5 3762306a36Sopenharmony_ci#define TPS6586X_INT_ACK2 0xb6 3862306a36Sopenharmony_ci#define TPS6586X_INT_ACK3 0xb7 3962306a36Sopenharmony_ci#define TPS6586X_INT_ACK4 0xb8 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* interrupt mask registers */ 4262306a36Sopenharmony_ci#define TPS6586X_INT_MASK1 0xb0 4362306a36Sopenharmony_ci#define TPS6586X_INT_MASK2 0xb1 4462306a36Sopenharmony_ci#define TPS6586X_INT_MASK3 0xb2 4562306a36Sopenharmony_ci#define TPS6586X_INT_MASK4 0xb3 4662306a36Sopenharmony_ci#define TPS6586X_INT_MASK5 0xb4 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* device id */ 4962306a36Sopenharmony_ci#define TPS6586X_VERSIONCRC 0xcd 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* Maximum register */ 5262306a36Sopenharmony_ci#define TPS6586X_MAX_REGISTER TPS6586X_VERSIONCRC 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct tps6586x_irq_data { 5562306a36Sopenharmony_ci u8 mask_reg; 5662306a36Sopenharmony_ci u8 mask_mask; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define TPS6586X_IRQ(_reg, _mask) \ 6062306a36Sopenharmony_ci { \ 6162306a36Sopenharmony_ci .mask_reg = (_reg) - TPS6586X_INT_MASK1, \ 6262306a36Sopenharmony_ci .mask_mask = (_mask), \ 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic const struct tps6586x_irq_data tps6586x_irqs[] = { 6662306a36Sopenharmony_ci [TPS6586X_INT_PLDO_0] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 0), 6762306a36Sopenharmony_ci [TPS6586X_INT_PLDO_1] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 1), 6862306a36Sopenharmony_ci [TPS6586X_INT_PLDO_2] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 2), 6962306a36Sopenharmony_ci [TPS6586X_INT_PLDO_3] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 3), 7062306a36Sopenharmony_ci [TPS6586X_INT_PLDO_4] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 4), 7162306a36Sopenharmony_ci [TPS6586X_INT_PLDO_5] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 5), 7262306a36Sopenharmony_ci [TPS6586X_INT_PLDO_6] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 6), 7362306a36Sopenharmony_ci [TPS6586X_INT_PLDO_7] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 7), 7462306a36Sopenharmony_ci [TPS6586X_INT_COMP_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 0), 7562306a36Sopenharmony_ci [TPS6586X_INT_ADC] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 1), 7662306a36Sopenharmony_ci [TPS6586X_INT_PLDO_8] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 2), 7762306a36Sopenharmony_ci [TPS6586X_INT_PLDO_9] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 3), 7862306a36Sopenharmony_ci [TPS6586X_INT_PSM_0] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 4), 7962306a36Sopenharmony_ci [TPS6586X_INT_PSM_1] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 5), 8062306a36Sopenharmony_ci [TPS6586X_INT_PSM_2] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 6), 8162306a36Sopenharmony_ci [TPS6586X_INT_PSM_3] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 7), 8262306a36Sopenharmony_ci [TPS6586X_INT_RTC_ALM1] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 4), 8362306a36Sopenharmony_ci [TPS6586X_INT_ACUSB_OVP] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 0x03), 8462306a36Sopenharmony_ci [TPS6586X_INT_USB_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 2), 8562306a36Sopenharmony_ci [TPS6586X_INT_AC_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 3), 8662306a36Sopenharmony_ci [TPS6586X_INT_BAT_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 1 << 0), 8762306a36Sopenharmony_ci [TPS6586X_INT_CHG_STAT] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 0xfc), 8862306a36Sopenharmony_ci [TPS6586X_INT_CHG_TEMP] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 0x06), 8962306a36Sopenharmony_ci [TPS6586X_INT_PP] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 0xf0), 9062306a36Sopenharmony_ci [TPS6586X_INT_RESUME] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 5), 9162306a36Sopenharmony_ci [TPS6586X_INT_LOW_SYS] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 6), 9262306a36Sopenharmony_ci [TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1), 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic const struct resource tps6586x_rtc_resources[] = { 9662306a36Sopenharmony_ci { 9762306a36Sopenharmony_ci .start = TPS6586X_INT_RTC_ALM1, 9862306a36Sopenharmony_ci .end = TPS6586X_INT_RTC_ALM1, 9962306a36Sopenharmony_ci .flags = IORESOURCE_IRQ, 10062306a36Sopenharmony_ci }, 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic const struct mfd_cell tps6586x_cell[] = { 10462306a36Sopenharmony_ci { 10562306a36Sopenharmony_ci .name = "tps6586x-gpio", 10662306a36Sopenharmony_ci }, 10762306a36Sopenharmony_ci { 10862306a36Sopenharmony_ci .name = "tps6586x-regulator", 10962306a36Sopenharmony_ci }, 11062306a36Sopenharmony_ci { 11162306a36Sopenharmony_ci .name = "tps6586x-rtc", 11262306a36Sopenharmony_ci .num_resources = ARRAY_SIZE(tps6586x_rtc_resources), 11362306a36Sopenharmony_ci .resources = &tps6586x_rtc_resources[0], 11462306a36Sopenharmony_ci }, 11562306a36Sopenharmony_ci { 11662306a36Sopenharmony_ci .name = "tps6586x-onkey", 11762306a36Sopenharmony_ci }, 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistruct tps6586x { 12162306a36Sopenharmony_ci struct device *dev; 12262306a36Sopenharmony_ci struct i2c_client *client; 12362306a36Sopenharmony_ci struct regmap *regmap; 12462306a36Sopenharmony_ci int version; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci int irq; 12762306a36Sopenharmony_ci struct irq_chip irq_chip; 12862306a36Sopenharmony_ci struct mutex irq_lock; 12962306a36Sopenharmony_ci int irq_base; 13062306a36Sopenharmony_ci u32 irq_en; 13162306a36Sopenharmony_ci u8 mask_reg[5]; 13262306a36Sopenharmony_ci struct irq_domain *irq_domain; 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic inline struct tps6586x *dev_to_tps6586x(struct device *dev) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci return i2c_get_clientdata(to_i2c_client(dev)); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ciint tps6586x_write(struct device *dev, int reg, uint8_t val) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct tps6586x *tps6586x = dev_to_tps6586x(dev); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return regmap_write(tps6586x->regmap, reg, val); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tps6586x_write); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ciint tps6586x_writes(struct device *dev, int reg, int len, uint8_t *val) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct tps6586x *tps6586x = dev_to_tps6586x(dev); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return regmap_bulk_write(tps6586x->regmap, reg, val, len); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tps6586x_writes); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ciint tps6586x_read(struct device *dev, int reg, uint8_t *val) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct tps6586x *tps6586x = dev_to_tps6586x(dev); 15962306a36Sopenharmony_ci unsigned int rval; 16062306a36Sopenharmony_ci int ret; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci ret = regmap_read(tps6586x->regmap, reg, &rval); 16362306a36Sopenharmony_ci if (!ret) 16462306a36Sopenharmony_ci *val = rval; 16562306a36Sopenharmony_ci return ret; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tps6586x_read); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ciint tps6586x_reads(struct device *dev, int reg, int len, uint8_t *val) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct tps6586x *tps6586x = dev_to_tps6586x(dev); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return regmap_bulk_read(tps6586x->regmap, reg, val, len); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tps6586x_reads); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciint tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct tps6586x *tps6586x = dev_to_tps6586x(dev); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return regmap_update_bits(tps6586x->regmap, reg, bit_mask, bit_mask); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tps6586x_set_bits); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ciint tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct tps6586x *tps6586x = dev_to_tps6586x(dev); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return regmap_update_bits(tps6586x->regmap, reg, bit_mask, 0); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tps6586x_clr_bits); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ciint tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct tps6586x *tps6586x = dev_to_tps6586x(dev); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return regmap_update_bits(tps6586x->regmap, reg, mask, val); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tps6586x_update); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ciint tps6586x_irq_get_virq(struct device *dev, int irq) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct tps6586x *tps6586x = dev_to_tps6586x(dev); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return irq_create_mapping(tps6586x->irq_domain, irq); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tps6586x_irq_get_virq); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ciint tps6586x_get_version(struct device *dev) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct tps6586x *tps6586x = dev_get_drvdata(dev); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return tps6586x->version; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tps6586x_get_version); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int __remove_subdev(struct device *dev, void *unused) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci platform_device_unregister(to_platform_device(dev)); 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int tps6586x_remove_subdevs(struct tps6586x *tps6586x) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci return device_for_each_child(tps6586x->dev, NULL, __remove_subdev); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic void tps6586x_irq_lock(struct irq_data *data) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct tps6586x *tps6586x = irq_data_get_irq_chip_data(data); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci mutex_lock(&tps6586x->irq_lock); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void tps6586x_irq_enable(struct irq_data *irq_data) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data); 23862306a36Sopenharmony_ci unsigned int __irq = irq_data->hwirq; 23962306a36Sopenharmony_ci const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq]; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci tps6586x->mask_reg[data->mask_reg] &= ~data->mask_mask; 24262306a36Sopenharmony_ci tps6586x->irq_en |= (1 << __irq); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic void tps6586x_irq_disable(struct irq_data *irq_data) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci unsigned int __irq = irq_data->hwirq; 25062306a36Sopenharmony_ci const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq]; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci tps6586x->mask_reg[data->mask_reg] |= data->mask_mask; 25362306a36Sopenharmony_ci tps6586x->irq_en &= ~(1 << __irq); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic void tps6586x_irq_sync_unlock(struct irq_data *data) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct tps6586x *tps6586x = irq_data_get_irq_chip_data(data); 25962306a36Sopenharmony_ci int i; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tps6586x->mask_reg); i++) { 26262306a36Sopenharmony_ci int ret; 26362306a36Sopenharmony_ci ret = tps6586x_write(tps6586x->dev, 26462306a36Sopenharmony_ci TPS6586X_INT_MASK1 + i, 26562306a36Sopenharmony_ci tps6586x->mask_reg[i]); 26662306a36Sopenharmony_ci WARN_ON(ret); 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci mutex_unlock(&tps6586x->irq_lock); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic int tps6586x_irq_set_wake(struct irq_data *irq_data, unsigned int on) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data); 27562306a36Sopenharmony_ci return irq_set_irq_wake(tps6586x->irq, on); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic struct irq_chip tps6586x_irq_chip = { 27962306a36Sopenharmony_ci .name = "tps6586x", 28062306a36Sopenharmony_ci .irq_bus_lock = tps6586x_irq_lock, 28162306a36Sopenharmony_ci .irq_bus_sync_unlock = tps6586x_irq_sync_unlock, 28262306a36Sopenharmony_ci .irq_disable = tps6586x_irq_disable, 28362306a36Sopenharmony_ci .irq_enable = tps6586x_irq_enable, 28462306a36Sopenharmony_ci .irq_set_wake = pm_sleep_ptr(tps6586x_irq_set_wake), 28562306a36Sopenharmony_ci}; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int tps6586x_irq_map(struct irq_domain *h, unsigned int virq, 28862306a36Sopenharmony_ci irq_hw_number_t hw) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct tps6586x *tps6586x = h->host_data; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci irq_set_chip_data(virq, tps6586x); 29362306a36Sopenharmony_ci irq_set_chip_and_handler(virq, &tps6586x_irq_chip, handle_simple_irq); 29462306a36Sopenharmony_ci irq_set_nested_thread(virq, 1); 29562306a36Sopenharmony_ci irq_set_noprobe(virq); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic const struct irq_domain_ops tps6586x_domain_ops = { 30162306a36Sopenharmony_ci .map = tps6586x_irq_map, 30262306a36Sopenharmony_ci .xlate = irq_domain_xlate_twocell, 30362306a36Sopenharmony_ci}; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic irqreturn_t tps6586x_irq(int irq, void *data) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct tps6586x *tps6586x = data; 30862306a36Sopenharmony_ci uint32_t acks; 30962306a36Sopenharmony_ci __le32 val; 31062306a36Sopenharmony_ci int ret = 0; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci ret = tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, 31362306a36Sopenharmony_ci sizeof(acks), (uint8_t *)&val); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (ret < 0) { 31662306a36Sopenharmony_ci dev_err(tps6586x->dev, "failed to read interrupt status\n"); 31762306a36Sopenharmony_ci return IRQ_NONE; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci acks = le32_to_cpu(val); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci while (acks) { 32362306a36Sopenharmony_ci int i = __ffs(acks); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (tps6586x->irq_en & (1 << i)) 32662306a36Sopenharmony_ci handle_nested_irq( 32762306a36Sopenharmony_ci irq_find_mapping(tps6586x->irq_domain, i)); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci acks &= ~(1 << i); 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return IRQ_HANDLED; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int tps6586x_irq_init(struct tps6586x *tps6586x, int irq, 33662306a36Sopenharmony_ci int irq_base) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci int i, ret; 33962306a36Sopenharmony_ci u8 tmp[4]; 34062306a36Sopenharmony_ci int new_irq_base; 34162306a36Sopenharmony_ci int irq_num = ARRAY_SIZE(tps6586x_irqs); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci tps6586x->irq = irq; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci mutex_init(&tps6586x->irq_lock); 34662306a36Sopenharmony_ci for (i = 0; i < 5; i++) { 34762306a36Sopenharmony_ci tps6586x->mask_reg[i] = 0xff; 34862306a36Sopenharmony_ci tps6586x_write(tps6586x->dev, TPS6586X_INT_MASK1 + i, 0xff); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, sizeof(tmp), tmp); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (irq_base > 0) { 35462306a36Sopenharmony_ci new_irq_base = irq_alloc_descs(irq_base, 0, irq_num, -1); 35562306a36Sopenharmony_ci if (new_irq_base < 0) { 35662306a36Sopenharmony_ci dev_err(tps6586x->dev, 35762306a36Sopenharmony_ci "Failed to alloc IRQs: %d\n", new_irq_base); 35862306a36Sopenharmony_ci return new_irq_base; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci } else { 36162306a36Sopenharmony_ci new_irq_base = 0; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci tps6586x->irq_domain = irq_domain_add_simple(tps6586x->dev->of_node, 36562306a36Sopenharmony_ci irq_num, new_irq_base, &tps6586x_domain_ops, 36662306a36Sopenharmony_ci tps6586x); 36762306a36Sopenharmony_ci if (!tps6586x->irq_domain) { 36862306a36Sopenharmony_ci dev_err(tps6586x->dev, "Failed to create IRQ domain\n"); 36962306a36Sopenharmony_ci return -ENOMEM; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci ret = request_threaded_irq(irq, NULL, tps6586x_irq, IRQF_ONESHOT, 37262306a36Sopenharmony_ci "tps6586x", tps6586x); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (!ret) 37562306a36Sopenharmony_ci device_init_wakeup(tps6586x->dev, 1); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return ret; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic int tps6586x_add_subdevs(struct tps6586x *tps6586x, 38162306a36Sopenharmony_ci struct tps6586x_platform_data *pdata) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct tps6586x_subdev_info *subdev; 38462306a36Sopenharmony_ci struct platform_device *pdev; 38562306a36Sopenharmony_ci int i, ret = 0; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci for (i = 0; i < pdata->num_subdevs; i++) { 38862306a36Sopenharmony_ci subdev = &pdata->subdevs[i]; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci pdev = platform_device_alloc(subdev->name, subdev->id); 39162306a36Sopenharmony_ci if (!pdev) { 39262306a36Sopenharmony_ci ret = -ENOMEM; 39362306a36Sopenharmony_ci goto failed; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci pdev->dev.parent = tps6586x->dev; 39762306a36Sopenharmony_ci pdev->dev.platform_data = subdev->platform_data; 39862306a36Sopenharmony_ci pdev->dev.of_node = subdev->of_node; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci ret = platform_device_add(pdev); 40162306a36Sopenharmony_ci if (ret) { 40262306a36Sopenharmony_ci platform_device_put(pdev); 40362306a36Sopenharmony_ci goto failed; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cifailed: 40962306a36Sopenharmony_ci tps6586x_remove_subdevs(tps6586x); 41062306a36Sopenharmony_ci return ret; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci#ifdef CONFIG_OF 41462306a36Sopenharmony_cistatic struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *client) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci struct device_node *np = client->dev.of_node; 41762306a36Sopenharmony_ci struct tps6586x_platform_data *pdata; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); 42062306a36Sopenharmony_ci if (!pdata) 42162306a36Sopenharmony_ci return NULL; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci pdata->num_subdevs = 0; 42462306a36Sopenharmony_ci pdata->subdevs = NULL; 42562306a36Sopenharmony_ci pdata->gpio_base = -1; 42662306a36Sopenharmony_ci pdata->irq_base = -1; 42762306a36Sopenharmony_ci pdata->pm_off = of_property_read_bool(np, "ti,system-power-controller"); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return pdata; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic const struct of_device_id tps6586x_of_match[] = { 43362306a36Sopenharmony_ci { .compatible = "ti,tps6586x", }, 43462306a36Sopenharmony_ci { }, 43562306a36Sopenharmony_ci}; 43662306a36Sopenharmony_ci#else 43762306a36Sopenharmony_cistatic struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *client) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci return NULL; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci#endif 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic bool is_volatile_reg(struct device *dev, unsigned int reg) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci /* Cache all interrupt mask register */ 44662306a36Sopenharmony_ci if ((reg >= TPS6586X_INT_MASK1) && (reg <= TPS6586X_INT_MASK5)) 44762306a36Sopenharmony_ci return false; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return true; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic const struct regmap_config tps6586x_regmap_config = { 45362306a36Sopenharmony_ci .reg_bits = 8, 45462306a36Sopenharmony_ci .val_bits = 8, 45562306a36Sopenharmony_ci .max_register = TPS6586X_MAX_REGISTER, 45662306a36Sopenharmony_ci .volatile_reg = is_volatile_reg, 45762306a36Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 45862306a36Sopenharmony_ci}; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic struct device *tps6586x_dev; 46162306a36Sopenharmony_cistatic void tps6586x_power_off(void) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci if (tps6586x_clr_bits(tps6586x_dev, TPS6586X_SUPPLYENE, EXITSLREQ_BIT)) 46462306a36Sopenharmony_ci return; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci tps6586x_set_bits(tps6586x_dev, TPS6586X_SUPPLYENE, SLEEP_MODE_BIT); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic void tps6586x_print_version(struct i2c_client *client, int version) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci const char *name; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci switch (version) { 47462306a36Sopenharmony_ci case TPS658621A: 47562306a36Sopenharmony_ci name = "TPS658621A"; 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci case TPS658621CD: 47862306a36Sopenharmony_ci name = "TPS658621C/D"; 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci case TPS658623: 48162306a36Sopenharmony_ci name = "TPS658623"; 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci case TPS658640: 48462306a36Sopenharmony_ci case TPS658640v2: 48562306a36Sopenharmony_ci name = "TPS658640"; 48662306a36Sopenharmony_ci break; 48762306a36Sopenharmony_ci case TPS658643: 48862306a36Sopenharmony_ci name = "TPS658643"; 48962306a36Sopenharmony_ci break; 49062306a36Sopenharmony_ci default: 49162306a36Sopenharmony_ci name = "TPS6586X"; 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci dev_info(&client->dev, "Found %s, VERSIONCRC is %02x\n", name, version); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic int tps6586x_i2c_probe(struct i2c_client *client) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct tps6586x_platform_data *pdata = dev_get_platdata(&client->dev); 50162306a36Sopenharmony_ci struct tps6586x *tps6586x; 50262306a36Sopenharmony_ci int ret; 50362306a36Sopenharmony_ci int version; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (!pdata && client->dev.of_node) 50662306a36Sopenharmony_ci pdata = tps6586x_parse_dt(client); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (!pdata) { 50962306a36Sopenharmony_ci dev_err(&client->dev, "tps6586x requires platform data\n"); 51062306a36Sopenharmony_ci return -ENOTSUPP; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci version = i2c_smbus_read_byte_data(client, TPS6586X_VERSIONCRC); 51462306a36Sopenharmony_ci if (version < 0) { 51562306a36Sopenharmony_ci dev_err(&client->dev, "Chip ID read failed: %d\n", version); 51662306a36Sopenharmony_ci return -EIO; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci tps6586x = devm_kzalloc(&client->dev, sizeof(*tps6586x), GFP_KERNEL); 52062306a36Sopenharmony_ci if (!tps6586x) 52162306a36Sopenharmony_ci return -ENOMEM; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci tps6586x->version = version; 52462306a36Sopenharmony_ci tps6586x_print_version(client, tps6586x->version); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci tps6586x->client = client; 52762306a36Sopenharmony_ci tps6586x->dev = &client->dev; 52862306a36Sopenharmony_ci i2c_set_clientdata(client, tps6586x); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci tps6586x->regmap = devm_regmap_init_i2c(client, 53162306a36Sopenharmony_ci &tps6586x_regmap_config); 53262306a36Sopenharmony_ci if (IS_ERR(tps6586x->regmap)) { 53362306a36Sopenharmony_ci ret = PTR_ERR(tps6586x->regmap); 53462306a36Sopenharmony_ci dev_err(&client->dev, "regmap init failed: %d\n", ret); 53562306a36Sopenharmony_ci return ret; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (client->irq) { 54062306a36Sopenharmony_ci ret = tps6586x_irq_init(tps6586x, client->irq, 54162306a36Sopenharmony_ci pdata->irq_base); 54262306a36Sopenharmony_ci if (ret) { 54362306a36Sopenharmony_ci dev_err(&client->dev, "IRQ init failed: %d\n", ret); 54462306a36Sopenharmony_ci return ret; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci ret = mfd_add_devices(tps6586x->dev, -1, 54962306a36Sopenharmony_ci tps6586x_cell, ARRAY_SIZE(tps6586x_cell), 55062306a36Sopenharmony_ci NULL, 0, tps6586x->irq_domain); 55162306a36Sopenharmony_ci if (ret < 0) { 55262306a36Sopenharmony_ci dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret); 55362306a36Sopenharmony_ci goto err_mfd_add; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci ret = tps6586x_add_subdevs(tps6586x, pdata); 55762306a36Sopenharmony_ci if (ret) { 55862306a36Sopenharmony_ci dev_err(&client->dev, "add devices failed: %d\n", ret); 55962306a36Sopenharmony_ci goto err_add_devs; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (pdata->pm_off && !pm_power_off) { 56362306a36Sopenharmony_ci tps6586x_dev = &client->dev; 56462306a36Sopenharmony_ci pm_power_off = tps6586x_power_off; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return 0; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cierr_add_devs: 57062306a36Sopenharmony_ci mfd_remove_devices(tps6586x->dev); 57162306a36Sopenharmony_cierr_mfd_add: 57262306a36Sopenharmony_ci if (client->irq) 57362306a36Sopenharmony_ci free_irq(client->irq, tps6586x); 57462306a36Sopenharmony_ci return ret; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic void tps6586x_i2c_remove(struct i2c_client *client) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct tps6586x *tps6586x = i2c_get_clientdata(client); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci tps6586x_remove_subdevs(tps6586x); 58262306a36Sopenharmony_ci mfd_remove_devices(tps6586x->dev); 58362306a36Sopenharmony_ci if (client->irq) 58462306a36Sopenharmony_ci free_irq(client->irq, tps6586x); 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic int __maybe_unused tps6586x_i2c_suspend(struct device *dev) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci struct tps6586x *tps6586x = dev_get_drvdata(dev); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (tps6586x->client->irq) 59262306a36Sopenharmony_ci disable_irq(tps6586x->client->irq); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic int __maybe_unused tps6586x_i2c_resume(struct device *dev) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct tps6586x *tps6586x = dev_get_drvdata(dev); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (tps6586x->client->irq) 60262306a36Sopenharmony_ci enable_irq(tps6586x->client->irq); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return 0; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tps6586x_pm_ops, tps6586x_i2c_suspend, 60862306a36Sopenharmony_ci tps6586x_i2c_resume); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic const struct i2c_device_id tps6586x_id_table[] = { 61162306a36Sopenharmony_ci { "tps6586x", 0 }, 61262306a36Sopenharmony_ci { }, 61362306a36Sopenharmony_ci}; 61462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tps6586x_id_table); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic struct i2c_driver tps6586x_driver = { 61762306a36Sopenharmony_ci .driver = { 61862306a36Sopenharmony_ci .name = "tps6586x", 61962306a36Sopenharmony_ci .of_match_table = of_match_ptr(tps6586x_of_match), 62062306a36Sopenharmony_ci .pm = &tps6586x_pm_ops, 62162306a36Sopenharmony_ci }, 62262306a36Sopenharmony_ci .probe = tps6586x_i2c_probe, 62362306a36Sopenharmony_ci .remove = tps6586x_i2c_remove, 62462306a36Sopenharmony_ci .id_table = tps6586x_id_table, 62562306a36Sopenharmony_ci}; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic int __init tps6586x_init(void) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci return i2c_add_driver(&tps6586x_driver); 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_cisubsys_initcall(tps6586x_init); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic void __exit tps6586x_exit(void) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci i2c_del_driver(&tps6586x_driver); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_cimodule_exit(tps6586x_exit); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ciMODULE_DESCRIPTION("TPS6586X core driver"); 64062306a36Sopenharmony_ciMODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); 641