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, ®);
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