[v13,2/2] usb: typec: ucsi: add support for Cypress CCGx
From: Ajay Gupta <ajayg@nvidia.com>
Date: 2018-10-25 21:55:47
Hi Heikki and Andy [...]
quoted
quoted
Shouldn't you return -ETIMEDOUT if count == 0?Yes. Good catch. Does the below fix looks ok? do { status = ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); if (status < 0) return status; usleep_range(10000, 11000); status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); if (status < 0) return status; if (!data) return 0; } while (data && count--);Doesn't that condition break out of the loop immediately?
How? I didn't get your point? We want to break out when data is zero (interrupt status cleared).
quoted
Ah, I see, but why you not reorganize it to put this into do-while loop?
We actually need to check data after reading it so will reorganize accordingly.
do {
read
check for data and break out if (!data)
write
sleep
} while (--count);
Thanks
Ajay
-nvpublicquoted
return -ETIMEDOUT;quoted
Something like: ... while (count--) status = ccg_read(uc, CCGX_RAB_INTR_REG, &data,sizeof(data));quoted
quoted
if (status < 0) return status; if (!data) return 0; } return -ETIMEDOUT; Or does the count of 10 have some specific meaning?quoted
+} + +static int ucsi_ccg_send_data(struct ucsi_ccg *uc) { + u8 *ppm = (u8 *)uc->ppm.data; + int status; + u16 rab; + + rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data,message_out));quoted
+ 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) { + u8 *ppm = (u8 *)uc->ppm.data; + int status; + u16 rab; + + rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, cci)); + 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));quoted
+ 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) { + int status; + unsigned char data; + + status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); + if (status < 0) + return status; + + return ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data)); } + +static int ucsi_ccg_sync(struct ucsi_ppm *ppm) { + struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm); + int status; + + 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) { + struct ucsi_ccg *uc = data; + + ucsi_notify(uc->ucsi); + + return IRQ_HANDLED; +} + +static int ucsi_ccg_probe(struct i2c_client *client, + const struct i2c_device_id *id) { + struct device *dev = &client->dev; + struct ucsi_ccg *uc; + int status; + u16 rab; + + uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL); + if (!uc) + return -ENOMEM; + + uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data),GFP_KERNEL);quoted
+ if (!uc->ppm.data) + return -ENOMEM; + + uc->ppm.cmd = ucsi_ccg_cmd; + uc->ppm.sync = ucsi_ccg_sync; + uc->dev = dev; + uc->client = client; + + /* reset ccg device and initialize ucsi */ + status = ucsi_ccg_init(uc); + if (status < 0) { + dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status); + return status; + } + + uc->irq = client->irq; + + status = devm_request_threaded_irq(dev, uc->irq, NULL,ccg_irq_handler,quoted
+ IRQF_ONESHOT |IRQF_TRIGGER_HIGH,quoted
+ dev_name(dev), uc); + if (status < 0) { + dev_err(uc->dev, "request_threaded_irq failed - %d\n",status);quoted
+ return status; + } + + uc->ucsi = ucsi_register_ppm(dev, &uc->ppm); + if (IS_ERR(uc->ucsi)) { + dev_err(uc->dev, "ucsi_register_ppm failed\n"); + return PTR_ERR(uc->ucsi); + } + + rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data,version));quoted
+ 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); + return 0; +} + +static int ucsi_ccg_remove(struct i2c_client *client) { + struct ucsi_ccg *uc = i2c_get_clientdata(client); + + ucsi_unregister_ppm(uc->ucsi); + + return 0; +} + +static const struct i2c_device_id ucsi_ccg_device_id[] = { + {"ccgx-ucsi", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); + +static struct i2c_driver ucsi_ccg_driver = { + .driver = { + .name = "ucsi_ccg", + }, + .probe = ucsi_ccg_probe, + .remove = ucsi_ccg_remove, + .id_table = ucsi_ccg_device_id, +}; + +module_i2c_driver(ucsi_ccg_driver); + +MODULE_AUTHOR("Ajay Gupta [off-list ref]"); +MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C +controller"); MODULE_LICENSE("GPL v2");I'm still worried about how this driver works on other platforms. It just looks like you have written ccg_read/write() functions for only yourI2C host.quoted
quoted
I would feel much more comfortable with this if you for example used those i2c_smbus_read/write*() helpers instead of raw i2c_transfer(). I would expect them to force you to write your i2c host driver, as well as this driver, in a more generic fashion.I2c_smbus_read/write*() will not work with Cypress CCGx controller since CCGx requires 2 byte of command for any read/write transaction. I2c_smbus_read/write*() APIs support only 1 byte of command.OK, got it. thanks, -- heikki