i2c: smbus: add core function handling SMBus host-notify
SMBus Host-Notify protocol, from the adapter point of view consist of receiving a message from a client, including the client address and some other data. It can be simply handled by creating a new slave device and registering a callback performing the parsing of the message received from the client. This commit introduces two new core functions * i2c_new_slave_host_notify_device * i2c_free_slave_host_notify_device that take care of registration of the new slave device and callback and will call i2c_handle_smbus_host_notify once a Host-Notify event is received. Signed-off-by: Alain Volmat <alain.volmat@st.com> Reviewed-by: Pierre-Yves MORDRET <pierre-yves.mordret@st.com> Signed-off-by: Wolfram Sang <wsa@kernel.org>
This commit is contained in:
Родитель
e6277308ac
Коммит
2a71593da3
|
@ -197,6 +197,113 @@ EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert);
|
|||
|
||||
module_i2c_driver(smbalert_driver);
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
#define SMBUS_HOST_NOTIFY_LEN 3
|
||||
struct i2c_slave_host_notify_status {
|
||||
u8 index;
|
||||
u8 addr;
|
||||
};
|
||||
|
||||
static int i2c_slave_host_notify_cb(struct i2c_client *client,
|
||||
enum i2c_slave_event event, u8 *val)
|
||||
{
|
||||
struct i2c_slave_host_notify_status *status = client->dev.platform_data;
|
||||
|
||||
switch (event) {
|
||||
case I2C_SLAVE_WRITE_RECEIVED:
|
||||
/* We only retrieve the first byte received (addr)
|
||||
* since there is currently no support to retrieve the data
|
||||
* parameter from the client.
|
||||
*/
|
||||
if (status->index == 0)
|
||||
status->addr = *val;
|
||||
if (status->index < U8_MAX)
|
||||
status->index++;
|
||||
break;
|
||||
case I2C_SLAVE_STOP:
|
||||
if (status->index == SMBUS_HOST_NOTIFY_LEN)
|
||||
i2c_handle_smbus_host_notify(client->adapter,
|
||||
status->addr);
|
||||
fallthrough;
|
||||
case I2C_SLAVE_WRITE_REQUESTED:
|
||||
status->index = 0;
|
||||
break;
|
||||
case I2C_SLAVE_READ_REQUESTED:
|
||||
case I2C_SLAVE_READ_PROCESSED:
|
||||
*val = 0xff;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_new_slave_host_notify_device - get a client for SMBus host-notify support
|
||||
* @adapter: the target adapter
|
||||
* Context: can sleep
|
||||
*
|
||||
* Setup handling of the SMBus host-notify protocol on a given I2C bus segment.
|
||||
*
|
||||
* Handling is done by creating a device and its callback and handling data
|
||||
* received via the SMBus host-notify address (0x8)
|
||||
*
|
||||
* This returns the client, which should be ultimately freed using
|
||||
* i2c_free_slave_host_notify_device(); or an ERRPTR to indicate an error.
|
||||
*/
|
||||
struct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter)
|
||||
{
|
||||
struct i2c_board_info host_notify_board_info = {
|
||||
I2C_BOARD_INFO("smbus_host_notify", 0x08),
|
||||
.flags = I2C_CLIENT_SLAVE,
|
||||
};
|
||||
struct i2c_slave_host_notify_status *status;
|
||||
struct i2c_client *client;
|
||||
int ret;
|
||||
|
||||
status = kzalloc(sizeof(struct i2c_slave_host_notify_status),
|
||||
GFP_KERNEL);
|
||||
if (!status)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
host_notify_board_info.platform_data = status;
|
||||
|
||||
client = i2c_new_client_device(adapter, &host_notify_board_info);
|
||||
if (IS_ERR(client)) {
|
||||
kfree(status);
|
||||
return client;
|
||||
}
|
||||
|
||||
ret = i2c_slave_register(client, i2c_slave_host_notify_cb);
|
||||
if (ret) {
|
||||
i2c_unregister_device(client);
|
||||
kfree(status);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_new_slave_host_notify_device);
|
||||
|
||||
/**
|
||||
* i2c_free_slave_host_notify_device - free the client for SMBus host-notify
|
||||
* support
|
||||
* @client: the client to free
|
||||
* Context: can sleep
|
||||
*
|
||||
* Free the i2c_client allocated via i2c_new_slave_host_notify_device
|
||||
*/
|
||||
void i2c_free_slave_host_notify_device(struct i2c_client *client)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(client))
|
||||
return;
|
||||
|
||||
i2c_slave_unregister(client);
|
||||
kfree(client->dev.platform_data);
|
||||
i2c_unregister_device(client);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_free_slave_host_notify_device);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SPD is not part of SMBus but we include it here for convenience as the
|
||||
* target systems are the same.
|
||||
|
|
|
@ -38,6 +38,18 @@ static inline int of_i2c_setup_smbus_alert(struct i2c_adapter *adap)
|
|||
return 0;
|
||||
}
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
struct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter);
|
||||
void i2c_free_slave_host_notify_device(struct i2c_client *client);
|
||||
#else
|
||||
static inline struct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter)
|
||||
{
|
||||
return ERR_PTR(-ENOSYS);
|
||||
}
|
||||
static inline void i2c_free_slave_host_notify_device(struct i2c_client *client)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_DMI)
|
||||
void i2c_register_spd(struct i2c_adapter *adap);
|
||||
|
|
Загрузка…
Ссылка в новой задаче