// SPDX-License-Identifier: GPL-2.0-only /* * huawei-gaokun-ec - An EC driver for HUAWEI Matebook E Go * * Copyright (C) 2024-2025 Pengyu Luo <mitltlatltl@gmail.com>
*/
/* Possible size 1, 4, 20, 24. Most of the time, the size is 1. */ staticinlinevoid refill_req(u8 *dest, const u8 *src, size_t size)
{
memcpy(dest + REQ_HDR_SIZE, src, size);
}
/* Possible size 1, 2, 4, 7, 20. Most of the time, the size is 1. */ staticinlinevoid extr_resp(u8 *dest, const u8 *src, size_t size)
{
memcpy(dest, src + RESP_HDR_SIZE, size);
}
guard(mutex)(&ec->lock);
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret != ARRAY_SIZE(msgs)) {
dev_err(&client->dev, "I2C transfer error %d\n", ret); goto out_after_break;
}
ret = *resp; if (ret)
dev_err(&client->dev, "EC transaction error %d\n", ret);
out_after_break:
usleep_range(2000, 2500); /* have a break, ACPI did this */
return ret;
}
/* -------------------------------------------------------------------------- */ /* Common API */
/** * gaokun_ec_read - Read from EC * @ec: The gaokun_ec structure * @req: The sequence to request * @resp_len: The size to read * @resp: The buffer to store response sequence * * This function is used to read data after writing a magic sequence to EC. * All EC operations depend on this function. * * Huawei uses magic sequences everywhere to complete various functions, all * these sequences are passed to ECCD(a ACPI method which is quiet similar * to gaokun_ec_request), there is no good abstraction to generalize these * sequences, so just wrap it for now. Almost all magic sequences are kept * in this file. * * Return: 0 on success or negative error code.
*/ int gaokun_ec_read(struct gaokun_ec *ec, const u8 *req,
size_t resp_len, u8 *resp)
{ return gaokun_ec_request(ec, req, resp_len, resp);
}
EXPORT_SYMBOL_GPL(gaokun_ec_read);
/** * gaokun_ec_write - Write to EC * @ec: The gaokun_ec structure * @req: The sequence to request * * This function has no big difference from gaokun_ec_read. When caller care * only write status and no actual data are returned, then use it. * * Return: 0 on success or negative error code.
*/ int gaokun_ec_write(struct gaokun_ec *ec, const u8 *req)
{
u8 ec_resp[] = MKRESP(0);
/** * gaokun_ec_register_notify - Register a notifier callback for EC events. * @ec: The gaokun_ec structure * @nb: Notifier block pointer to register * * Return: 0 on success or negative error code.
*/ int gaokun_ec_register_notify(struct gaokun_ec *ec, struct notifier_block *nb)
{ return blocking_notifier_chain_register(&ec->notifier_list, nb);
}
EXPORT_SYMBOL_GPL(gaokun_ec_register_notify);
/** * gaokun_ec_unregister_notify - Unregister notifier callback for EC events. * @ec: The gaokun_ec structure * @nb: Notifier block pointer to unregister * * Unregister a notifier callback that was previously registered with * gaokun_ec_register_notify().
*/ void gaokun_ec_unregister_notify(struct gaokun_ec *ec, struct notifier_block *nb)
{
blocking_notifier_chain_unregister(&ec->notifier_list, nb);
}
EXPORT_SYMBOL_GPL(gaokun_ec_unregister_notify);
/* -------------------------------------------------------------------------- */ /* API for PSY */
/** * gaokun_ec_psy_multi_read - Read contiguous registers * @ec: The gaokun_ec structure * @reg: The start register * @resp_len: The number of registers to be read * @resp: The buffer to store response sequence * * Return: 0 on success or negative error code.
*/ int gaokun_ec_psy_multi_read(struct gaokun_ec *ec, u8 reg,
size_t resp_len, u8 *resp)
{
u8 ec_req[] = MKREQ(0x02, EC_READ, 1, 0);
u8 ec_resp[] = MKRESP(1); int i, ret;
for (i = 0; i < resp_len; ++i, reg++) {
refill_req_byte(ec_req, ®);
ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp); if (ret) return ret;
extr_resp_byte(&resp[i], ec_resp);
}
/* -------------------------------------------------------------------------- */ /* API for UCSI */
/** * gaokun_ec_ucsi_read - Read UCSI data from EC * @ec: The gaokun_ec structure * @resp: The buffer to store response sequence * * Read CCI and MSGI (used by UCSI subdriver). * * Return: 0 on success or negative error code.
*/ int gaokun_ec_ucsi_read(struct gaokun_ec *ec,
u8 resp[GAOKUN_UCSI_READ_SIZE])
{
u8 ec_req[] = MKREQ(0x03, UCSI_DATA_READ, 0);
u8 ec_resp[] = MKRESP(GAOKUN_UCSI_READ_SIZE); int ret;
ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp); if (ret) return ret;
/** * gaokun_ec_ucsi_pan_ack - Ack pin assignment notifications from EC * @ec: The gaokun_ec structure * @port_id: The port id receiving and handling the notifications * * Ack pin assignment notifications (used by UCSI subdriver). * * Return: 0 on success or negative error code.
*/ int gaokun_ec_ucsi_pan_ack(struct gaokun_ec *ec, int port_id)
{
u8 ec_req[] = MKREQ(0x03, UCSI_REG_WRITE, 1);
u8 data = 1 << port_id;
if (port_id == GAOKUN_UCSI_NO_PORT_UPDATE)
data = 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.