Release 4.11 drivers/input/misc/ims-pcu.c
/*
* Driver for IMS Passenger Control Unit Devices
*
* Copyright (C) 2013 The IMS Company
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/ihex.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/usb/input.h>
#include <linux/usb/cdc.h>
#include <asm/unaligned.h>
#define IMS_PCU_KEYMAP_LEN 32
struct ims_pcu_buttons {
struct input_dev *input;
char name[32];
char phys[32];
unsigned short keymap[IMS_PCU_KEYMAP_LEN];
};
struct ims_pcu_gamepad {
struct input_dev *input;
char name[32];
char phys[32];
};
struct ims_pcu_backlight {
struct led_classdev cdev;
struct work_struct work;
enum led_brightness desired_brightness;
char name[32];
};
#define IMS_PCU_PART_NUMBER_LEN 15
#define IMS_PCU_SERIAL_NUMBER_LEN 8
#define IMS_PCU_DOM_LEN 8
#define IMS_PCU_FW_VERSION_LEN (9 + 1)
#define IMS_PCU_BL_VERSION_LEN (9 + 1)
#define IMS_PCU_BL_RESET_REASON_LEN (2 + 1)
#define IMS_PCU_PCU_B_DEVICE_ID 5
#define IMS_PCU_BUF_SIZE 128
struct ims_pcu {
struct usb_device *udev;
struct device *dev; /* control interface's device, used for logging */
unsigned int device_no;
bool bootloader_mode;
char part_number[IMS_PCU_PART_NUMBER_LEN];
char serial_number[IMS_PCU_SERIAL_NUMBER_LEN];
char date_of_manufacturing[IMS_PCU_DOM_LEN];
char fw_version[IMS_PCU_FW_VERSION_LEN];
char bl_version[IMS_PCU_BL_VERSION_LEN];
char reset_reason[IMS_PCU_BL_RESET_REASON_LEN];
int update_firmware_status;
u8 device_id;
u8 ofn_reg_addr;
struct usb_interface *ctrl_intf;
struct usb_endpoint_descriptor *ep_ctrl;
struct urb *urb_ctrl;
u8 *urb_ctrl_buf;
dma_addr_t ctrl_dma;
size_t max_ctrl_size;
struct usb_interface *data_intf;
struct usb_endpoint_descriptor *ep_in;
struct urb *urb_in;
u8 *urb_in_buf;
dma_addr_t read_dma;
size_t max_in_size;
struct usb_endpoint_descriptor *ep_out;
u8 *urb_out_buf;
size_t max_out_size;
u8 read_buf[IMS_PCU_BUF_SIZE];
u8 read_pos;
u8 check_sum;
bool have_stx;
bool have_dle;
u8 cmd_buf[IMS_PCU_BUF_SIZE];
u8 ack_id;
u8 expected_response;
u8 cmd_buf_len;
struct completion cmd_done;
struct mutex cmd_mutex;
u32 fw_start_addr;
u32 fw_end_addr;
struct completion async_firmware_done;
struct ims_pcu_buttons buttons;
struct ims_pcu_gamepad *gamepad;
struct ims_pcu_backlight backlight;
bool setup_complete; /* Input and LED devices have been created */
};
/*********************************************************************
* Buttons Input device support *
*********************************************************************/
static const unsigned short ims_pcu_keymap_1[] = {
[1] = KEY_ATTENDANT_OFF,
[2] = KEY_ATTENDANT_ON,
[3] = KEY_LIGHTS_TOGGLE,
[4] = KEY_VOLUMEUP,
[5] = KEY_VOLUMEDOWN,
[6] = KEY_INFO,
};
static const unsigned short ims_pcu_keymap_2[] = {
[4] = KEY_VOLUMEUP,
[5] = KEY_VOLUMEDOWN,
[6] = KEY_INFO,
};
static const unsigned short ims_pcu_keymap_3[] = {
[1] = KEY_HOMEPAGE,
[2] = KEY_ATTENDANT_TOGGLE,
[3] = KEY_LIGHTS_TOGGLE,
[4] = KEY_VOLUMEUP,
[5] = KEY_VOLUMEDOWN,
[6] = KEY_DISPLAYTOGGLE,
[18] = KEY_PLAYPAUSE,
};
static const unsigned short ims_pcu_keymap_4[] = {
[1] = KEY_ATTENDANT_OFF,
[2] = KEY_ATTENDANT_ON,
[3] = KEY_LIGHTS_TOGGLE,
[4] = KEY_VOLUMEUP,
[5] = KEY_VOLUMEDOWN,
[6] = KEY_INFO,
[18] = KEY_PLAYPAUSE,
};
static const unsigned short ims_pcu_keymap_5[] = {
[1] = KEY_ATTENDANT_OFF,
[2] = KEY_ATTENDANT_ON,
[3] = KEY_LIGHTS_TOGGLE,
};
struct ims_pcu_device_info {
const unsigned short *keymap;
size_t keymap_len;
bool has_gamepad;
};
#define IMS_PCU_DEVINFO(_n, _gamepad) \
[_n] = { \
.keymap = ims_pcu_keymap_##_n, \
.keymap_len = ARRAY_SIZE(ims_pcu_keymap_##_n), \
.has_gamepad = _gamepad, \
}
static const struct ims_pcu_device_info ims_pcu_device_info[] = {
IMS_PCU_DEVINFO(1, true),
IMS_PCU_DEVINFO(2, true),
IMS_PCU_DEVINFO(3, true),
IMS_PCU_DEVINFO(4, true),
IMS_PCU_DEVINFO(5, false),
};
static void ims_pcu_buttons_report(struct ims_pcu *pcu, u32 data)
{
struct ims_pcu_buttons *buttons = &pcu->buttons;
struct input_dev *input = buttons->input;
int i;
for (i = 0; i < 32; i++) {
unsigned short keycode = buttons->keymap[i];
if (keycode != KEY_RESERVED)
input_report_key(input, keycode, data & (1UL << i));
}
input_sync(input);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 88 | 100.00% | 1 | 100.00% |
Total | 88 | 100.00% | 1 | 100.00% |
static int ims_pcu_setup_buttons(struct ims_pcu *pcu,
const unsigned short *keymap,
size_t keymap_len)
{
struct ims_pcu_buttons *buttons = &pcu->buttons;
struct input_dev *input;
int i;
int error;
input = input_allocate_device();
if (!input) {
dev_err(pcu->dev,
"Not enough memory for input input device\n");
return -ENOMEM;
}
snprintf(buttons->name, sizeof(buttons->name),
"IMS PCU#%d Button Interface", pcu->device_no);
usb_make_path(pcu->udev, buttons->phys, sizeof(buttons->phys));
strlcat(buttons->phys, "/input0", sizeof(buttons->phys));
memcpy(buttons->keymap, keymap, sizeof(*keymap) * keymap_len);
input->name = buttons->name;
input->phys = buttons->phys;
usb_to_input_id(pcu->udev, &input->id);
input->dev.parent = &pcu->ctrl_intf->dev;
input->keycode = buttons->keymap;
input->keycodemax = ARRAY_SIZE(buttons->keymap);
input->keycodesize = sizeof(buttons->keymap[0]);
__set_bit(EV_KEY, input->evbit);
for (i = 0; i < IMS_PCU_KEYMAP_LEN; i++)
__set_bit(buttons->keymap[i], input->keybit);
__clear_bit(KEY_RESERVED, input->keybit);
error = input_register_device(input);
if (error) {
dev_err(pcu->dev,
"Failed to register buttons input device: %d\n",
error);
input_free_device(input);
return error;
}
buttons->input = input;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 297 | 100.00% | 1 | 100.00% |
Total | 297 | 100.00% | 1 | 100.00% |
static void ims_pcu_destroy_buttons(struct ims_pcu *pcu)
{
struct ims_pcu_buttons *buttons = &pcu->buttons;
input_unregister_device(buttons->input);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 28 | 100.00% | 1 | 100.00% |
Total | 28 | 100.00% | 1 | 100.00% |
/*********************************************************************
* Gamepad Input device support *
*********************************************************************/
static void ims_pcu_gamepad_report(struct ims_pcu *pcu, u32 data)
{
struct ims_pcu_gamepad *gamepad = pcu->gamepad;
struct input_dev *input = gamepad->input;
int x, y;
x = !!(data & (1 << 14)) - !!(data & (1 << 13));
y = !!(data & (1 << 12)) - !!(data & (1 << 11));
input_report_abs(input, ABS_X, x);
input_report_abs(input, ABS_Y, y);
input_report_key(input, BTN_A, data & (1 << 7));
input_report_key(input, BTN_B, data & (1 << 8));
input_report_key(input, BTN_X, data & (1 << 9));
input_report_key(input, BTN_Y, data & (1 << 10));
input_report_key(input, BTN_START, data & (1 << 15));
input_report_key(input, BTN_SELECT, data & (1 << 16));
input_sync(input);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 202 | 100.00% | 1 | 100.00% |
Total | 202 | 100.00% | 1 | 100.00% |
static int ims_pcu_setup_gamepad(struct ims_pcu *pcu)
{
struct ims_pcu_gamepad *gamepad;
struct input_dev *input;
int error;
gamepad = kzalloc(sizeof(struct ims_pcu_gamepad), GFP_KERNEL);
input = input_allocate_device();
if (!gamepad || !input) {
dev_err(pcu->dev,
"Not enough memory for gamepad device\n");
error = -ENOMEM;
goto err_free_mem;
}
gamepad->input = input;
snprintf(gamepad->name, sizeof(gamepad->name),
"IMS PCU#%d Gamepad Interface", pcu->device_no);
usb_make_path(pcu->udev, gamepad->phys, sizeof(gamepad->phys));
strlcat(gamepad->phys, "/input1", sizeof(gamepad->phys));
input->name = gamepad->name;
input->phys = gamepad->phys;
usb_to_input_id(pcu->udev, &input->id);
input->dev.parent = &pcu->ctrl_intf->dev;
__set_bit(EV_KEY, input->evbit);
__set_bit(BTN_A, input->keybit);
__set_bit(BTN_B, input->keybit);
__set_bit(BTN_X, input->keybit);
__set_bit(BTN_Y, input->keybit);
__set_bit(BTN_START, input->keybit);
__set_bit(BTN_SELECT, input->keybit);
__set_bit(EV_ABS, input->evbit);
input_set_abs_params(input, ABS_X, -1, 1, 0, 0);
input_set_abs_params(input, ABS_Y, -1, 1, 0, 0);
error = input_register_device(input);
if (error) {
dev_err(pcu->dev,
"Failed to register gamepad input device: %d\n",
error);
goto err_free_mem;
}
pcu->gamepad = gamepad;
return 0;
err_free_mem:
input_free_device(input);
kfree(gamepad);
return -ENOMEM;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 326 | 100.00% | 2 | 100.00% |
Total | 326 | 100.00% | 2 | 100.00% |
static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu)
{
struct ims_pcu_gamepad *gamepad = pcu->gamepad;
input_unregister_device(gamepad->input);
kfree(gamepad);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 32 | 100.00% | 1 | 100.00% |
Total | 32 | 100.00% | 1 | 100.00% |
/*********************************************************************
* PCU Communication protocol handling *
*********************************************************************/
#define IMS_PCU_PROTOCOL_STX 0x02
#define IMS_PCU_PROTOCOL_ETX 0x03
#define IMS_PCU_PROTOCOL_DLE 0x10
/* PCU commands */
#define IMS_PCU_CMD_STATUS 0xa0
#define IMS_PCU_CMD_PCU_RESET 0xa1
#define IMS_PCU_CMD_RESET_REASON 0xa2
#define IMS_PCU_CMD_SEND_BUTTONS 0xa3
#define IMS_PCU_CMD_JUMP_TO_BTLDR 0xa4
#define IMS_PCU_CMD_GET_INFO 0xa5
#define IMS_PCU_CMD_SET_BRIGHTNESS 0xa6
#define IMS_PCU_CMD_EEPROM 0xa7
#define IMS_PCU_CMD_GET_FW_VERSION 0xa8
#define IMS_PCU_CMD_GET_BL_VERSION 0xa9
#define IMS_PCU_CMD_SET_INFO 0xab
#define IMS_PCU_CMD_GET_BRIGHTNESS 0xac
#define IMS_PCU_CMD_GET_DEVICE_ID 0xae
#define IMS_PCU_CMD_SPECIAL_INFO 0xb0
#define IMS_PCU_CMD_BOOTLOADER 0xb1
/* Pass data to bootloader */
#define IMS_PCU_CMD_OFN_SET_CONFIG 0xb3
#define IMS_PCU_CMD_OFN_GET_CONFIG 0xb4
/* PCU responses */
#define IMS_PCU_RSP_STATUS 0xc0
#define IMS_PCU_RSP_PCU_RESET 0
/* Originally 0xc1 */
#define IMS_PCU_RSP_RESET_REASON 0xc2
#define IMS_PCU_RSP_SEND_BUTTONS 0xc3
#define IMS_PCU_RSP_JUMP_TO_BTLDR 0
/* Originally 0xc4 */
#define IMS_PCU_RSP_GET_INFO 0xc5
#define IMS_PCU_RSP_SET_BRIGHTNESS 0xc6
#define IMS_PCU_RSP_EEPROM 0xc7
#define IMS_PCU_RSP_GET_FW_VERSION 0xc8
#define IMS_PCU_RSP_GET_BL_VERSION 0xc9
#define IMS_PCU_RSP_SET_INFO 0xcb
#define IMS_PCU_RSP_GET_BRIGHTNESS 0xcc
#define IMS_PCU_RSP_CMD_INVALID 0xcd
#define IMS_PCU_RSP_GET_DEVICE_ID 0xce
#define IMS_PCU_RSP_SPECIAL_INFO 0xd0
#define IMS_PCU_RSP_BOOTLOADER 0xd1
/* Bootloader response */
#define IMS_PCU_RSP_OFN_SET_CONFIG 0xd2
#define IMS_PCU_RSP_OFN_GET_CONFIG 0xd3
#define IMS_PCU_RSP_EVNT_BUTTONS 0xe0
/* Unsolicited, button state */
#define IMS_PCU_GAMEPAD_MASK 0x0001ff80UL
/* Bits 7 through 16 */
#define IMS_PCU_MIN_PACKET_LEN 3
#define IMS_PCU_DATA_OFFSET 2
#define IMS_PCU_CMD_WRITE_TIMEOUT 100
/* msec */
#define IMS_PCU_CMD_RESPONSE_TIMEOUT 500
/* msec */
static void ims_pcu_report_events(struct ims_pcu *pcu)
{
u32 data = get_unaligned_be32(&pcu->read_buf[3]);
ims_pcu_buttons_report(pcu, data & ~IMS_PCU_GAMEPAD_MASK);
if (pcu->gamepad)
ims_pcu_gamepad_report(pcu, data);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 48 | 100.00% | 1 | 100.00% |
Total | 48 | 100.00% | 1 | 100.00% |
static void ims_pcu_handle_response(struct ims_pcu *pcu)
{
switch (pcu->read_buf[0]) {
case IMS_PCU_RSP_EVNT_BUTTONS:
if (likely(pcu->setup_complete))
ims_pcu_report_events(pcu);
break;
default:
/*
* See if we got command completion.
* If both the sequence and response code match save
* the data and signal completion.
*/
if (pcu->read_buf[0] == pcu->expected_response &&
pcu->read_buf[1] == pcu->ack_id - 1) {
memcpy(pcu->cmd_buf, pcu->read_buf, pcu->read_pos);
pcu->cmd_buf_len = pcu->read_pos;
complete(&pcu->cmd_done);
}
break;
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 102 | 100.00% | 1 | 100.00% |
Total | 102 | 100.00% | 1 | 100.00% |
static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
{
int i;
for (i = 0; i < urb->actual_length; i++) {
u8 data = pcu->urb_in_buf[i];
/* Skip everything until we get Start Xmit */
if (!pcu->have_stx && data != IMS_PCU_PROTOCOL_STX)
continue;
if (pcu->have_dle) {
pcu->have_dle = false;
pcu->read_buf[pcu->read_pos++] = data;
pcu->check_sum += data;
continue;
}
switch (data) {
case IMS_PCU_PROTOCOL_STX:
if (pcu->have_stx)
dev_warn(pcu->dev,
"Unexpected STX at byte %d, discarding old data\n",
pcu->read_pos);
pcu->have_stx = true;
pcu->have_dle = false;
pcu->read_pos = 0;
pcu->check_sum = 0;
break;
case IMS_PCU_PROTOCOL_DLE:
pcu->have_dle = true;
break;
case IMS_PCU_PROTOCOL_ETX:
if (pcu->read_pos < IMS_PCU_MIN_PACKET_LEN) {
dev_warn(pcu->dev,
"Short packet received (%d bytes), ignoring\n",
pcu->read_pos);
} else if (pcu->check_sum != 0) {
dev_warn(pcu->dev,
"Invalid checksum in packet (%d bytes), ignoring\n",
pcu->read_pos);
} else {
ims_pcu_handle_response(pcu);
}
pcu->have_stx = false;
pcu->have_dle = false;
pcu->read_pos = 0;
break;
default:
pcu->read_buf[pcu->read_pos++] = data;
pcu->check_sum += data;
break;
}
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 252 | 100.00% | 1 | 100.00% |
Total | 252 | 100.00% | 1 | 100.00% |
static bool ims_pcu_byte_needs_escape(u8 byte)
{
return byte == IMS_PCU_PROTOCOL_STX ||
byte == IMS_PCU_PROTOCOL_ETX ||
byte == IMS_PCU_PROTOCOL_DLE;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 22 | 100.00% | 1 | 100.00% |
Total | 22 | 100.00% | 1 | 100.00% |
static int ims_pcu_send_cmd_chunk(struct ims_pcu *pcu,
u8 command, int chunk, int len)
{
int error;
error = usb_bulk_msg(pcu->udev,
usb_sndbulkpipe(pcu->udev,
pcu->ep_out->bEndpointAddress),
pcu->urb_out_buf, len,
NULL, IMS_PCU_CMD_WRITE_TIMEOUT);
if (error < 0) {
dev_dbg(pcu->dev,
"Sending 0x%02x command failed at chunk %d: %d\n",
command, chunk, error);
return error;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 84 | 100.00% | 1 | 100.00% |
Total | 84 | 100.00% | 1 | 100.00% |
static int ims_pcu_send_command(struct ims_pcu *pcu,
u8 command, const u8 *data, int len)
{
int count = 0;
int chunk = 0;
int delta;
int i;
int error;
u8 csum = 0;
u8 ack_id;
pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_STX;
/* We know the command need not be escaped */
pcu->urb_out_buf[count++] = command;
csum += command;
ack_id = pcu->ack_id++;
if (ack_id == 0xff)
ack_id = pcu->ack_id++;
if (ims_pcu_byte_needs_escape(ack_id))
pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE;
pcu->urb_out_buf[count++] = ack_id;
csum += ack_id;
for (i = 0; i < len; i++) {
delta = ims_pcu_byte_needs_escape(data[i]) ? 2 : 1;
if (count + delta >= pcu->max_out_size) {
error = ims_pcu_send_cmd_chunk(pcu, command,
++chunk, count);
if (error)
return error;
count = 0;
}
if (delta == 2)
pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE;
pcu->urb_out_buf[count++] = data[i];
csum += data[i];
}
csum = 1 + ~csum;
delta = ims_pcu_byte_needs_escape(csum) ? 3 : 2;
if (count + delta >= pcu->max_out_size) {
error = ims_pcu_send_cmd_chunk(pcu, command, ++chunk, count);
if (error)
return error;
count = 0;
}
if (delta == 3)
pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE;
pcu->urb_out_buf[count++] = csum;
pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_ETX;
return ims_pcu_send_cmd_chunk(pcu, command, ++chunk, count);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 331 | 100.00% | 1 | 100.00% |
Total | 331 | 100.00% | 1 | 100.00% |
static int __ims_pcu_execute_command(struct ims_pcu *pcu,
u8 command, const void *data, size_t len,
u8 expected_response, int response_time)
{
int error;
pcu->expected_response = expected_response;
init_completion(&pcu->cmd_done);
error = ims_pcu_send_command(pcu, command, data, len);
if (error)
return error;
if (expected_response &&
!wait_for_completion_timeout(&pcu->cmd_done,
msecs_to_jiffies(response_time))) {
dev_dbg(pcu->dev, "Command 0x%02x timed out\n", command);
return -ETIMEDOUT;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Dmitry Torokhov | 103 | 100.00% | 1 | 100.00% |
Total | 103 | 100.00% | 1 | 100.00% |
#define ims_pcu_execute_command(pcu, code, data, len) \
__ims_pcu_execute_command(pcu, \
IMS_PCU_CMD_##code, data, len, \
IMS_PCU_RSP_##code, \
IMS_PCU_CMD_RESPONSE_TIMEOUT)
#define ims_pcu_execute_query(pcu, code) \
ims_pcu_execute_command(pcu, code, NULL, 0)
/* Bootloader commands */
#define IMS_PCU_BL_CMD_QUERY_DEVICE 0xa1
#define IMS_PCU_BL_CMD_UNLOCK_CONFIG 0xa2
#define IMS_PCU_BL_CMD_ERASE_APP 0xa3
#define IMS_PCU_BL_CMD_PROGRAM_DEVICE 0xa4
#define IMS_PCU_BL_CMD_PROGRAM_COMPLETE 0xa5
#define IMS_PCU_BL_CMD_READ_APP 0xa6
#define IMS_PCU_BL_CMD_RESET_DEVICE 0xa7
#define IMS_PCU_BL_CMD_LAUNCH_APP 0xa8
/* Bootloader commands */
#define IMS_PCU_BL_RSP_QUERY_DEVICE 0xc1
#define IMS_PCU_BL_RSP_UNLOCK_CONFIG 0xc2
#define IMS_PCU_BL_RSP_ERASE_APP 0xc3
#define IMS_PCU_BL_RSP_PROGRAM_DEVICE 0xc4
#define IMS_PCU_BL_RSP_PROGRAM_COMPLETE 0xc5
#define IMS_PCU_BL_RSP_READ_APP 0xc6
#define IMS_PCU_BL_RSP_RESET_DEVICE 0
/* originally 0xa7 */
#define IMS_PCU_BL_RSP_LAUNCH_APP 0
/* originally 0xa8 */
#define IMS_PCU_BL_DATA_OFFSET 3
static int __ims_pcu_execute_bl_command(struct ims_pcu *pcu,
u8 command, const void *data, size_t len,
u8 expected_response, int response_time)
{
int error;
pcu->cmd_buf[0] = command;
if (data)
memcpy(&pcu->cmd_buf[1], data, len);
error = __ims_pcu_execute_command(pcu,
IMS_PCU_CMD_BOOTLOADER, pcu->cmd_buf, len + 1,
expected_response ? IMS_PCU_RSP_BOOTLOADER : 0,
response_time);
if (error) {
dev_err(pcu->dev,
"Failure when sending 0x%02x command to bootloader, error: %d\n",
pcu->cmd_buf[0], error);
return error;
}
if (expected_response && pcu->cmd_buf[2] != expected_response)