xref: /linux/drivers/platform/arm64/huawei-gaokun-ec.c (revision 1193e205dbb6feca917dc8e1862ffcdf2194234b)
17636f090SPengyu Luo // SPDX-License-Identifier: GPL-2.0-only
27636f090SPengyu Luo /*
37636f090SPengyu Luo  * huawei-gaokun-ec - An EC driver for HUAWEI Matebook E Go
47636f090SPengyu Luo  *
57636f090SPengyu Luo  * Copyright (C) 2024-2025 Pengyu Luo <mitltlatltl@gmail.com>
67636f090SPengyu Luo  */
77636f090SPengyu Luo 
87636f090SPengyu Luo #include <linux/auxiliary_bus.h>
97636f090SPengyu Luo #include <linux/cleanup.h>
107636f090SPengyu Luo #include <linux/delay.h>
117636f090SPengyu Luo #include <linux/device.h>
127636f090SPengyu Luo #include <linux/hwmon.h>
137636f090SPengyu Luo #include <linux/hwmon-sysfs.h>
147636f090SPengyu Luo #include <linux/i2c.h>
157636f090SPengyu Luo #include <linux/input.h>
167636f090SPengyu Luo #include <linux/notifier.h>
177636f090SPengyu Luo #include <linux/module.h>
187636f090SPengyu Luo #include <linux/mutex.h>
197636f090SPengyu Luo #include <linux/platform_data/huawei-gaokun-ec.h>
207636f090SPengyu Luo 
217636f090SPengyu Luo #define EC_EVENT		0x06
227636f090SPengyu Luo 
237636f090SPengyu Luo /* Also can be found in ACPI specification 12.3 */
247636f090SPengyu Luo #define EC_READ			0x80
257636f090SPengyu Luo #define EC_WRITE		0x81
267636f090SPengyu Luo #define EC_BURST		0x82
277636f090SPengyu Luo #define EC_QUERY		0x84
287636f090SPengyu Luo 
297636f090SPengyu Luo #define EC_FN_LOCK_ON		0x5A
307636f090SPengyu Luo #define EC_FN_LOCK_OFF		0x55
317636f090SPengyu Luo #define EC_FN_LOCK_READ		0x6B
327636f090SPengyu Luo #define EC_FN_LOCK_WRITE	0x6C
337636f090SPengyu Luo 
347636f090SPengyu Luo #define EC_EVENT_LID		0x81
357636f090SPengyu Luo 
367636f090SPengyu Luo #define EC_LID_STATE		0x80
377636f090SPengyu Luo #define EC_LID_OPEN		BIT(1)
387636f090SPengyu Luo 
397636f090SPengyu Luo #define EC_TEMP_REG		0x61
407636f090SPengyu Luo 
417636f090SPengyu Luo #define EC_STANDBY_REG		0xB2
427636f090SPengyu Luo #define EC_STANDBY_ENTER	0xDB
437636f090SPengyu Luo #define EC_STANDBY_EXIT		0xEB
447636f090SPengyu Luo 
457636f090SPengyu Luo enum gaokun_ec_smart_charge_cmd {
467636f090SPengyu Luo 	SMART_CHARGE_DATA_WRITE = 0xE3,
477636f090SPengyu Luo 	SMART_CHARGE_DATA_READ,
487636f090SPengyu Luo 	SMART_CHARGE_ENABLE_WRITE,
497636f090SPengyu Luo 	SMART_CHARGE_ENABLE_READ,
507636f090SPengyu Luo };
517636f090SPengyu Luo 
527636f090SPengyu Luo enum gaokun_ec_ucsi_cmd {
537636f090SPengyu Luo 	UCSI_REG_WRITE = 0xD2,
547636f090SPengyu Luo 	UCSI_REG_READ,
557636f090SPengyu Luo 	UCSI_DATA_WRITE,
567636f090SPengyu Luo 	UCSI_DATA_READ,
577636f090SPengyu Luo };
587636f090SPengyu Luo 
597636f090SPengyu Luo #define UCSI_REG_SIZE		7
607636f090SPengyu Luo 
617636f090SPengyu Luo /*
627636f090SPengyu Luo  * For tx, command sequences are arranged as
637636f090SPengyu Luo  * {master_cmd, slave_cmd, data_len, data_seq}
647636f090SPengyu Luo  */
657636f090SPengyu Luo #define REQ_HDR_SIZE		3
667636f090SPengyu Luo #define INPUT_SIZE_OFFSET	2
677636f090SPengyu Luo #define REQ_LEN(req) (REQ_HDR_SIZE + (req)[INPUT_SIZE_OFFSET])
687636f090SPengyu Luo 
697636f090SPengyu Luo /*
707636f090SPengyu Luo  * For rx, data sequences are arranged as
717636f090SPengyu Luo  * {status, data_len(unreliable), data_seq}
727636f090SPengyu Luo  */
737636f090SPengyu Luo #define RESP_HDR_SIZE		2
747636f090SPengyu Luo 
757636f090SPengyu Luo #define MKREQ(REG0, REG1, SIZE, ...)			\
767636f090SPengyu Luo {							\
777636f090SPengyu Luo 	REG0, REG1, SIZE,				\
787636f090SPengyu Luo 	/* ## will remove comma when SIZE is 0 */	\
797636f090SPengyu Luo 	## __VA_ARGS__,					\
807636f090SPengyu Luo 	/* make sure len(pkt[3:]) >= SIZE */		\
817636f090SPengyu Luo 	[3 + (SIZE)] = 0,				\
827636f090SPengyu Luo }
837636f090SPengyu Luo 
847636f090SPengyu Luo #define MKRESP(SIZE)				\
857636f090SPengyu Luo {						\
867636f090SPengyu Luo 	[RESP_HDR_SIZE + (SIZE) - 1] = 0,	\
877636f090SPengyu Luo }
887636f090SPengyu Luo 
897636f090SPengyu Luo /* Possible size 1, 4, 20, 24. Most of the time, the size is 1. */
refill_req(u8 * dest,const u8 * src,size_t size)907636f090SPengyu Luo static inline void refill_req(u8 *dest, const u8 *src, size_t size)
917636f090SPengyu Luo {
927636f090SPengyu Luo 	memcpy(dest + REQ_HDR_SIZE, src, size);
937636f090SPengyu Luo }
947636f090SPengyu Luo 
refill_req_byte(u8 * dest,const u8 * src)957636f090SPengyu Luo static inline void refill_req_byte(u8 *dest, const u8 *src)
967636f090SPengyu Luo {
977636f090SPengyu Luo 	dest[REQ_HDR_SIZE] = *src;
987636f090SPengyu Luo }
997636f090SPengyu Luo 
1007636f090SPengyu Luo /* Possible size 1, 2, 4, 7, 20. Most of the time, the size is 1. */
extr_resp(u8 * dest,const u8 * src,size_t size)1017636f090SPengyu Luo static inline void extr_resp(u8 *dest, const u8 *src, size_t size)
1027636f090SPengyu Luo {
1037636f090SPengyu Luo 	memcpy(dest, src + RESP_HDR_SIZE, size);
1047636f090SPengyu Luo }
1057636f090SPengyu Luo 
extr_resp_byte(u8 * dest,const u8 * src)1067636f090SPengyu Luo static inline void extr_resp_byte(u8 *dest, const u8 *src)
1077636f090SPengyu Luo {
1087636f090SPengyu Luo 	*dest = src[RESP_HDR_SIZE];
1097636f090SPengyu Luo }
1107636f090SPengyu Luo 
extr_resp_shallow(const u8 * src)1117636f090SPengyu Luo static inline void *extr_resp_shallow(const u8 *src)
1127636f090SPengyu Luo {
1137636f090SPengyu Luo 	return (void *)(src + RESP_HDR_SIZE);
1147636f090SPengyu Luo }
1157636f090SPengyu Luo 
1167636f090SPengyu Luo struct gaokun_ec {
1177636f090SPengyu Luo 	struct i2c_client *client;
1187636f090SPengyu Luo 	struct mutex lock; /* EC transaction lock */
1197636f090SPengyu Luo 	struct blocking_notifier_head notifier_list;
1207636f090SPengyu Luo 	struct device *hwmon_dev;
1217636f090SPengyu Luo 	struct input_dev *idev;
1227636f090SPengyu Luo 	bool suspended;
1237636f090SPengyu Luo };
1247636f090SPengyu Luo 
gaokun_ec_request(struct gaokun_ec * ec,const u8 * req,size_t resp_len,u8 * resp)1257636f090SPengyu Luo static int gaokun_ec_request(struct gaokun_ec *ec, const u8 *req,
1267636f090SPengyu Luo 			     size_t resp_len, u8 *resp)
1277636f090SPengyu Luo {
1287636f090SPengyu Luo 	struct i2c_client *client = ec->client;
1297636f090SPengyu Luo 	struct i2c_msg msgs[] = {
1307636f090SPengyu Luo 		{
1317636f090SPengyu Luo 			.addr = client->addr,
1327636f090SPengyu Luo 			.flags = client->flags,
1337636f090SPengyu Luo 			.len = REQ_LEN(req),
1347636f090SPengyu Luo 			.buf = (void *)req,
1357636f090SPengyu Luo 		}, {
1367636f090SPengyu Luo 			.addr = client->addr,
1377636f090SPengyu Luo 			.flags = client->flags | I2C_M_RD,
1387636f090SPengyu Luo 			.len = resp_len,
1397636f090SPengyu Luo 			.buf = resp,
1407636f090SPengyu Luo 		},
1417636f090SPengyu Luo 	};
1427636f090SPengyu Luo 	int ret;
1437636f090SPengyu Luo 
1447636f090SPengyu Luo 	guard(mutex)(&ec->lock);
1457636f090SPengyu Luo 	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
1467636f090SPengyu Luo 	if (ret != ARRAY_SIZE(msgs)) {
1477636f090SPengyu Luo 		dev_err(&client->dev, "I2C transfer error %d\n", ret);
1487636f090SPengyu Luo 		goto out_after_break;
1497636f090SPengyu Luo 	}
1507636f090SPengyu Luo 
1517636f090SPengyu Luo 	ret = *resp;
1527636f090SPengyu Luo 	if (ret)
1537636f090SPengyu Luo 		dev_err(&client->dev, "EC transaction error %d\n", ret);
1547636f090SPengyu Luo 
1557636f090SPengyu Luo out_after_break:
1567636f090SPengyu Luo 	usleep_range(2000, 2500); /* have a break, ACPI did this */
1577636f090SPengyu Luo 
1587636f090SPengyu Luo 	return ret;
1597636f090SPengyu Luo }
1607636f090SPengyu Luo 
1617636f090SPengyu Luo /* -------------------------------------------------------------------------- */
1627636f090SPengyu Luo /* Common API */
1637636f090SPengyu Luo 
1647636f090SPengyu Luo /**
1657636f090SPengyu Luo  * gaokun_ec_read - Read from EC
1667636f090SPengyu Luo  * @ec: The gaokun_ec structure
1677636f090SPengyu Luo  * @req: The sequence to request
1687636f090SPengyu Luo  * @resp_len: The size to read
1697636f090SPengyu Luo  * @resp: The buffer to store response sequence
1707636f090SPengyu Luo  *
1717636f090SPengyu Luo  * This function is used to read data after writing a magic sequence to EC.
1727636f090SPengyu Luo  * All EC operations depend on this function.
1737636f090SPengyu Luo  *
1747636f090SPengyu Luo  * Huawei uses magic sequences everywhere to complete various functions, all
1757636f090SPengyu Luo  * these sequences are passed to ECCD(a ACPI method which is quiet similar
1767636f090SPengyu Luo  * to gaokun_ec_request), there is no good abstraction to generalize these
1777636f090SPengyu Luo  * sequences, so just wrap it for now. Almost all magic sequences are kept
1787636f090SPengyu Luo  * in this file.
1797636f090SPengyu Luo  *
1807636f090SPengyu Luo  * Return: 0 on success or negative error code.
1817636f090SPengyu Luo  */
gaokun_ec_read(struct gaokun_ec * ec,const u8 * req,size_t resp_len,u8 * resp)1827636f090SPengyu Luo int gaokun_ec_read(struct gaokun_ec *ec, const u8 *req,
1837636f090SPengyu Luo 		   size_t resp_len, u8 *resp)
1847636f090SPengyu Luo {
1857636f090SPengyu Luo 	return gaokun_ec_request(ec, req, resp_len, resp);
1867636f090SPengyu Luo }
1877636f090SPengyu Luo EXPORT_SYMBOL_GPL(gaokun_ec_read);
1887636f090SPengyu Luo 
1897636f090SPengyu Luo /**
1907636f090SPengyu Luo  * gaokun_ec_write - Write to EC
1917636f090SPengyu Luo  * @ec: The gaokun_ec structure
1927636f090SPengyu Luo  * @req: The sequence to request
1937636f090SPengyu Luo  *
1947636f090SPengyu Luo  * This function has no big difference from gaokun_ec_read. When caller care
1957636f090SPengyu Luo  * only write status and no actual data are returned, then use it.
1967636f090SPengyu Luo  *
1977636f090SPengyu Luo  * Return: 0 on success or negative error code.
1987636f090SPengyu Luo  */
gaokun_ec_write(struct gaokun_ec * ec,const u8 * req)1997636f090SPengyu Luo int gaokun_ec_write(struct gaokun_ec *ec, const u8 *req)
2007636f090SPengyu Luo {
2017636f090SPengyu Luo 	u8 ec_resp[] = MKRESP(0);
2027636f090SPengyu Luo 
2037636f090SPengyu Luo 	return gaokun_ec_request(ec, req, sizeof(ec_resp), ec_resp);
2047636f090SPengyu Luo }
2057636f090SPengyu Luo EXPORT_SYMBOL_GPL(gaokun_ec_write);
2067636f090SPengyu Luo 
gaokun_ec_read_byte(struct gaokun_ec * ec,const u8 * req,u8 * byte)2077636f090SPengyu Luo int gaokun_ec_read_byte(struct gaokun_ec *ec, const u8 *req, u8 *byte)
2087636f090SPengyu Luo {
2097636f090SPengyu Luo 	int ret;
2107636f090SPengyu Luo 	u8 ec_resp[] = MKRESP(sizeof(*byte));
2117636f090SPengyu Luo 
2127636f090SPengyu Luo 	ret = gaokun_ec_read(ec, req, sizeof(ec_resp), ec_resp);
2137636f090SPengyu Luo 	extr_resp_byte(byte, ec_resp);
2147636f090SPengyu Luo 
2157636f090SPengyu Luo 	return ret;
2167636f090SPengyu Luo }
2177636f090SPengyu Luo EXPORT_SYMBOL_GPL(gaokun_ec_read_byte);
2187636f090SPengyu Luo 
2197636f090SPengyu Luo /**
2207636f090SPengyu Luo  * gaokun_ec_register_notify - Register a notifier callback for EC events.
2217636f090SPengyu Luo  * @ec: The gaokun_ec structure
2227636f090SPengyu Luo  * @nb: Notifier block pointer to register
2237636f090SPengyu Luo  *
2247636f090SPengyu Luo  * Return: 0 on success or negative error code.
2257636f090SPengyu Luo  */
gaokun_ec_register_notify(struct gaokun_ec * ec,struct notifier_block * nb)2267636f090SPengyu Luo int gaokun_ec_register_notify(struct gaokun_ec *ec, struct notifier_block *nb)
2277636f090SPengyu Luo {
2287636f090SPengyu Luo 	return blocking_notifier_chain_register(&ec->notifier_list, nb);
2297636f090SPengyu Luo }
2307636f090SPengyu Luo EXPORT_SYMBOL_GPL(gaokun_ec_register_notify);
2317636f090SPengyu Luo 
2327636f090SPengyu Luo /**
2337636f090SPengyu Luo  * gaokun_ec_unregister_notify - Unregister notifier callback for EC events.
2347636f090SPengyu Luo  * @ec: The gaokun_ec structure
2357636f090SPengyu Luo  * @nb: Notifier block pointer to unregister
2367636f090SPengyu Luo  *
2377636f090SPengyu Luo  * Unregister a notifier callback that was previously registered with
2387636f090SPengyu Luo  * gaokun_ec_register_notify().
2397636f090SPengyu Luo  */
gaokun_ec_unregister_notify(struct gaokun_ec * ec,struct notifier_block * nb)2407636f090SPengyu Luo void gaokun_ec_unregister_notify(struct gaokun_ec *ec, struct notifier_block *nb)
2417636f090SPengyu Luo {
2427636f090SPengyu Luo 	blocking_notifier_chain_unregister(&ec->notifier_list, nb);
2437636f090SPengyu Luo }
2447636f090SPengyu Luo EXPORT_SYMBOL_GPL(gaokun_ec_unregister_notify);
2457636f090SPengyu Luo 
2467636f090SPengyu Luo /* -------------------------------------------------------------------------- */
2477636f090SPengyu Luo /* API for PSY */
2487636f090SPengyu Luo 
2497636f090SPengyu Luo /**
2507636f090SPengyu Luo  * gaokun_ec_psy_multi_read - Read contiguous registers
2517636f090SPengyu Luo  * @ec: The gaokun_ec structure
2527636f090SPengyu Luo  * @reg: The start register
2537636f090SPengyu Luo  * @resp_len: The number of registers to be read
2547636f090SPengyu Luo  * @resp: The buffer to store response sequence
2557636f090SPengyu Luo  *
2567636f090SPengyu Luo  * Return: 0 on success or negative error code.
2577636f090SPengyu Luo  */
gaokun_ec_psy_multi_read(struct gaokun_ec * ec,u8 reg,size_t resp_len,u8 * resp)2587636f090SPengyu Luo int gaokun_ec_psy_multi_read(struct gaokun_ec *ec, u8 reg,
2597636f090SPengyu Luo 			     size_t resp_len, u8 *resp)
2607636f090SPengyu Luo {
2617636f090SPengyu Luo 	u8 ec_req[] = MKREQ(0x02, EC_READ, 1, 0);
2627636f090SPengyu Luo 	u8 ec_resp[] = MKRESP(1);
2637636f090SPengyu Luo 	int i, ret;
2647636f090SPengyu Luo 
2657636f090SPengyu Luo 	for (i = 0; i < resp_len; ++i, reg++) {
2667636f090SPengyu Luo 		refill_req_byte(ec_req, &reg);
2677636f090SPengyu Luo 		ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp);
2687636f090SPengyu Luo 		if (ret)
2697636f090SPengyu Luo 			return ret;
2707636f090SPengyu Luo 		extr_resp_byte(&resp[i], ec_resp);
2717636f090SPengyu Luo 	}
2727636f090SPengyu Luo 
2737636f090SPengyu Luo 	return 0;
2747636f090SPengyu Luo }
2757636f090SPengyu Luo EXPORT_SYMBOL_GPL(gaokun_ec_psy_multi_read);
2767636f090SPengyu Luo 
2777636f090SPengyu Luo /* Smart charge */
2787636f090SPengyu Luo 
2797636f090SPengyu Luo /**
2807636f090SPengyu Luo  * gaokun_ec_psy_get_smart_charge - Get smart charge data from EC
2817636f090SPengyu Luo  * @ec: The gaokun_ec structure
2827636f090SPengyu Luo  * @resp: The buffer to store response sequence (mode, delay, start, end)
2837636f090SPengyu Luo  *
2847636f090SPengyu Luo  * Return: 0 on success or negative error code.
2857636f090SPengyu Luo  */
gaokun_ec_psy_get_smart_charge(struct gaokun_ec * ec,u8 resp[GAOKUN_SMART_CHARGE_DATA_SIZE])2867636f090SPengyu Luo int gaokun_ec_psy_get_smart_charge(struct gaokun_ec *ec,
2877636f090SPengyu Luo 				   u8 resp[GAOKUN_SMART_CHARGE_DATA_SIZE])
2887636f090SPengyu Luo {
2897636f090SPengyu Luo 	/* GBCM */
2907636f090SPengyu Luo 	u8 ec_req[] = MKREQ(0x02, SMART_CHARGE_DATA_READ, 0);
2917636f090SPengyu Luo 	u8 ec_resp[] = MKRESP(GAOKUN_SMART_CHARGE_DATA_SIZE);
2927636f090SPengyu Luo 	int ret;
2937636f090SPengyu Luo 
2947636f090SPengyu Luo 	ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp);
2957636f090SPengyu Luo 	if (ret)
2967636f090SPengyu Luo 		return ret;
2977636f090SPengyu Luo 
2987636f090SPengyu Luo 	extr_resp(resp, ec_resp, GAOKUN_SMART_CHARGE_DATA_SIZE);
2997636f090SPengyu Luo 
3007636f090SPengyu Luo 	return 0;
3017636f090SPengyu Luo }
3027636f090SPengyu Luo EXPORT_SYMBOL_GPL(gaokun_ec_psy_get_smart_charge);
3037636f090SPengyu Luo 
validate_battery_threshold_range(u8 start,u8 end)3047636f090SPengyu Luo static inline bool validate_battery_threshold_range(u8 start, u8 end)
3057636f090SPengyu Luo {
3067636f090SPengyu Luo 	return end != 0 && start <= end && end <= 100;
3077636f090SPengyu Luo }
3087636f090SPengyu Luo 
3097636f090SPengyu Luo /**
3107636f090SPengyu Luo  * gaokun_ec_psy_set_smart_charge - Set smart charge data
3117636f090SPengyu Luo  * @ec: The gaokun_ec structure
3127636f090SPengyu Luo  * @req: The sequence to request (mode, delay, start, end)
3137636f090SPengyu Luo  *
3147636f090SPengyu Luo  * Return: 0 on success or negative error code.
3157636f090SPengyu Luo  */
gaokun_ec_psy_set_smart_charge(struct gaokun_ec * ec,const u8 req[GAOKUN_SMART_CHARGE_DATA_SIZE])3167636f090SPengyu Luo int gaokun_ec_psy_set_smart_charge(struct gaokun_ec *ec,
3177636f090SPengyu Luo 				   const u8 req[GAOKUN_SMART_CHARGE_DATA_SIZE])
3187636f090SPengyu Luo {
3197636f090SPengyu Luo 	/* SBCM */
3207636f090SPengyu Luo 	u8 ec_req[] = MKREQ(0x02, SMART_CHARGE_DATA_WRITE,
3217636f090SPengyu Luo 			    GAOKUN_SMART_CHARGE_DATA_SIZE);
3227636f090SPengyu Luo 
3237636f090SPengyu Luo 	if (!validate_battery_threshold_range(req[2], req[3]))
3247636f090SPengyu Luo 		return -EINVAL;
3257636f090SPengyu Luo 
3267636f090SPengyu Luo 	refill_req(ec_req, req, GAOKUN_SMART_CHARGE_DATA_SIZE);
3277636f090SPengyu Luo 
3287636f090SPengyu Luo 	return gaokun_ec_write(ec, ec_req);
3297636f090SPengyu Luo }
3307636f090SPengyu Luo EXPORT_SYMBOL_GPL(gaokun_ec_psy_set_smart_charge);
3317636f090SPengyu Luo 
3327636f090SPengyu Luo /* Smart charge enable */
3337636f090SPengyu Luo 
3347636f090SPengyu Luo /**
3357636f090SPengyu Luo  * gaokun_ec_psy_get_smart_charge_enable - Get smart charge state
3367636f090SPengyu Luo  * @ec: The gaokun_ec structure
3377636f090SPengyu Luo  * @on: The state
3387636f090SPengyu Luo  *
3397636f090SPengyu Luo  * Return: 0 on success or negative error code.
3407636f090SPengyu Luo  */
gaokun_ec_psy_get_smart_charge_enable(struct gaokun_ec * ec,bool * on)3417636f090SPengyu Luo int gaokun_ec_psy_get_smart_charge_enable(struct gaokun_ec *ec, bool *on)
3427636f090SPengyu Luo {
3437636f090SPengyu Luo 	/* GBAC */
3447636f090SPengyu Luo 	u8 ec_req[] = MKREQ(0x02, SMART_CHARGE_ENABLE_READ, 0);
3457636f090SPengyu Luo 	u8 state;
3467636f090SPengyu Luo 	int ret;
3477636f090SPengyu Luo 
3487636f090SPengyu Luo 	ret = gaokun_ec_read_byte(ec, ec_req, &state);
3497636f090SPengyu Luo 	if (ret)
3507636f090SPengyu Luo 		return ret;
3517636f090SPengyu Luo 
3527636f090SPengyu Luo 	*on = !!state;
3537636f090SPengyu Luo 
3547636f090SPengyu Luo 	return 0;
3557636f090SPengyu Luo }
3567636f090SPengyu Luo EXPORT_SYMBOL_GPL(gaokun_ec_psy_get_smart_charge_enable);
3577636f090SPengyu Luo 
3587636f090SPengyu Luo /**
3597636f090SPengyu Luo  * gaokun_ec_psy_set_smart_charge_enable - Set smart charge state
3607636f090SPengyu Luo  * @ec: The gaokun_ec structure
3617636f090SPengyu Luo  * @on: The state
3627636f090SPengyu Luo  *
3637636f090SPengyu Luo  * Return: 0 on success or negative error code.
3647636f090SPengyu Luo  */
gaokun_ec_psy_set_smart_charge_enable(struct gaokun_ec * ec,bool on)3657636f090SPengyu Luo int gaokun_ec_psy_set_smart_charge_enable(struct gaokun_ec *ec, bool on)
3667636f090SPengyu Luo {
3677636f090SPengyu Luo 	/* SBAC */
3687636f090SPengyu Luo 	u8 ec_req[] = MKREQ(0x02, SMART_CHARGE_ENABLE_WRITE, 1, on);
3697636f090SPengyu Luo 
3707636f090SPengyu Luo 	return gaokun_ec_write(ec, ec_req);
3717636f090SPengyu Luo }
3727636f090SPengyu Luo EXPORT_SYMBOL_GPL(gaokun_ec_psy_set_smart_charge_enable);
3737636f090SPengyu Luo 
3747636f090SPengyu Luo /* -------------------------------------------------------------------------- */
3757636f090SPengyu Luo /* API for UCSI */
3767636f090SPengyu Luo 
3777636f090SPengyu Luo /**
3787636f090SPengyu Luo  * gaokun_ec_ucsi_read - Read UCSI data from EC
3797636f090SPengyu Luo  * @ec: The gaokun_ec structure
3807636f090SPengyu Luo  * @resp: The buffer to store response sequence
3817636f090SPengyu Luo  *
3827636f090SPengyu Luo  * Read CCI and MSGI (used by UCSI subdriver).
3837636f090SPengyu Luo  *
3847636f090SPengyu Luo  * Return: 0 on success or negative error code.
3857636f090SPengyu Luo  */
gaokun_ec_ucsi_read(struct gaokun_ec * ec,u8 resp[GAOKUN_UCSI_READ_SIZE])3867636f090SPengyu Luo int gaokun_ec_ucsi_read(struct gaokun_ec *ec,
3877636f090SPengyu Luo 			u8 resp[GAOKUN_UCSI_READ_SIZE])
3887636f090SPengyu Luo {
3897636f090SPengyu Luo 	u8 ec_req[] = MKREQ(0x03, UCSI_DATA_READ, 0);
3907636f090SPengyu Luo 	u8 ec_resp[] = MKRESP(GAOKUN_UCSI_READ_SIZE);
3917636f090SPengyu Luo 	int ret;
3927636f090SPengyu Luo 
3937636f090SPengyu Luo 	ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp);
3947636f090SPengyu Luo 	if (ret)
3957636f090SPengyu Luo 		return ret;
3967636f090SPengyu Luo 
3977636f090SPengyu Luo 	extr_resp(resp, ec_resp, GAOKUN_UCSI_READ_SIZE);
3987636f090SPengyu Luo 	return 0;
3997636f090SPengyu Luo }
4007636f090SPengyu Luo EXPORT_SYMBOL_GPL(gaokun_ec_ucsi_read);
4017636f090SPengyu Luo 
4027636f090SPengyu Luo /**
4037636f090SPengyu Luo  * gaokun_ec_ucsi_write - Write UCSI data to EC
4047636f090SPengyu Luo  * @ec: The gaokun_ec structure
4057636f090SPengyu Luo  * @req: The sequence to request
4067636f090SPengyu Luo  *
4077636f090SPengyu Luo  * Write CTRL and MSGO (used by UCSI subdriver).
4087636f090SPengyu Luo  *
4097636f090SPengyu Luo  * Return: 0 on success or negative error code.
4107636f090SPengyu Luo  */
gaokun_ec_ucsi_write(struct gaokun_ec * ec,const u8 req[GAOKUN_UCSI_WRITE_SIZE])4117636f090SPengyu Luo int gaokun_ec_ucsi_write(struct gaokun_ec *ec,
4127636f090SPengyu Luo 			 const u8 req[GAOKUN_UCSI_WRITE_SIZE])
4137636f090SPengyu Luo {
4147636f090SPengyu Luo 	u8 ec_req[] = MKREQ(0x03, UCSI_DATA_WRITE, GAOKUN_UCSI_WRITE_SIZE);
4157636f090SPengyu Luo 
4167636f090SPengyu Luo 	refill_req(ec_req, req, GAOKUN_UCSI_WRITE_SIZE);
4177636f090SPengyu Luo 
4187636f090SPengyu Luo 	return gaokun_ec_write(ec, ec_req);
4197636f090SPengyu Luo }
4207636f090SPengyu Luo EXPORT_SYMBOL_GPL(gaokun_ec_ucsi_write);
4217636f090SPengyu Luo 
4227636f090SPengyu Luo /**
4237636f090SPengyu Luo  * gaokun_ec_ucsi_get_reg - Get UCSI register from EC
4247636f090SPengyu Luo  * @ec: The gaokun_ec structure
4257636f090SPengyu Luo  * @ureg: The gaokun ucsi register
4267636f090SPengyu Luo  *
4277636f090SPengyu Luo  * Get UCSI register data (used by UCSI subdriver).
4287636f090SPengyu Luo  *
4297636f090SPengyu Luo  * Return: 0 on success or negative error code.
4307636f090SPengyu Luo  */
gaokun_ec_ucsi_get_reg(struct gaokun_ec * ec,struct gaokun_ucsi_reg * ureg)4317636f090SPengyu Luo int gaokun_ec_ucsi_get_reg(struct gaokun_ec *ec, struct gaokun_ucsi_reg *ureg)
4327636f090SPengyu Luo {
4337636f090SPengyu Luo 	u8 ec_req[] = MKREQ(0x03, UCSI_REG_READ, 0);
4347636f090SPengyu Luo 	u8 ec_resp[] = MKRESP(UCSI_REG_SIZE);
4357636f090SPengyu Luo 	int ret;
4367636f090SPengyu Luo 
4377636f090SPengyu Luo 	ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp);
4387636f090SPengyu Luo 	if (ret)
4397636f090SPengyu Luo 		return ret;
4407636f090SPengyu Luo 
4417636f090SPengyu Luo 	extr_resp((u8 *)ureg, ec_resp, UCSI_REG_SIZE);
4427636f090SPengyu Luo 
4437636f090SPengyu Luo 	return 0;
4447636f090SPengyu Luo }
4457636f090SPengyu Luo EXPORT_SYMBOL_GPL(gaokun_ec_ucsi_get_reg);
4467636f090SPengyu Luo 
4477636f090SPengyu Luo /**
4487636f090SPengyu Luo  * gaokun_ec_ucsi_pan_ack - Ack pin assignment notifications from EC
4497636f090SPengyu Luo  * @ec: The gaokun_ec structure
4507636f090SPengyu Luo  * @port_id: The port id receiving and handling the notifications
4517636f090SPengyu Luo  *
4527636f090SPengyu Luo  * Ack pin assignment notifications (used by UCSI subdriver).
4537636f090SPengyu Luo  *
4547636f090SPengyu Luo  * Return: 0 on success or negative error code.
4557636f090SPengyu Luo  */
gaokun_ec_ucsi_pan_ack(struct gaokun_ec * ec,int port_id)4567636f090SPengyu Luo int gaokun_ec_ucsi_pan_ack(struct gaokun_ec *ec, int port_id)
4577636f090SPengyu Luo {
4587636f090SPengyu Luo 	u8 ec_req[] = MKREQ(0x03, UCSI_REG_WRITE, 1);
4597636f090SPengyu Luo 	u8 data = 1 << port_id;
4607636f090SPengyu Luo 
4617636f090SPengyu Luo 	if (port_id == GAOKUN_UCSI_NO_PORT_UPDATE)
4627636f090SPengyu Luo 		data = 0;
4637636f090SPengyu Luo 
4647636f090SPengyu Luo 	refill_req_byte(ec_req, &data);
4657636f090SPengyu Luo 
4667636f090SPengyu Luo 	return gaokun_ec_write(ec, ec_req);
4677636f090SPengyu Luo }
4687636f090SPengyu Luo EXPORT_SYMBOL_GPL(gaokun_ec_ucsi_pan_ack);
4697636f090SPengyu Luo 
4707636f090SPengyu Luo /* -------------------------------------------------------------------------- */
4717636f090SPengyu Luo /* EC Sysfs */
4727636f090SPengyu Luo 
4737636f090SPengyu Luo /* Fn lock */
gaokun_ec_get_fn_lock(struct gaokun_ec * ec,bool * on)4747636f090SPengyu Luo static int gaokun_ec_get_fn_lock(struct gaokun_ec *ec, bool *on)
4757636f090SPengyu Luo {
4767636f090SPengyu Luo 	/* GFRS */
4777636f090SPengyu Luo 	u8 ec_req[] = MKREQ(0x02, EC_FN_LOCK_READ, 0);
4787636f090SPengyu Luo 	int ret;
4797636f090SPengyu Luo 	u8 state;
4807636f090SPengyu Luo 
4817636f090SPengyu Luo 	ret = gaokun_ec_read_byte(ec, ec_req, &state);
4827636f090SPengyu Luo 	if (ret)
4837636f090SPengyu Luo 		return ret;
4847636f090SPengyu Luo 
4857636f090SPengyu Luo 	if (state == EC_FN_LOCK_ON)
4867636f090SPengyu Luo 		*on = true;
4877636f090SPengyu Luo 	else if (state == EC_FN_LOCK_OFF)
4887636f090SPengyu Luo 		*on = false;
4897636f090SPengyu Luo 	else
4907636f090SPengyu Luo 		return -EIO;
4917636f090SPengyu Luo 
4927636f090SPengyu Luo 	return 0;
4937636f090SPengyu Luo }
4947636f090SPengyu Luo 
gaokun_ec_set_fn_lock(struct gaokun_ec * ec,bool on)4957636f090SPengyu Luo static int gaokun_ec_set_fn_lock(struct gaokun_ec *ec, bool on)
4967636f090SPengyu Luo {
4977636f090SPengyu Luo 	/* SFRS */
4987636f090SPengyu Luo 	u8 ec_req[] = MKREQ(0x02, EC_FN_LOCK_WRITE, 1,
4997636f090SPengyu Luo 			    on ? EC_FN_LOCK_ON : EC_FN_LOCK_OFF);
5007636f090SPengyu Luo 
5017636f090SPengyu Luo 	return gaokun_ec_write(ec, ec_req);
5027636f090SPengyu Luo }
5037636f090SPengyu Luo 
fn_lock_show(struct device * dev,struct device_attribute * attr,char * buf)5047636f090SPengyu Luo static ssize_t fn_lock_show(struct device *dev,
5057636f090SPengyu Luo 			    struct device_attribute *attr,
5067636f090SPengyu Luo 			    char *buf)
5077636f090SPengyu Luo {
5087636f090SPengyu Luo 	struct gaokun_ec *ec = dev_get_drvdata(dev);
5097636f090SPengyu Luo 	bool on;
5107636f090SPengyu Luo 	int ret;
5117636f090SPengyu Luo 
5127636f090SPengyu Luo 	ret = gaokun_ec_get_fn_lock(ec, &on);
5137636f090SPengyu Luo 	if (ret)
5147636f090SPengyu Luo 		return ret;
5157636f090SPengyu Luo 
5167636f090SPengyu Luo 	return sysfs_emit(buf, "%d\n", on);
5177636f090SPengyu Luo }
5187636f090SPengyu Luo 
fn_lock_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)5197636f090SPengyu Luo static ssize_t fn_lock_store(struct device *dev,
5207636f090SPengyu Luo 			     struct device_attribute *attr,
5217636f090SPengyu Luo 			     const char *buf, size_t size)
5227636f090SPengyu Luo {
5237636f090SPengyu Luo 	struct gaokun_ec *ec = dev_get_drvdata(dev);
5247636f090SPengyu Luo 	bool on;
5257636f090SPengyu Luo 	int ret;
5267636f090SPengyu Luo 
5277636f090SPengyu Luo 	if (kstrtobool(buf, &on))
5287636f090SPengyu Luo 		return -EINVAL;
5297636f090SPengyu Luo 
5307636f090SPengyu Luo 	ret = gaokun_ec_set_fn_lock(ec, on);
5317636f090SPengyu Luo 	if (ret)
5327636f090SPengyu Luo 		return ret;
5337636f090SPengyu Luo 
5347636f090SPengyu Luo 	return size;
5357636f090SPengyu Luo }
5367636f090SPengyu Luo 
5377636f090SPengyu Luo static DEVICE_ATTR_RW(fn_lock);
5387636f090SPengyu Luo 
5397636f090SPengyu Luo static struct attribute *gaokun_ec_attrs[] = {
5407636f090SPengyu Luo 	&dev_attr_fn_lock.attr,
5417636f090SPengyu Luo 	NULL,
5427636f090SPengyu Luo };
5437636f090SPengyu Luo ATTRIBUTE_GROUPS(gaokun_ec);
5447636f090SPengyu Luo 
5457636f090SPengyu Luo /* -------------------------------------------------------------------------- */
5467636f090SPengyu Luo /* Thermal Zone HwMon */
5477636f090SPengyu Luo 
5487636f090SPengyu Luo /* Range from 0 to 0x2C, partially valid */
5497636f090SPengyu Luo static const u8 temp_reg[] = {
5507636f090SPengyu Luo 	0x05, 0x07, 0x08, 0x0E, 0x0F, 0x12, 0x15, 0x1E,
5517636f090SPengyu Luo 	0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
5527636f090SPengyu Luo 	0x27, 0x28, 0x29, 0x2A
5537636f090SPengyu Luo };
5547636f090SPengyu Luo 
gaokun_ec_get_temp(struct gaokun_ec * ec,u8 idx,long * temp)5557636f090SPengyu Luo static int gaokun_ec_get_temp(struct gaokun_ec *ec, u8 idx, long *temp)
5567636f090SPengyu Luo {
5577636f090SPengyu Luo 	/* GTMP */
5587636f090SPengyu Luo 	u8 ec_req[] = MKREQ(0x02, EC_TEMP_REG, 1, temp_reg[idx]);
5597636f090SPengyu Luo 	u8 ec_resp[] = MKRESP(sizeof(__le16));
5607636f090SPengyu Luo 	__le16 *tmp;
5617636f090SPengyu Luo 	int ret;
5627636f090SPengyu Luo 
5637636f090SPengyu Luo 	ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp);
5647636f090SPengyu Luo 	if (ret)
5657636f090SPengyu Luo 		return ret;
5667636f090SPengyu Luo 
5677636f090SPengyu Luo 	tmp = (__le16 *)extr_resp_shallow(ec_resp);
5687636f090SPengyu Luo 	*temp = le16_to_cpu(*tmp) * 100; /* convert to HwMon's unit */
5697636f090SPengyu Luo 
5707636f090SPengyu Luo 	return 0;
5717636f090SPengyu Luo }
5727636f090SPengyu Luo 
5737636f090SPengyu Luo static umode_t
gaokun_ec_hwmon_is_visible(const void * data,enum hwmon_sensor_types type,u32 attr,int channel)5747636f090SPengyu Luo gaokun_ec_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
5757636f090SPengyu Luo 			   u32 attr, int channel)
5767636f090SPengyu Luo {
5777636f090SPengyu Luo 	return type == hwmon_temp ? 0444 : 0;
5787636f090SPengyu Luo }
5797636f090SPengyu Luo 
5807636f090SPengyu Luo static int
gaokun_ec_hwmon_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)5817636f090SPengyu Luo gaokun_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
5827636f090SPengyu Luo 		     u32 attr, int channel, long *val)
5837636f090SPengyu Luo {
5847636f090SPengyu Luo 	struct gaokun_ec *ec = dev_get_drvdata(dev);
5857636f090SPengyu Luo 
5867636f090SPengyu Luo 	if (type == hwmon_temp)
5877636f090SPengyu Luo 		return gaokun_ec_get_temp(ec, channel, val);
5887636f090SPengyu Luo 
5897636f090SPengyu Luo 	return -EINVAL;
5907636f090SPengyu Luo }
5917636f090SPengyu Luo 
5927636f090SPengyu Luo static const struct hwmon_ops gaokun_ec_hwmon_ops = {
5937636f090SPengyu Luo 	.is_visible = gaokun_ec_hwmon_is_visible,
5947636f090SPengyu Luo 	.read = gaokun_ec_hwmon_read,
5957636f090SPengyu Luo };
5967636f090SPengyu Luo 
5977636f090SPengyu Luo static u32 gaokun_ec_temp_config[] = {
5987636f090SPengyu Luo 	[0 ... ARRAY_SIZE(temp_reg) - 1] = HWMON_T_INPUT,
5997636f090SPengyu Luo 	0
6007636f090SPengyu Luo };
6017636f090SPengyu Luo 
6027636f090SPengyu Luo static const struct hwmon_channel_info gaokun_ec_temp = {
6037636f090SPengyu Luo 	.type = hwmon_temp,
6047636f090SPengyu Luo 	.config = gaokun_ec_temp_config,
6057636f090SPengyu Luo };
6067636f090SPengyu Luo 
6077636f090SPengyu Luo static const struct hwmon_channel_info * const gaokun_ec_hwmon_info[] = {
6087636f090SPengyu Luo 	&gaokun_ec_temp,
6097636f090SPengyu Luo 	NULL
6107636f090SPengyu Luo };
6117636f090SPengyu Luo 
6127636f090SPengyu Luo static const struct hwmon_chip_info gaokun_ec_hwmon_chip_info = {
6137636f090SPengyu Luo 	.ops = &gaokun_ec_hwmon_ops,
6147636f090SPengyu Luo 	.info = gaokun_ec_hwmon_info,
6157636f090SPengyu Luo };
6167636f090SPengyu Luo 
6177636f090SPengyu Luo /* -------------------------------------------------------------------------- */
6187636f090SPengyu Luo /* Modern Standby */
6197636f090SPengyu Luo 
gaokun_ec_suspend(struct device * dev)6207636f090SPengyu Luo static int gaokun_ec_suspend(struct device *dev)
6217636f090SPengyu Luo {
6227636f090SPengyu Luo 	struct gaokun_ec *ec = dev_get_drvdata(dev);
6237636f090SPengyu Luo 	u8 ec_req[] = MKREQ(0x02, EC_STANDBY_REG, 1, EC_STANDBY_ENTER);
6247636f090SPengyu Luo 	int ret;
6257636f090SPengyu Luo 
6267636f090SPengyu Luo 	if (ec->suspended)
6277636f090SPengyu Luo 		return 0;
6287636f090SPengyu Luo 
6297636f090SPengyu Luo 	ret = gaokun_ec_write(ec, ec_req);
6307636f090SPengyu Luo 	if (ret)
6317636f090SPengyu Luo 		return ret;
6327636f090SPengyu Luo 
6337636f090SPengyu Luo 	ec->suspended = true;
6347636f090SPengyu Luo 
6357636f090SPengyu Luo 	return 0;
6367636f090SPengyu Luo }
6377636f090SPengyu Luo 
gaokun_ec_resume(struct device * dev)6387636f090SPengyu Luo static int gaokun_ec_resume(struct device *dev)
6397636f090SPengyu Luo {
6407636f090SPengyu Luo 	struct gaokun_ec *ec = dev_get_drvdata(dev);
6417636f090SPengyu Luo 	u8 ec_req[] = MKREQ(0x02, EC_STANDBY_REG, 1, EC_STANDBY_EXIT);
6427636f090SPengyu Luo 	int ret;
6437636f090SPengyu Luo 	int i;
6447636f090SPengyu Luo 
6457636f090SPengyu Luo 	if (!ec->suspended)
6467636f090SPengyu Luo 		return 0;
6477636f090SPengyu Luo 
6487636f090SPengyu Luo 	for (i = 0; i < 3; ++i) {
6497636f090SPengyu Luo 		ret = gaokun_ec_write(ec, ec_req);
6507636f090SPengyu Luo 		if (ret == 0)
6517636f090SPengyu Luo 			break;
6527636f090SPengyu Luo 
6537636f090SPengyu Luo 		msleep(100); /* EC need time to resume */
654*70081121SChen Ni 	}
6557636f090SPengyu Luo 
6567636f090SPengyu Luo 	ec->suspended = false;
6577636f090SPengyu Luo 
6587636f090SPengyu Luo 	return 0;
6597636f090SPengyu Luo }
6607636f090SPengyu Luo 
gaokun_aux_release(struct device * dev)6617636f090SPengyu Luo static void gaokun_aux_release(struct device *dev)
6627636f090SPengyu Luo {
6637636f090SPengyu Luo 	struct auxiliary_device *adev = to_auxiliary_dev(dev);
6647636f090SPengyu Luo 
6657636f090SPengyu Luo 	kfree(adev);
6667636f090SPengyu Luo }
6677636f090SPengyu Luo 
gaokun_aux_remove(void * data)6687636f090SPengyu Luo static void gaokun_aux_remove(void *data)
6697636f090SPengyu Luo {
6707636f090SPengyu Luo 	struct auxiliary_device *adev = data;
6717636f090SPengyu Luo 
6727636f090SPengyu Luo 	auxiliary_device_delete(adev);
6737636f090SPengyu Luo 	auxiliary_device_uninit(adev);
6747636f090SPengyu Luo }
6757636f090SPengyu Luo 
gaokun_aux_init(struct device * parent,const char * name,struct gaokun_ec * ec)6767636f090SPengyu Luo static int gaokun_aux_init(struct device *parent, const char *name,
6777636f090SPengyu Luo 			   struct gaokun_ec *ec)
6787636f090SPengyu Luo {
6797636f090SPengyu Luo 	struct auxiliary_device *adev;
6807636f090SPengyu Luo 	int ret;
6817636f090SPengyu Luo 
6827636f090SPengyu Luo 	adev = kzalloc(sizeof(*adev), GFP_KERNEL);
6837636f090SPengyu Luo 	if (!adev)
6847636f090SPengyu Luo 		return -ENOMEM;
6857636f090SPengyu Luo 
6867636f090SPengyu Luo 	adev->name = name;
6877636f090SPengyu Luo 	adev->id = 0;
6887636f090SPengyu Luo 	adev->dev.parent = parent;
6897636f090SPengyu Luo 	adev->dev.release = gaokun_aux_release;
6907636f090SPengyu Luo 	adev->dev.platform_data = ec;
6917636f090SPengyu Luo 	/* Allow aux devices to access parent's DT nodes directly */
6927636f090SPengyu Luo 	device_set_of_node_from_dev(&adev->dev, parent);
6937636f090SPengyu Luo 
6947636f090SPengyu Luo 	ret = auxiliary_device_init(adev);
6957636f090SPengyu Luo 	if (ret) {
6967636f090SPengyu Luo 		kfree(adev);
6977636f090SPengyu Luo 		return ret;
6987636f090SPengyu Luo 	}
6997636f090SPengyu Luo 
7007636f090SPengyu Luo 	ret = auxiliary_device_add(adev);
7017636f090SPengyu Luo 	if (ret) {
7027636f090SPengyu Luo 		auxiliary_device_uninit(adev);
7037636f090SPengyu Luo 		return ret;
7047636f090SPengyu Luo 	}
7057636f090SPengyu Luo 
7067636f090SPengyu Luo 	return devm_add_action_or_reset(parent, gaokun_aux_remove, adev);
7077636f090SPengyu Luo }
7087636f090SPengyu Luo 
7097636f090SPengyu Luo /* -------------------------------------------------------------------------- */
7107636f090SPengyu Luo /* EC */
7117636f090SPengyu Luo 
gaokun_ec_irq_handler(int irq,void * data)7127636f090SPengyu Luo static irqreturn_t gaokun_ec_irq_handler(int irq, void *data)
7137636f090SPengyu Luo {
7147636f090SPengyu Luo 	struct gaokun_ec *ec = data;
7157636f090SPengyu Luo 	u8 ec_req[] = MKREQ(EC_EVENT, EC_QUERY, 0);
7167636f090SPengyu Luo 	u8 status, id;
7177636f090SPengyu Luo 	int ret;
7187636f090SPengyu Luo 
7197636f090SPengyu Luo 	ret = gaokun_ec_read_byte(ec, ec_req, &id);
7207636f090SPengyu Luo 	if (ret)
7217636f090SPengyu Luo 		return IRQ_HANDLED;
7227636f090SPengyu Luo 
7237636f090SPengyu Luo 	switch (id) {
7247636f090SPengyu Luo 	case 0x0: /* No event */
7257636f090SPengyu Luo 		break;
7267636f090SPengyu Luo 
7277636f090SPengyu Luo 	case EC_EVENT_LID:
7287636f090SPengyu Luo 		gaokun_ec_psy_read_byte(ec, EC_LID_STATE, &status);
7297636f090SPengyu Luo 		status &= EC_LID_OPEN;
7307636f090SPengyu Luo 		input_report_switch(ec->idev, SW_LID, !status);
7317636f090SPengyu Luo 		input_sync(ec->idev);
7327636f090SPengyu Luo 		break;
7337636f090SPengyu Luo 
7347636f090SPengyu Luo 	default:
7357636f090SPengyu Luo 		blocking_notifier_call_chain(&ec->notifier_list, id, ec);
7367636f090SPengyu Luo 	}
7377636f090SPengyu Luo 
7387636f090SPengyu Luo 	return IRQ_HANDLED;
7397636f090SPengyu Luo }
7407636f090SPengyu Luo 
gaokun_ec_probe(struct i2c_client * client)7417636f090SPengyu Luo static int gaokun_ec_probe(struct i2c_client *client)
7427636f090SPengyu Luo {
7437636f090SPengyu Luo 	struct device *dev = &client->dev;
7447636f090SPengyu Luo 	struct gaokun_ec *ec;
7457636f090SPengyu Luo 	int ret;
7467636f090SPengyu Luo 
7477636f090SPengyu Luo 	ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
7487636f090SPengyu Luo 	if (!ec)
7497636f090SPengyu Luo 		return -ENOMEM;
7507636f090SPengyu Luo 
7517636f090SPengyu Luo 	ret = devm_mutex_init(dev, &ec->lock);
7527636f090SPengyu Luo 	if (ret)
7537636f090SPengyu Luo 		return ret;
7547636f090SPengyu Luo 
7557636f090SPengyu Luo 	ec->client = client;
7567636f090SPengyu Luo 	i2c_set_clientdata(client, ec);
7577636f090SPengyu Luo 	BLOCKING_INIT_NOTIFIER_HEAD(&ec->notifier_list);
7587636f090SPengyu Luo 
7597636f090SPengyu Luo 	/* Lid switch */
7607636f090SPengyu Luo 	ec->idev = devm_input_allocate_device(dev);
7617636f090SPengyu Luo 	if (!ec->idev)
7627636f090SPengyu Luo 		return -ENOMEM;
7637636f090SPengyu Luo 
7647636f090SPengyu Luo 	ec->idev->name = "LID";
7657636f090SPengyu Luo 	ec->idev->phys = "gaokun-ec/input0";
7667636f090SPengyu Luo 	input_set_capability(ec->idev, EV_SW, SW_LID);
7677636f090SPengyu Luo 
7687636f090SPengyu Luo 	ret = input_register_device(ec->idev);
7697636f090SPengyu Luo 	if (ret)
7707636f090SPengyu Luo 		return dev_err_probe(dev, ret, "Failed to register input device\n");
7717636f090SPengyu Luo 
7727636f090SPengyu Luo 	ret = gaokun_aux_init(dev, GAOKUN_DEV_PSY, ec);
7737636f090SPengyu Luo 	if (ret)
7747636f090SPengyu Luo 		return ret;
7757636f090SPengyu Luo 
7767636f090SPengyu Luo 	ret = gaokun_aux_init(dev, GAOKUN_DEV_UCSI, ec);
7777636f090SPengyu Luo 	if (ret)
7787636f090SPengyu Luo 		return ret;
7797636f090SPengyu Luo 
7807636f090SPengyu Luo 	ret = devm_request_threaded_irq(dev, client->irq, NULL,
7817636f090SPengyu Luo 					gaokun_ec_irq_handler, IRQF_ONESHOT,
7827636f090SPengyu Luo 					dev_name(dev), ec);
7837636f090SPengyu Luo 	if (ret)
7847636f090SPengyu Luo 		return dev_err_probe(dev, ret, "Failed to request IRQ\n");
7857636f090SPengyu Luo 
7867636f090SPengyu Luo 	ec->hwmon_dev = devm_hwmon_device_register_with_info(dev, "gaokun_ec_hwmon",
7877636f090SPengyu Luo 							     ec, &gaokun_ec_hwmon_chip_info, NULL);
7887636f090SPengyu Luo 	if (IS_ERR(ec->hwmon_dev))
7897636f090SPengyu Luo 		return dev_err_probe(dev, PTR_ERR(ec->hwmon_dev),
7907636f090SPengyu Luo 				     "Failed to register hwmon device\n");
7917636f090SPengyu Luo 
7927636f090SPengyu Luo 	return 0;
7937636f090SPengyu Luo }
7947636f090SPengyu Luo 
7957636f090SPengyu Luo static const struct i2c_device_id gaokun_ec_id[] = {
7967636f090SPengyu Luo 	{ "gaokun-ec", },
7977636f090SPengyu Luo 	{ }
7987636f090SPengyu Luo };
7997636f090SPengyu Luo MODULE_DEVICE_TABLE(i2c, gaokun_ec_id);
8007636f090SPengyu Luo 
8017636f090SPengyu Luo static const struct of_device_id gaokun_ec_of_match[] = {
8027636f090SPengyu Luo 	{ .compatible = "huawei,gaokun3-ec", },
8037636f090SPengyu Luo 	{ }
8047636f090SPengyu Luo };
8057636f090SPengyu Luo MODULE_DEVICE_TABLE(of, gaokun_ec_of_match);
8067636f090SPengyu Luo 
8077636f090SPengyu Luo static const struct dev_pm_ops gaokun_ec_pm_ops = {
8087636f090SPengyu Luo 	NOIRQ_SYSTEM_SLEEP_PM_OPS(gaokun_ec_suspend, gaokun_ec_resume)
8097636f090SPengyu Luo };
8107636f090SPengyu Luo 
8117636f090SPengyu Luo static struct i2c_driver gaokun_ec_driver = {
8127636f090SPengyu Luo 	.driver = {
8137636f090SPengyu Luo 		.name = "gaokun-ec",
8147636f090SPengyu Luo 		.of_match_table = gaokun_ec_of_match,
8157636f090SPengyu Luo 		.pm = &gaokun_ec_pm_ops,
8167636f090SPengyu Luo 		.dev_groups = gaokun_ec_groups,
8177636f090SPengyu Luo 	},
8187636f090SPengyu Luo 	.probe = gaokun_ec_probe,
8197636f090SPengyu Luo 	.id_table = gaokun_ec_id,
8207636f090SPengyu Luo };
8217636f090SPengyu Luo module_i2c_driver(gaokun_ec_driver);
8227636f090SPengyu Luo 
8237636f090SPengyu Luo MODULE_DESCRIPTION("HUAWEI Matebook E Go EC driver");
8247636f090SPengyu Luo MODULE_AUTHOR("Pengyu Luo <mitltlatltl@gmail.com>");
8257636f090SPengyu Luo MODULE_LICENSE("GPL");
826