mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 01:55:51 +02:00
usb: typec: ucsi: ccg: Move to the new API
Replacing the old "cmd" and "sync" callbacks with an implementation of struct ucsi_operations. The interrupt handler will from now on read the CCI (Command Status and Connector Change Indication) register, and call ucsi_connector_change() function and/or complete pending command completions based on it. Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> Tested-by: Ajay Gupta <ajayg@nvidia.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Link: https://lore.kernel.org/r/20191104142435.29960-14-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
f56de278e8
commit
e32fd989ac
|
|
@ -176,8 +176,8 @@ struct ccg_resp {
|
||||||
struct ucsi_ccg {
|
struct ucsi_ccg {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct ucsi *ucsi;
|
struct ucsi *ucsi;
|
||||||
struct ucsi_ppm ppm;
|
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
|
|
||||||
struct ccg_dev_info info;
|
struct ccg_dev_info info;
|
||||||
/* version info for boot, primary and secondary */
|
/* version info for boot, primary and secondary */
|
||||||
struct version_info version[FW2 + 1];
|
struct version_info version[FW2 + 1];
|
||||||
|
|
@ -196,6 +196,8 @@ struct ucsi_ccg {
|
||||||
/* fw build with vendor information */
|
/* fw build with vendor information */
|
||||||
u16 fw_build;
|
u16 fw_build;
|
||||||
struct work_struct pm_work;
|
struct work_struct pm_work;
|
||||||
|
|
||||||
|
struct completion complete;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
|
static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
|
||||||
|
|
@ -243,7 +245,7 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
|
static int ccg_write(struct ucsi_ccg *uc, u16 rab, const u8 *data, u32 len)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = uc->client;
|
struct i2c_client *client = uc->client;
|
||||||
unsigned char *buf;
|
unsigned char *buf;
|
||||||
|
|
@ -317,88 +319,85 @@ static int ucsi_ccg_init(struct ucsi_ccg *uc)
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ucsi_ccg_send_data(struct ucsi_ccg *uc)
|
static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
|
||||||
|
void *val, size_t val_len)
|
||||||
{
|
{
|
||||||
u8 *ppm = (u8 *)uc->ppm.data;
|
u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
|
||||||
int status;
|
|
||||||
u16 rab;
|
|
||||||
|
|
||||||
rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_out));
|
return ccg_read(ucsi_get_drvdata(ucsi), reg, val, val_len);
|
||||||
status = ccg_write(uc, rab, ppm +
|
|
||||||
offsetof(struct ucsi_data, message_out),
|
|
||||||
sizeof(uc->ppm.data->message_out));
|
|
||||||
if (status < 0)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, ctrl));
|
|
||||||
return ccg_write(uc, rab, ppm + offsetof(struct ucsi_data, ctrl),
|
|
||||||
sizeof(uc->ppm.data->ctrl));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ucsi_ccg_recv_data(struct ucsi_ccg *uc)
|
static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset,
|
||||||
|
const void *val, size_t val_len)
|
||||||
{
|
{
|
||||||
u8 *ppm = (u8 *)uc->ppm.data;
|
u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
|
||||||
int status;
|
|
||||||
u16 rab;
|
|
||||||
|
|
||||||
rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, cci));
|
return ccg_write(ucsi_get_drvdata(ucsi), reg, val, val_len);
|
||||||
status = ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, cci),
|
|
||||||
sizeof(uc->ppm.data->cci));
|
|
||||||
if (status < 0)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_in));
|
|
||||||
return ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, message_in),
|
|
||||||
sizeof(uc->ppm.data->message_in));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ucsi_ccg_ack_interrupt(struct ucsi_ccg *uc)
|
static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset,
|
||||||
|
const void *val, size_t val_len)
|
||||||
{
|
{
|
||||||
int status;
|
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
|
||||||
unsigned char data;
|
int ret;
|
||||||
|
|
||||||
status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
|
mutex_lock(&uc->lock);
|
||||||
if (status < 0)
|
pm_runtime_get_sync(uc->dev);
|
||||||
return status;
|
set_bit(DEV_CMD_PENDING, &uc->flags);
|
||||||
|
|
||||||
return ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
|
ret = ucsi_ccg_async_write(ucsi, offset, val, val_len);
|
||||||
|
if (ret)
|
||||||
|
goto err_clear_bit;
|
||||||
|
|
||||||
|
if (!wait_for_completion_timeout(&uc->complete, msecs_to_jiffies(5000)))
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
|
||||||
|
err_clear_bit:
|
||||||
|
clear_bit(DEV_CMD_PENDING, &uc->flags);
|
||||||
|
pm_runtime_put_sync(uc->dev);
|
||||||
|
mutex_unlock(&uc->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ucsi_ccg_sync(struct ucsi_ppm *ppm)
|
static const struct ucsi_operations ucsi_ccg_ops = {
|
||||||
{
|
.read = ucsi_ccg_read,
|
||||||
struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
|
.sync_write = ucsi_ccg_sync_write,
|
||||||
int status;
|
.async_write = ucsi_ccg_async_write
|
||||||
|
};
|
||||||
status = ucsi_ccg_recv_data(uc);
|
|
||||||
if (status < 0)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
/* ack interrupt to allow next command to run */
|
|
||||||
return ucsi_ccg_ack_interrupt(uc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ucsi_ccg_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl)
|
|
||||||
{
|
|
||||||
struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
|
|
||||||
|
|
||||||
ppm->data->ctrl.raw_cmd = ctrl->raw_cmd;
|
|
||||||
return ucsi_ccg_send_data(uc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static irqreturn_t ccg_irq_handler(int irq, void *data)
|
static irqreturn_t ccg_irq_handler(int irq, void *data)
|
||||||
{
|
{
|
||||||
|
u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_CCI);
|
||||||
struct ucsi_ccg *uc = data;
|
struct ucsi_ccg *uc = data;
|
||||||
|
u8 intr_reg;
|
||||||
|
u32 cci;
|
||||||
|
int ret;
|
||||||
|
|
||||||
ucsi_notify(uc->ucsi);
|
ret = ccg_read(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = ccg_read(uc, reg, (void *)&cci, sizeof(cci));
|
||||||
|
if (ret)
|
||||||
|
goto err_clear_irq;
|
||||||
|
|
||||||
|
if (UCSI_CCI_CONNECTOR(cci))
|
||||||
|
ucsi_connector_change(uc->ucsi, UCSI_CCI_CONNECTOR(cci));
|
||||||
|
|
||||||
|
if (test_bit(DEV_CMD_PENDING, &uc->flags) &&
|
||||||
|
cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))
|
||||||
|
complete(&uc->complete);
|
||||||
|
|
||||||
|
err_clear_irq:
|
||||||
|
ccg_write(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ccg_pm_workaround_work(struct work_struct *pm_work)
|
static void ccg_pm_workaround_work(struct work_struct *pm_work)
|
||||||
{
|
{
|
||||||
struct ucsi_ccg *uc = container_of(pm_work, struct ucsi_ccg, pm_work);
|
ccg_irq_handler(0, container_of(pm_work, struct ucsi_ccg, pm_work));
|
||||||
|
|
||||||
ucsi_notify(uc->ucsi);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_fw_info(struct ucsi_ccg *uc)
|
static int get_fw_info(struct ucsi_ccg *uc)
|
||||||
|
|
@ -1027,10 +1026,10 @@ static int ccg_restart(struct ucsi_ccg *uc)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
uc->ucsi = ucsi_register_ppm(dev, &uc->ppm);
|
status = ucsi_register(uc->ucsi);
|
||||||
if (IS_ERR(uc->ucsi)) {
|
if (status) {
|
||||||
dev_err(uc->dev, "ucsi_register_ppm failed\n");
|
dev_err(uc->dev, "failed to register the interface\n");
|
||||||
return PTR_ERR(uc->ucsi);
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1047,7 +1046,7 @@ static void ccg_update_firmware(struct work_struct *work)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (flash_mode != FLASH_NOT_NEEDED) {
|
if (flash_mode != FLASH_NOT_NEEDED) {
|
||||||
ucsi_unregister_ppm(uc->ucsi);
|
ucsi_unregister(uc->ucsi);
|
||||||
free_irq(uc->irq, uc);
|
free_irq(uc->irq, uc);
|
||||||
|
|
||||||
ccg_fw_update(uc, flash_mode);
|
ccg_fw_update(uc, flash_mode);
|
||||||
|
|
@ -1091,21 +1090,15 @@ static int ucsi_ccg_probe(struct i2c_client *client,
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
struct ucsi_ccg *uc;
|
struct ucsi_ccg *uc;
|
||||||
int status;
|
int status;
|
||||||
u16 rab;
|
|
||||||
|
|
||||||
uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL);
|
uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL);
|
||||||
if (!uc)
|
if (!uc)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data), GFP_KERNEL);
|
|
||||||
if (!uc->ppm.data)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
uc->ppm.cmd = ucsi_ccg_cmd;
|
|
||||||
uc->ppm.sync = ucsi_ccg_sync;
|
|
||||||
uc->dev = dev;
|
uc->dev = dev;
|
||||||
uc->client = client;
|
uc->client = client;
|
||||||
mutex_init(&uc->lock);
|
mutex_init(&uc->lock);
|
||||||
|
init_completion(&uc->complete);
|
||||||
INIT_WORK(&uc->work, ccg_update_firmware);
|
INIT_WORK(&uc->work, ccg_update_firmware);
|
||||||
INIT_WORK(&uc->pm_work, ccg_pm_workaround_work);
|
INIT_WORK(&uc->pm_work, ccg_pm_workaround_work);
|
||||||
|
|
||||||
|
|
@ -1133,30 +1126,25 @@ static int ucsi_ccg_probe(struct i2c_client *client,
|
||||||
if (uc->info.mode & CCG_DEVINFO_PDPORTS_MASK)
|
if (uc->info.mode & CCG_DEVINFO_PDPORTS_MASK)
|
||||||
uc->port_num++;
|
uc->port_num++;
|
||||||
|
|
||||||
|
uc->ucsi = ucsi_create(dev, &ucsi_ccg_ops);
|
||||||
|
if (IS_ERR(uc->ucsi))
|
||||||
|
return PTR_ERR(uc->ucsi);
|
||||||
|
|
||||||
|
ucsi_set_drvdata(uc->ucsi, uc);
|
||||||
|
|
||||||
status = request_threaded_irq(client->irq, NULL, ccg_irq_handler,
|
status = request_threaded_irq(client->irq, NULL, ccg_irq_handler,
|
||||||
IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
|
IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
|
||||||
dev_name(dev), uc);
|
dev_name(dev), uc);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
dev_err(uc->dev, "request_threaded_irq failed - %d\n", status);
|
dev_err(uc->dev, "request_threaded_irq failed - %d\n", status);
|
||||||
return status;
|
goto out_ucsi_destroy;
|
||||||
}
|
}
|
||||||
|
|
||||||
uc->irq = client->irq;
|
uc->irq = client->irq;
|
||||||
|
|
||||||
uc->ucsi = ucsi_register_ppm(dev, &uc->ppm);
|
status = ucsi_register(uc->ucsi);
|
||||||
if (IS_ERR(uc->ucsi)) {
|
if (status)
|
||||||
dev_err(uc->dev, "ucsi_register_ppm failed\n");
|
goto out_free_irq;
|
||||||
return PTR_ERR(uc->ucsi);
|
|
||||||
}
|
|
||||||
|
|
||||||
rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, version));
|
|
||||||
status = ccg_read(uc, rab, (u8 *)(uc->ppm.data) +
|
|
||||||
offsetof(struct ucsi_data, version),
|
|
||||||
sizeof(uc->ppm.data->version));
|
|
||||||
if (status < 0) {
|
|
||||||
ucsi_unregister_ppm(uc->ucsi);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
i2c_set_clientdata(client, uc);
|
i2c_set_clientdata(client, uc);
|
||||||
|
|
||||||
|
|
@ -1167,6 +1155,13 @@ static int ucsi_ccg_probe(struct i2c_client *client,
|
||||||
pm_runtime_idle(uc->dev);
|
pm_runtime_idle(uc->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out_free_irq:
|
||||||
|
free_irq(uc->irq, uc);
|
||||||
|
out_ucsi_destroy:
|
||||||
|
ucsi_destroy(uc->ucsi);
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ucsi_ccg_remove(struct i2c_client *client)
|
static int ucsi_ccg_remove(struct i2c_client *client)
|
||||||
|
|
@ -1175,8 +1170,9 @@ static int ucsi_ccg_remove(struct i2c_client *client)
|
||||||
|
|
||||||
cancel_work_sync(&uc->pm_work);
|
cancel_work_sync(&uc->pm_work);
|
||||||
cancel_work_sync(&uc->work);
|
cancel_work_sync(&uc->work);
|
||||||
ucsi_unregister_ppm(uc->ucsi);
|
|
||||||
pm_runtime_disable(uc->dev);
|
pm_runtime_disable(uc->dev);
|
||||||
|
ucsi_unregister(uc->ucsi);
|
||||||
|
ucsi_destroy(uc->ucsi);
|
||||||
free_irq(uc->irq, uc);
|
free_irq(uc->irq, uc);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user