162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Intel CHT Whiskey Cove PMIC I2C Master driver
462306a36Sopenharmony_ci * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Based on various non upstream patches to support the CHT Whiskey Cove PMIC:
762306a36Sopenharmony_ci * Copyright (C) 2011 - 2014 Intel Corporation. All rights reserved.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/acpi.h>
1162306a36Sopenharmony_ci#include <linux/completion.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/i2c.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/irq.h>
1662306a36Sopenharmony_ci#include <linux/irqdomain.h>
1762306a36Sopenharmony_ci#include <linux/mfd/intel_soc_pmic.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/power/bq24190_charger.h>
2162306a36Sopenharmony_ci#include <linux/power/bq25890_charger.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define CHT_WC_I2C_CTRL			0x5e24
2562306a36Sopenharmony_ci#define CHT_WC_I2C_CTRL_WR		BIT(0)
2662306a36Sopenharmony_ci#define CHT_WC_I2C_CTRL_RD		BIT(1)
2762306a36Sopenharmony_ci#define CHT_WC_I2C_CLIENT_ADDR		0x5e25
2862306a36Sopenharmony_ci#define CHT_WC_I2C_REG_OFFSET		0x5e26
2962306a36Sopenharmony_ci#define CHT_WC_I2C_WRDATA		0x5e27
3062306a36Sopenharmony_ci#define CHT_WC_I2C_RDDATA		0x5e28
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define CHT_WC_EXTCHGRIRQ		0x6e0a
3362306a36Sopenharmony_ci#define CHT_WC_EXTCHGRIRQ_CLIENT_IRQ	BIT(0)
3462306a36Sopenharmony_ci#define CHT_WC_EXTCHGRIRQ_WRITE_IRQ	BIT(1)
3562306a36Sopenharmony_ci#define CHT_WC_EXTCHGRIRQ_READ_IRQ	BIT(2)
3662306a36Sopenharmony_ci#define CHT_WC_EXTCHGRIRQ_NACK_IRQ	BIT(3)
3762306a36Sopenharmony_ci#define CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK	((u8)GENMASK(3, 1))
3862306a36Sopenharmony_ci#define CHT_WC_EXTCHGRIRQ_MSK		0x6e17
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct cht_wc_i2c_adap {
4162306a36Sopenharmony_ci	struct i2c_adapter adapter;
4262306a36Sopenharmony_ci	wait_queue_head_t wait;
4362306a36Sopenharmony_ci	struct irq_chip irqchip;
4462306a36Sopenharmony_ci	struct mutex adap_lock;
4562306a36Sopenharmony_ci	struct mutex irqchip_lock;
4662306a36Sopenharmony_ci	struct regmap *regmap;
4762306a36Sopenharmony_ci	struct irq_domain *irq_domain;
4862306a36Sopenharmony_ci	struct i2c_client *client;
4962306a36Sopenharmony_ci	int client_irq;
5062306a36Sopenharmony_ci	u8 irq_mask;
5162306a36Sopenharmony_ci	u8 old_irq_mask;
5262306a36Sopenharmony_ci	int read_data;
5362306a36Sopenharmony_ci	bool io_error;
5462306a36Sopenharmony_ci	bool done;
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic irqreturn_t cht_wc_i2c_adap_thread_handler(int id, void *data)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct cht_wc_i2c_adap *adap = data;
6062306a36Sopenharmony_ci	int ret, reg;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	mutex_lock(&adap->adap_lock);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* Read IRQs */
6562306a36Sopenharmony_ci	ret = regmap_read(adap->regmap, CHT_WC_EXTCHGRIRQ, &reg);
6662306a36Sopenharmony_ci	if (ret) {
6762306a36Sopenharmony_ci		dev_err(&adap->adapter.dev, "Error reading extchgrirq reg\n");
6862306a36Sopenharmony_ci		mutex_unlock(&adap->adap_lock);
6962306a36Sopenharmony_ci		return IRQ_NONE;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	reg &= ~adap->irq_mask;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* Reads must be acked after reading the received data. */
7562306a36Sopenharmony_ci	ret = regmap_read(adap->regmap, CHT_WC_I2C_RDDATA, &adap->read_data);
7662306a36Sopenharmony_ci	if (ret)
7762306a36Sopenharmony_ci		adap->io_error = true;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/*
8062306a36Sopenharmony_ci	 * Immediately ack IRQs, so that if new IRQs arrives while we're
8162306a36Sopenharmony_ci	 * handling the previous ones our irq will re-trigger when we're done.
8262306a36Sopenharmony_ci	 */
8362306a36Sopenharmony_ci	ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ, reg);
8462306a36Sopenharmony_ci	if (ret)
8562306a36Sopenharmony_ci		dev_err(&adap->adapter.dev, "Error writing extchgrirq reg\n");
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (reg & CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK) {
8862306a36Sopenharmony_ci		adap->io_error |= !!(reg & CHT_WC_EXTCHGRIRQ_NACK_IRQ);
8962306a36Sopenharmony_ci		adap->done = true;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	mutex_unlock(&adap->adap_lock);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (reg & CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK)
9562306a36Sopenharmony_ci		wake_up(&adap->wait);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/*
9862306a36Sopenharmony_ci	 * Do NOT use handle_nested_irq here, the client irq handler will
9962306a36Sopenharmony_ci	 * likely want to do i2c transfers and the i2c controller uses this
10062306a36Sopenharmony_ci	 * interrupt handler as well, so running the client irq handler from
10162306a36Sopenharmony_ci	 * this thread will cause things to lock up.
10262306a36Sopenharmony_ci	 */
10362306a36Sopenharmony_ci	if (reg & CHT_WC_EXTCHGRIRQ_CLIENT_IRQ)
10462306a36Sopenharmony_ci		generic_handle_irq_safe(adap->client_irq);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return IRQ_HANDLED;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic u32 cht_wc_i2c_adap_master_func(struct i2c_adapter *adap)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	/* This i2c adapter only supports SMBUS byte transfers */
11262306a36Sopenharmony_ci	return I2C_FUNC_SMBUS_BYTE_DATA;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic int cht_wc_i2c_adap_smbus_xfer(struct i2c_adapter *_adap, u16 addr,
11662306a36Sopenharmony_ci				      unsigned short flags, char read_write,
11762306a36Sopenharmony_ci				      u8 command, int size,
11862306a36Sopenharmony_ci				      union i2c_smbus_data *data)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct cht_wc_i2c_adap *adap = i2c_get_adapdata(_adap);
12162306a36Sopenharmony_ci	int ret;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	mutex_lock(&adap->adap_lock);
12462306a36Sopenharmony_ci	adap->io_error = false;
12562306a36Sopenharmony_ci	adap->done = false;
12662306a36Sopenharmony_ci	mutex_unlock(&adap->adap_lock);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	ret = regmap_write(adap->regmap, CHT_WC_I2C_CLIENT_ADDR, addr);
12962306a36Sopenharmony_ci	if (ret)
13062306a36Sopenharmony_ci		return ret;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (read_write == I2C_SMBUS_WRITE) {
13362306a36Sopenharmony_ci		ret = regmap_write(adap->regmap, CHT_WC_I2C_WRDATA, data->byte);
13462306a36Sopenharmony_ci		if (ret)
13562306a36Sopenharmony_ci			return ret;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	ret = regmap_write(adap->regmap, CHT_WC_I2C_REG_OFFSET, command);
13962306a36Sopenharmony_ci	if (ret)
14062306a36Sopenharmony_ci		return ret;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	ret = regmap_write(adap->regmap, CHT_WC_I2C_CTRL,
14362306a36Sopenharmony_ci			   (read_write == I2C_SMBUS_WRITE) ?
14462306a36Sopenharmony_ci			   CHT_WC_I2C_CTRL_WR : CHT_WC_I2C_CTRL_RD);
14562306a36Sopenharmony_ci	if (ret)
14662306a36Sopenharmony_ci		return ret;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	ret = wait_event_timeout(adap->wait, adap->done, msecs_to_jiffies(30));
14962306a36Sopenharmony_ci	if (ret == 0) {
15062306a36Sopenharmony_ci		/*
15162306a36Sopenharmony_ci		 * The CHT GPIO controller serializes all IRQs, sometimes
15262306a36Sopenharmony_ci		 * causing significant delays, check status manually.
15362306a36Sopenharmony_ci		 */
15462306a36Sopenharmony_ci		cht_wc_i2c_adap_thread_handler(0, adap);
15562306a36Sopenharmony_ci		if (!adap->done)
15662306a36Sopenharmony_ci			return -ETIMEDOUT;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	ret = 0;
16062306a36Sopenharmony_ci	mutex_lock(&adap->adap_lock);
16162306a36Sopenharmony_ci	if (adap->io_error)
16262306a36Sopenharmony_ci		ret = -EIO;
16362306a36Sopenharmony_ci	else if (read_write == I2C_SMBUS_READ)
16462306a36Sopenharmony_ci		data->byte = adap->read_data;
16562306a36Sopenharmony_ci	mutex_unlock(&adap->adap_lock);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	return ret;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic const struct i2c_algorithm cht_wc_i2c_adap_algo = {
17162306a36Sopenharmony_ci	.functionality = cht_wc_i2c_adap_master_func,
17262306a36Sopenharmony_ci	.smbus_xfer = cht_wc_i2c_adap_smbus_xfer,
17362306a36Sopenharmony_ci};
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/*
17662306a36Sopenharmony_ci * We are an i2c-adapter which itself is part of an i2c-client. This means that
17762306a36Sopenharmony_ci * transfers done through us take adapter->bus_lock twice, once for our parent
17862306a36Sopenharmony_ci * i2c-adapter and once to take our own bus_lock. Lockdep does not like this
17962306a36Sopenharmony_ci * nested locking, to make lockdep happy in the case of busses with muxes, the
18062306a36Sopenharmony_ci * i2c-core's i2c_adapter_lock_bus function calls:
18162306a36Sopenharmony_ci * rt_mutex_lock_nested(&adapter->bus_lock, i2c_adapter_depth(adapter));
18262306a36Sopenharmony_ci *
18362306a36Sopenharmony_ci * But i2c_adapter_depth only works when the direct parent of the adapter is
18462306a36Sopenharmony_ci * another adapter, as it is only meant for muxes. In our case there is an
18562306a36Sopenharmony_ci * i2c-client and MFD instantiated platform_device in the parent->child chain
18662306a36Sopenharmony_ci * between the 2 devices.
18762306a36Sopenharmony_ci *
18862306a36Sopenharmony_ci * So we override the default i2c_lock_operations and pass a hardcoded
18962306a36Sopenharmony_ci * depth of 1 to rt_mutex_lock_nested, to make lockdep happy.
19062306a36Sopenharmony_ci *
19162306a36Sopenharmony_ci * Note that if there were to be a mux attached to our adapter, this would
19262306a36Sopenharmony_ci * break things again since the i2c-mux code expects the root-adapter to have
19362306a36Sopenharmony_ci * a locking depth of 0. But we always have only 1 client directly attached
19462306a36Sopenharmony_ci * in the form of the Charger IC paired with the CHT Whiskey Cove PMIC.
19562306a36Sopenharmony_ci */
19662306a36Sopenharmony_cistatic void cht_wc_i2c_adap_lock_bus(struct i2c_adapter *adapter,
19762306a36Sopenharmony_ci				 unsigned int flags)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	rt_mutex_lock_nested(&adapter->bus_lock, 1);
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int cht_wc_i2c_adap_trylock_bus(struct i2c_adapter *adapter,
20362306a36Sopenharmony_ci				   unsigned int flags)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	return rt_mutex_trylock(&adapter->bus_lock);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic void cht_wc_i2c_adap_unlock_bus(struct i2c_adapter *adapter,
20962306a36Sopenharmony_ci				   unsigned int flags)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	rt_mutex_unlock(&adapter->bus_lock);
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic const struct i2c_lock_operations cht_wc_i2c_adap_lock_ops = {
21562306a36Sopenharmony_ci	.lock_bus =    cht_wc_i2c_adap_lock_bus,
21662306a36Sopenharmony_ci	.trylock_bus = cht_wc_i2c_adap_trylock_bus,
21762306a36Sopenharmony_ci	.unlock_bus =  cht_wc_i2c_adap_unlock_bus,
21862306a36Sopenharmony_ci};
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci/**** irqchip for the client connected to the extchgr i2c adapter ****/
22162306a36Sopenharmony_cistatic void cht_wc_i2c_irq_lock(struct irq_data *data)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	mutex_lock(&adap->irqchip_lock);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic void cht_wc_i2c_irq_sync_unlock(struct irq_data *data)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data);
23162306a36Sopenharmony_ci	int ret;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (adap->irq_mask != adap->old_irq_mask) {
23462306a36Sopenharmony_ci		ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ_MSK,
23562306a36Sopenharmony_ci				   adap->irq_mask);
23662306a36Sopenharmony_ci		if (ret == 0)
23762306a36Sopenharmony_ci			adap->old_irq_mask = adap->irq_mask;
23862306a36Sopenharmony_ci		else
23962306a36Sopenharmony_ci			dev_err(&adap->adapter.dev, "Error writing EXTCHGRIRQ_MSK\n");
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	mutex_unlock(&adap->irqchip_lock);
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic void cht_wc_i2c_irq_enable(struct irq_data *data)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	adap->irq_mask &= ~CHT_WC_EXTCHGRIRQ_CLIENT_IRQ;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic void cht_wc_i2c_irq_disable(struct irq_data *data)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct cht_wc_i2c_adap *adap = irq_data_get_irq_chip_data(data);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	adap->irq_mask |= CHT_WC_EXTCHGRIRQ_CLIENT_IRQ;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic const struct irq_chip cht_wc_i2c_irq_chip = {
26062306a36Sopenharmony_ci	.irq_bus_lock		= cht_wc_i2c_irq_lock,
26162306a36Sopenharmony_ci	.irq_bus_sync_unlock	= cht_wc_i2c_irq_sync_unlock,
26262306a36Sopenharmony_ci	.irq_disable		= cht_wc_i2c_irq_disable,
26362306a36Sopenharmony_ci	.irq_enable		= cht_wc_i2c_irq_enable,
26462306a36Sopenharmony_ci	.name			= "cht_wc_ext_chrg_irq_chip",
26562306a36Sopenharmony_ci};
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci/********** GPD Win / Pocket charger IC settings **********/
26862306a36Sopenharmony_cistatic const char * const bq24190_suppliers[] = {
26962306a36Sopenharmony_ci	"tcpm-source-psy-i2c-fusb302" };
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic const struct property_entry bq24190_props[] = {
27262306a36Sopenharmony_ci	PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_suppliers),
27362306a36Sopenharmony_ci	PROPERTY_ENTRY_BOOL("omit-battery-class"),
27462306a36Sopenharmony_ci	PROPERTY_ENTRY_BOOL("disable-reset"),
27562306a36Sopenharmony_ci	{ }
27662306a36Sopenharmony_ci};
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic const struct software_node bq24190_node = {
27962306a36Sopenharmony_ci	.properties = bq24190_props,
28062306a36Sopenharmony_ci};
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic struct regulator_consumer_supply fusb302_consumer = {
28362306a36Sopenharmony_ci	.supply = "vbus",
28462306a36Sopenharmony_ci	/* Must match fusb302 dev_name in intel_cht_int33fe.c */
28562306a36Sopenharmony_ci	.dev_name = "i2c-fusb302",
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic const struct regulator_init_data bq24190_vbus_init_data = {
28962306a36Sopenharmony_ci	.constraints = {
29062306a36Sopenharmony_ci		/* The name is used in intel_cht_int33fe.c do not change. */
29162306a36Sopenharmony_ci		.name = "cht_wc_usb_typec_vbus",
29262306a36Sopenharmony_ci		.valid_ops_mask = REGULATOR_CHANGE_STATUS,
29362306a36Sopenharmony_ci	},
29462306a36Sopenharmony_ci	.consumer_supplies = &fusb302_consumer,
29562306a36Sopenharmony_ci	.num_consumer_supplies = 1,
29662306a36Sopenharmony_ci};
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic struct bq24190_platform_data bq24190_pdata = {
29962306a36Sopenharmony_ci	.regulator_init_data = &bq24190_vbus_init_data,
30062306a36Sopenharmony_ci};
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic struct i2c_board_info gpd_win_board_info = {
30362306a36Sopenharmony_ci	.type = "bq24190",
30462306a36Sopenharmony_ci	.addr = 0x6b,
30562306a36Sopenharmony_ci	.dev_name = "bq24190",
30662306a36Sopenharmony_ci	.swnode = &bq24190_node,
30762306a36Sopenharmony_ci	.platform_data = &bq24190_pdata,
30862306a36Sopenharmony_ci};
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci/********** Xiaomi Mi Pad 2 charger IC settings  **********/
31162306a36Sopenharmony_cistatic struct regulator_consumer_supply bq2589x_vbus_consumer = {
31262306a36Sopenharmony_ci	.supply = "vbus",
31362306a36Sopenharmony_ci	.dev_name = "cht_wcove_pwrsrc",
31462306a36Sopenharmony_ci};
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic const struct regulator_init_data bq2589x_vbus_init_data = {
31762306a36Sopenharmony_ci	.constraints = {
31862306a36Sopenharmony_ci		.valid_ops_mask = REGULATOR_CHANGE_STATUS,
31962306a36Sopenharmony_ci	},
32062306a36Sopenharmony_ci	.consumer_supplies = &bq2589x_vbus_consumer,
32162306a36Sopenharmony_ci	.num_consumer_supplies = 1,
32262306a36Sopenharmony_ci};
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic struct bq25890_platform_data bq2589x_pdata = {
32562306a36Sopenharmony_ci	.regulator_init_data = &bq2589x_vbus_init_data,
32662306a36Sopenharmony_ci};
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic const struct property_entry xiaomi_mipad2_props[] = {
32962306a36Sopenharmony_ci	PROPERTY_ENTRY_BOOL("linux,skip-reset"),
33062306a36Sopenharmony_ci	PROPERTY_ENTRY_BOOL("linux,read-back-settings"),
33162306a36Sopenharmony_ci	{ }
33262306a36Sopenharmony_ci};
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic const struct software_node xiaomi_mipad2_node = {
33562306a36Sopenharmony_ci	.properties = xiaomi_mipad2_props,
33662306a36Sopenharmony_ci};
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic struct i2c_board_info xiaomi_mipad2_board_info = {
33962306a36Sopenharmony_ci	.type = "bq25890",
34062306a36Sopenharmony_ci	.addr = 0x6a,
34162306a36Sopenharmony_ci	.dev_name = "bq25890",
34262306a36Sopenharmony_ci	.swnode = &xiaomi_mipad2_node,
34362306a36Sopenharmony_ci	.platform_data = &bq2589x_pdata,
34462306a36Sopenharmony_ci};
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci/********** Lenovo Yogabook YB1-X90F/-X91F/-X91L charger settings **********/
34762306a36Sopenharmony_cistatic const char * const lenovo_yb1_bq25892_suppliers[] = { "cht_wcove_pwrsrc" };
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic const struct property_entry lenovo_yb1_bq25892_props[] = {
35062306a36Sopenharmony_ci	PROPERTY_ENTRY_STRING_ARRAY("supplied-from",
35162306a36Sopenharmony_ci				    lenovo_yb1_bq25892_suppliers),
35262306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("linux,pump-express-vbus-max", 12000000),
35362306a36Sopenharmony_ci	PROPERTY_ENTRY_BOOL("linux,skip-reset"),
35462306a36Sopenharmony_ci	/*
35562306a36Sopenharmony_ci	 * The firmware sets everything to the defaults, which leads to a
35662306a36Sopenharmony_ci	 * somewhat low charge-current of 2048mA and worse to a battery-voltage
35762306a36Sopenharmony_ci	 * of 4.2V instead of 4.35V (when booted without a charger connected).
35862306a36Sopenharmony_ci	 * Use our own values instead of "linux,read-back-settings" to fix this.
35962306a36Sopenharmony_ci	 */
36062306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("ti,charge-current", 4224000),
36162306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("ti,battery-regulation-voltage", 4352000),
36262306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("ti,termination-current", 256000),
36362306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("ti,precharge-current", 128000),
36462306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("ti,minimum-sys-voltage", 3500000),
36562306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("ti,boost-voltage", 4998000),
36662306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("ti,boost-max-current", 1400000),
36762306a36Sopenharmony_ci	PROPERTY_ENTRY_BOOL("ti,use-ilim-pin"),
36862306a36Sopenharmony_ci	{ }
36962306a36Sopenharmony_ci};
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic const struct software_node lenovo_yb1_bq25892_node = {
37262306a36Sopenharmony_ci	.properties = lenovo_yb1_bq25892_props,
37362306a36Sopenharmony_ci};
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic struct i2c_board_info lenovo_yogabook1_board_info = {
37662306a36Sopenharmony_ci	.type = "bq25892",
37762306a36Sopenharmony_ci	.addr = 0x6b,
37862306a36Sopenharmony_ci	.dev_name = "bq25892",
37962306a36Sopenharmony_ci	.swnode = &lenovo_yb1_bq25892_node,
38062306a36Sopenharmony_ci	.platform_data = &bq2589x_pdata,
38162306a36Sopenharmony_ci};
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci/********** Lenovo Yogabook YT3-X90F charger settings **********/
38462306a36Sopenharmony_cistatic const char * const lenovo_yt3_bq25892_1_suppliers[] = { "cht_wcove_pwrsrc" };
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci/*
38762306a36Sopenharmony_ci * bq25892 charger settings for the round li-ion cells in the hinge,
38862306a36Sopenharmony_ci * this is the main / biggest battery.
38962306a36Sopenharmony_ci */
39062306a36Sopenharmony_cistatic const struct property_entry lenovo_yt3_bq25892_1_props[] = {
39162306a36Sopenharmony_ci	PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lenovo_yt3_bq25892_1_suppliers),
39262306a36Sopenharmony_ci	PROPERTY_ENTRY_STRING("linux,secondary-charger-name", "bq25890-charger-0"),
39362306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("linux,iinlim-percentage", 60),
39462306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("linux,pump-express-vbus-max", 12000000),
39562306a36Sopenharmony_ci	PROPERTY_ENTRY_BOOL("linux,skip-reset"),
39662306a36Sopenharmony_ci	/*
39762306a36Sopenharmony_ci	 * The firmware sets everything to the defaults, leading to a low(ish)
39862306a36Sopenharmony_ci	 * charge-current and battery-voltage of 2048mA resp 4.2V. Use the
39962306a36Sopenharmony_ci	 * Android values instead of "linux,read-back-settings" to fix this.
40062306a36Sopenharmony_ci	 */
40162306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("ti,charge-current", 3072000),
40262306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("ti,battery-regulation-voltage", 4352000),
40362306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("ti,termination-current", 128000),
40462306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("ti,precharge-current", 128000),
40562306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("ti,minimum-sys-voltage", 3700000),
40662306a36Sopenharmony_ci	PROPERTY_ENTRY_BOOL("ti,use-ilim-pin"),
40762306a36Sopenharmony_ci	/* Set 5V boost current-limit to 1.2A (MAX/POR values are 2.45A/1.4A) */
40862306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("ti,boost-voltage", 4998000),
40962306a36Sopenharmony_ci	PROPERTY_ENTRY_U32("ti,boost-max-current", 1200000),
41062306a36Sopenharmony_ci	{ }
41162306a36Sopenharmony_ci};
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic const struct software_node lenovo_yt3_bq25892_1_node = {
41462306a36Sopenharmony_ci	.properties = lenovo_yt3_bq25892_1_props,
41562306a36Sopenharmony_ci};
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci/* bq25892 charger for the round li-ion cells in the hinge */
41862306a36Sopenharmony_cistatic struct i2c_board_info lenovo_yoga_tab3_board_info = {
41962306a36Sopenharmony_ci	.type = "bq25892",
42062306a36Sopenharmony_ci	.addr = 0x6b,
42162306a36Sopenharmony_ci	.dev_name = "bq25892_1",
42262306a36Sopenharmony_ci	.swnode = &lenovo_yt3_bq25892_1_node,
42362306a36Sopenharmony_ci	.platform_data = &bq2589x_pdata,
42462306a36Sopenharmony_ci};
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
42962306a36Sopenharmony_ci	struct i2c_board_info *board_info = NULL;
43062306a36Sopenharmony_ci	struct cht_wc_i2c_adap *adap;
43162306a36Sopenharmony_ci	int ret, reg, irq;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
43462306a36Sopenharmony_ci	if (irq < 0)
43562306a36Sopenharmony_ci		return irq;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	adap = devm_kzalloc(&pdev->dev, sizeof(*adap), GFP_KERNEL);
43862306a36Sopenharmony_ci	if (!adap)
43962306a36Sopenharmony_ci		return -ENOMEM;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	init_waitqueue_head(&adap->wait);
44262306a36Sopenharmony_ci	mutex_init(&adap->adap_lock);
44362306a36Sopenharmony_ci	mutex_init(&adap->irqchip_lock);
44462306a36Sopenharmony_ci	adap->irqchip = cht_wc_i2c_irq_chip;
44562306a36Sopenharmony_ci	adap->regmap = pmic->regmap;
44662306a36Sopenharmony_ci	adap->adapter.owner = THIS_MODULE;
44762306a36Sopenharmony_ci	adap->adapter.class = I2C_CLASS_HWMON;
44862306a36Sopenharmony_ci	adap->adapter.algo = &cht_wc_i2c_adap_algo;
44962306a36Sopenharmony_ci	adap->adapter.lock_ops = &cht_wc_i2c_adap_lock_ops;
45062306a36Sopenharmony_ci	strscpy(adap->adapter.name, "PMIC I2C Adapter",
45162306a36Sopenharmony_ci		sizeof(adap->adapter.name));
45262306a36Sopenharmony_ci	adap->adapter.dev.parent = &pdev->dev;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/* Clear and activate i2c-adapter interrupts, disable client IRQ */
45562306a36Sopenharmony_ci	adap->old_irq_mask = adap->irq_mask = ~CHT_WC_EXTCHGRIRQ_ADAP_IRQMASK;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	ret = regmap_read(adap->regmap, CHT_WC_I2C_RDDATA, &reg);
45862306a36Sopenharmony_ci	if (ret)
45962306a36Sopenharmony_ci		return ret;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ, ~adap->irq_mask);
46262306a36Sopenharmony_ci	if (ret)
46362306a36Sopenharmony_ci		return ret;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	ret = regmap_write(adap->regmap, CHT_WC_EXTCHGRIRQ_MSK, adap->irq_mask);
46662306a36Sopenharmony_ci	if (ret)
46762306a36Sopenharmony_ci		return ret;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	/* Alloc and register client IRQ */
47062306a36Sopenharmony_ci	adap->irq_domain = irq_domain_add_linear(NULL, 1, &irq_domain_simple_ops, NULL);
47162306a36Sopenharmony_ci	if (!adap->irq_domain)
47262306a36Sopenharmony_ci		return -ENOMEM;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	adap->client_irq = irq_create_mapping(adap->irq_domain, 0);
47562306a36Sopenharmony_ci	if (!adap->client_irq) {
47662306a36Sopenharmony_ci		ret = -ENOMEM;
47762306a36Sopenharmony_ci		goto remove_irq_domain;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	irq_set_chip_data(adap->client_irq, adap);
48162306a36Sopenharmony_ci	irq_set_chip_and_handler(adap->client_irq, &adap->irqchip,
48262306a36Sopenharmony_ci				 handle_simple_irq);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
48562306a36Sopenharmony_ci					cht_wc_i2c_adap_thread_handler,
48662306a36Sopenharmony_ci					IRQF_ONESHOT, "PMIC I2C Adapter", adap);
48762306a36Sopenharmony_ci	if (ret)
48862306a36Sopenharmony_ci		goto remove_irq_domain;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	i2c_set_adapdata(&adap->adapter, adap);
49162306a36Sopenharmony_ci	ret = i2c_add_adapter(&adap->adapter);
49262306a36Sopenharmony_ci	if (ret)
49362306a36Sopenharmony_ci		goto remove_irq_domain;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	switch (pmic->cht_wc_model) {
49662306a36Sopenharmony_ci	case INTEL_CHT_WC_GPD_WIN_POCKET:
49762306a36Sopenharmony_ci		board_info = &gpd_win_board_info;
49862306a36Sopenharmony_ci		break;
49962306a36Sopenharmony_ci	case INTEL_CHT_WC_XIAOMI_MIPAD2:
50062306a36Sopenharmony_ci		board_info = &xiaomi_mipad2_board_info;
50162306a36Sopenharmony_ci		break;
50262306a36Sopenharmony_ci	case INTEL_CHT_WC_LENOVO_YOGABOOK1:
50362306a36Sopenharmony_ci		board_info = &lenovo_yogabook1_board_info;
50462306a36Sopenharmony_ci		break;
50562306a36Sopenharmony_ci	case INTEL_CHT_WC_LENOVO_YT3_X90:
50662306a36Sopenharmony_ci		board_info = &lenovo_yoga_tab3_board_info;
50762306a36Sopenharmony_ci		break;
50862306a36Sopenharmony_ci	default:
50962306a36Sopenharmony_ci		dev_warn(&pdev->dev, "Unknown model, not instantiating charger device\n");
51062306a36Sopenharmony_ci		break;
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	if (board_info) {
51462306a36Sopenharmony_ci		board_info->irq = adap->client_irq;
51562306a36Sopenharmony_ci		adap->client = i2c_new_client_device(&adap->adapter, board_info);
51662306a36Sopenharmony_ci		if (IS_ERR(adap->client)) {
51762306a36Sopenharmony_ci			ret = PTR_ERR(adap->client);
51862306a36Sopenharmony_ci			goto del_adapter;
51962306a36Sopenharmony_ci		}
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	platform_set_drvdata(pdev, adap);
52362306a36Sopenharmony_ci	return 0;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cidel_adapter:
52662306a36Sopenharmony_ci	i2c_del_adapter(&adap->adapter);
52762306a36Sopenharmony_ciremove_irq_domain:
52862306a36Sopenharmony_ci	irq_domain_remove(adap->irq_domain);
52962306a36Sopenharmony_ci	return ret;
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic void cht_wc_i2c_adap_i2c_remove(struct platform_device *pdev)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	struct cht_wc_i2c_adap *adap = platform_get_drvdata(pdev);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	i2c_unregister_device(adap->client);
53762306a36Sopenharmony_ci	i2c_del_adapter(&adap->adapter);
53862306a36Sopenharmony_ci	irq_domain_remove(adap->irq_domain);
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_cistatic const struct platform_device_id cht_wc_i2c_adap_id_table[] = {
54262306a36Sopenharmony_ci	{ .name = "cht_wcove_ext_chgr" },
54362306a36Sopenharmony_ci	{},
54462306a36Sopenharmony_ci};
54562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, cht_wc_i2c_adap_id_table);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic struct platform_driver cht_wc_i2c_adap_driver = {
54862306a36Sopenharmony_ci	.probe = cht_wc_i2c_adap_i2c_probe,
54962306a36Sopenharmony_ci	.remove_new = cht_wc_i2c_adap_i2c_remove,
55062306a36Sopenharmony_ci	.driver = {
55162306a36Sopenharmony_ci		.name = "cht_wcove_ext_chgr",
55262306a36Sopenharmony_ci	},
55362306a36Sopenharmony_ci	.id_table = cht_wc_i2c_adap_id_table,
55462306a36Sopenharmony_ci};
55562306a36Sopenharmony_cimodule_platform_driver(cht_wc_i2c_adap_driver);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel CHT Whiskey Cove PMIC I2C Master driver");
55862306a36Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
55962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
560