Message ID | 20231207-tps6598x_update-v2-4-f3cfcde6d890@wolfvision.net (mailing list archive) |
---|---|
State | Accepted |
Commit | 4c3ea81aa8e11400f24e5541bf46c2cadb4202e9 |
Headers | show |
Series | usb: typec: tipd: add patch update support for tps6598x | expand |
On Thu, Dec 14, 2023 at 05:29:12PM +0100, Javier Carrasco wrote: > The TPS6598x PD controller supports firmware updates that can be loaded > either from an external flash memory or a host using the device's I2C > host interface. This patch implements the second approach, which is > especially relevant if no flash memory is available. > > In order to make patch bundle updates, a series of tasks (special > commands) must be sent to the device as it is documented in the > TPS65987DDH and TPS65988DH Host Interface Technical Reference Manual[1], > section 4.11 (Patch Bundle Update Tasks). > > The update sequence is as follows: > 1. PTCs - Start Patch Load Sequence: the proposed approach includes > device and application configuration data. > 2. PTCd - Patch Download: 64-byte data chunks must be sent until the end > of the firmware file is reached (the last chunk may be shorter). > 3. PTCc - Patch Data Transfer Complete: ends the patch loading sequence. > > After this sequence and if no errors occurred, the device will change > its mode to 'APP' after SETUP_MS milliseconds, and then it will be ready > for normal operation. > > [1] https://www.ti.com/lit/ug/slvubh2b/slvubh2b.pdf?ts=1697623299919&ref_url=https%253A%252F%252Fwww.ti.com%252Fproduct%252FTPS65987D > > Signed-off-by: Javier Carrasco <javier.carrasco@wolfvision.net> Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> > --- > drivers/usb/typec/tipd/core.c | 68 ++++++++++++++++++++++++++++++++++++++- > drivers/usb/typec/tipd/tps6598x.h | 18 +++++++++++ > 2 files changed, 85 insertions(+), 1 deletion(-) > > diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c > index 7f4bbc0629b0..a956eb976906 100644 > --- a/drivers/usb/typec/tipd/core.c > +++ b/drivers/usb/typec/tipd/core.c > @@ -1125,6 +1125,71 @@ static int tps25750_apply_patch(struct tps6598x *tps) > return 0; > }; > > +static int tps6598x_apply_patch(struct tps6598x *tps) > +{ > + u8 in = TPS_PTCS_CONTENT_DEV | TPS_PTCS_CONTENT_APP; > + u8 out[TPS_MAX_LEN] = {0}; > + size_t in_len = sizeof(in); > + size_t copied_bytes = 0; > + size_t bytes_left; > + const struct firmware *fw; > + const char *firmware_name; > + int ret; > + > + ret = device_property_read_string(tps->dev, "firmware-name", > + &firmware_name); > + if (ret) > + return ret; > + > + ret = tps_request_firmware(tps, &fw); > + if (ret) > + return ret; > + > + ret = tps6598x_exec_cmd(tps, "PTCs", in_len, &in, > + TPS_PTCS_OUT_BYTES, out); > + if (ret || out[TPS_PTCS_STATUS] == TPS_PTCS_STATUS_FAIL) { > + if (!ret) > + ret = -EBUSY; > + dev_err(tps->dev, "Update start failed (%d)\n", ret); > + goto release_fw; > + } > + > + bytes_left = fw->size; > + while (bytes_left) { > + if (bytes_left < TPS_MAX_LEN) > + in_len = bytes_left; > + else > + in_len = TPS_MAX_LEN; > + ret = tps6598x_exec_cmd(tps, "PTCd", in_len, > + fw->data + copied_bytes, > + TPS_PTCD_OUT_BYTES, out); > + if (ret || out[TPS_PTCD_TRANSFER_STATUS] || > + out[TPS_PTCD_LOADING_STATE] == TPS_PTCD_LOAD_ERR) { > + if (!ret) > + ret = -EBUSY; > + dev_err(tps->dev, "Patch download failed (%d)\n", ret); > + goto release_fw; > + } > + copied_bytes += in_len; > + bytes_left -= in_len; > + } > + > + ret = tps6598x_exec_cmd(tps, "PTCc", 0, NULL, TPS_PTCC_OUT_BYTES, out); > + if (ret || out[TPS_PTCC_DEV] || out[TPS_PTCC_APP]) { > + if (!ret) > + ret = -EBUSY; > + dev_err(tps->dev, "Update completion failed (%d)\n", ret); > + goto release_fw; > + } > + msleep(TPS_SETUP_MS); > + dev_info(tps->dev, "Firmware update succeeded\n"); > + > +release_fw: > + release_firmware(fw); > + > + return ret; > +}; > + > static int cd321x_init(struct tps6598x *tps) > { > return 0; > @@ -1150,7 +1215,7 @@ static int tps25750_init(struct tps6598x *tps) > > static int tps6598x_init(struct tps6598x *tps) > { > - return 0; > + return tps->data->apply_patch(tps); > } > > static int cd321x_reset(struct tps6598x *tps) > @@ -1468,6 +1533,7 @@ static const struct tipd_data tps6598x_data = { > .register_port = tps6598x_register_port, > .trace_power_status = trace_tps6598x_power_status, > .trace_status = trace_tps6598x_status, > + .apply_patch = tps6598x_apply_patch, > .init = tps6598x_init, > .reset = tps6598x_reset, > }; > diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h > index 01609bf509e4..89b24519463a 100644 > --- a/drivers/usb/typec/tipd/tps6598x.h > +++ b/drivers/usb/typec/tipd/tps6598x.h > @@ -235,4 +235,22 @@ > /* SLEEP CONF REG */ > #define TPS_SLEEP_CONF_SLEEP_MODE_ALLOWED BIT(0) > > +/* Start Patch Download Sequence */ > +#define TPS_PTCS_CONTENT_APP BIT(0) > +#define TPS_PTCS_CONTENT_DEV BIT(1) > +#define TPS_PTCS_OUT_BYTES 4 > +#define TPS_PTCS_STATUS 1 > + > +#define TPS_PTCS_STATUS_FAIL 0x80 > +/* Patch Download */ > +#define TPS_PTCD_OUT_BYTES 10 > +#define TPS_PTCD_TRANSFER_STATUS 1 > +#define TPS_PTCD_LOADING_STATE 2 > + > +#define TPS_PTCD_LOAD_ERR 0x09 > +/* Patch Download Complete */ > +#define TPS_PTCC_OUT_BYTES 4 > +#define TPS_PTCC_DEV 2 > +#define TPS_PTCC_APP 3 > + > #endif /* __TPS6598X_H__ */ > > -- > 2.39.2
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 7f4bbc0629b0..a956eb976906 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -1125,6 +1125,71 @@ static int tps25750_apply_patch(struct tps6598x *tps) return 0; }; +static int tps6598x_apply_patch(struct tps6598x *tps) +{ + u8 in = TPS_PTCS_CONTENT_DEV | TPS_PTCS_CONTENT_APP; + u8 out[TPS_MAX_LEN] = {0}; + size_t in_len = sizeof(in); + size_t copied_bytes = 0; + size_t bytes_left; + const struct firmware *fw; + const char *firmware_name; + int ret; + + ret = device_property_read_string(tps->dev, "firmware-name", + &firmware_name); + if (ret) + return ret; + + ret = tps_request_firmware(tps, &fw); + if (ret) + return ret; + + ret = tps6598x_exec_cmd(tps, "PTCs", in_len, &in, + TPS_PTCS_OUT_BYTES, out); + if (ret || out[TPS_PTCS_STATUS] == TPS_PTCS_STATUS_FAIL) { + if (!ret) + ret = -EBUSY; + dev_err(tps->dev, "Update start failed (%d)\n", ret); + goto release_fw; + } + + bytes_left = fw->size; + while (bytes_left) { + if (bytes_left < TPS_MAX_LEN) + in_len = bytes_left; + else + in_len = TPS_MAX_LEN; + ret = tps6598x_exec_cmd(tps, "PTCd", in_len, + fw->data + copied_bytes, + TPS_PTCD_OUT_BYTES, out); + if (ret || out[TPS_PTCD_TRANSFER_STATUS] || + out[TPS_PTCD_LOADING_STATE] == TPS_PTCD_LOAD_ERR) { + if (!ret) + ret = -EBUSY; + dev_err(tps->dev, "Patch download failed (%d)\n", ret); + goto release_fw; + } + copied_bytes += in_len; + bytes_left -= in_len; + } + + ret = tps6598x_exec_cmd(tps, "PTCc", 0, NULL, TPS_PTCC_OUT_BYTES, out); + if (ret || out[TPS_PTCC_DEV] || out[TPS_PTCC_APP]) { + if (!ret) + ret = -EBUSY; + dev_err(tps->dev, "Update completion failed (%d)\n", ret); + goto release_fw; + } + msleep(TPS_SETUP_MS); + dev_info(tps->dev, "Firmware update succeeded\n"); + +release_fw: + release_firmware(fw); + + return ret; +}; + static int cd321x_init(struct tps6598x *tps) { return 0; @@ -1150,7 +1215,7 @@ static int tps25750_init(struct tps6598x *tps) static int tps6598x_init(struct tps6598x *tps) { - return 0; + return tps->data->apply_patch(tps); } static int cd321x_reset(struct tps6598x *tps) @@ -1468,6 +1533,7 @@ static const struct tipd_data tps6598x_data = { .register_port = tps6598x_register_port, .trace_power_status = trace_tps6598x_power_status, .trace_status = trace_tps6598x_status, + .apply_patch = tps6598x_apply_patch, .init = tps6598x_init, .reset = tps6598x_reset, }; diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h index 01609bf509e4..89b24519463a 100644 --- a/drivers/usb/typec/tipd/tps6598x.h +++ b/drivers/usb/typec/tipd/tps6598x.h @@ -235,4 +235,22 @@ /* SLEEP CONF REG */ #define TPS_SLEEP_CONF_SLEEP_MODE_ALLOWED BIT(0) +/* Start Patch Download Sequence */ +#define TPS_PTCS_CONTENT_APP BIT(0) +#define TPS_PTCS_CONTENT_DEV BIT(1) +#define TPS_PTCS_OUT_BYTES 4 +#define TPS_PTCS_STATUS 1 + +#define TPS_PTCS_STATUS_FAIL 0x80 +/* Patch Download */ +#define TPS_PTCD_OUT_BYTES 10 +#define TPS_PTCD_TRANSFER_STATUS 1 +#define TPS_PTCD_LOADING_STATE 2 + +#define TPS_PTCD_LOAD_ERR 0x09 +/* Patch Download Complete */ +#define TPS_PTCC_OUT_BYTES 4 +#define TPS_PTCC_DEV 2 +#define TPS_PTCC_APP 3 + #endif /* __TPS6598X_H__ */
The TPS6598x PD controller supports firmware updates that can be loaded either from an external flash memory or a host using the device's I2C host interface. This patch implements the second approach, which is especially relevant if no flash memory is available. In order to make patch bundle updates, a series of tasks (special commands) must be sent to the device as it is documented in the TPS65987DDH and TPS65988DH Host Interface Technical Reference Manual[1], section 4.11 (Patch Bundle Update Tasks). The update sequence is as follows: 1. PTCs - Start Patch Load Sequence: the proposed approach includes device and application configuration data. 2. PTCd - Patch Download: 64-byte data chunks must be sent until the end of the firmware file is reached (the last chunk may be shorter). 3. PTCc - Patch Data Transfer Complete: ends the patch loading sequence. After this sequence and if no errors occurred, the device will change its mode to 'APP' after SETUP_MS milliseconds, and then it will be ready for normal operation. [1] https://www.ti.com/lit/ug/slvubh2b/slvubh2b.pdf?ts=1697623299919&ref_url=https%253A%252F%252Fwww.ti.com%252Fproduct%252FTPS65987D Signed-off-by: Javier Carrasco <javier.carrasco@wolfvision.net> --- drivers/usb/typec/tipd/core.c | 68 ++++++++++++++++++++++++++++++++++++++- drivers/usb/typec/tipd/tps6598x.h | 18 +++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-)