usb: it82xx2: fix issue with the unexpected setting of the ready bit

In the IT82xx2 chip, the ready bit is automatically cleared by
hardware. When setting other bits, there is a chance that the ready bit
hasn't been cleared yet, leading to unexpected USB transactions. This
change ensures that the ready bit is always set to '0' when setting the
endpoint control value.

Signed-off-by: Ren Chen <Ren.Chen@ite.com.tw>
This commit is contained in:
Ren Chen 2024-08-28 14:16:49 +08:00 committed by Fabio Baltieri
commit 1c769c342b

View file

@ -81,8 +81,12 @@ enum it82xx2_transaction_types {
#define DC_CONNECT_TO_HOST BIT(6) /* internal pull-up */
/* ENDPOINT[3..0]_CONTROL_REG */
#define ENDPOINT_EN BIT(0)
#define EP_SEND_STALL BIT(3)
#define ENDPOINT_ENABLE_BIT BIT(0)
#define ENDPOINT_READY_BIT BIT(1)
#define ENDPOINT_OUTDATA_SEQ_BIT BIT(2)
#define ENDPOINT_SEND_STALL_BIT BIT(3)
#define ENDPOINT_ISO_ENABLE_BIT BIT(4)
#define ENDPOINT_DIRECTION_BIT BIT(5)
enum it82xx2_ep_status {
EP_INIT = 0,
@ -417,14 +421,11 @@ static int it82xx2_usb_extend_ep_ctrl(uint8_t ep, enum it82xx2_ep_ctrl ctrl, boo
}
break;
case EP_READY_ENABLE:
unsigned int key;
int idx = ((ep_idx - 4) % 3) + 1;
key = irq_lock();
(enable) ? (ext_ctrl[idx].epn_ext_ctrl2 |= BIT((ep_idx - 4) / 3))
: (ext_ctrl[idx].epn_ext_ctrl2 &= ~BIT((ep_idx - 4) / 3));
ep_regs[ep_fifo].ep_ctrl.fields.ready_bit = enable;
irq_unlock(key);
break;
default:
LOG_ERR("Unknown control type 0x%x", ctrl);
@ -438,51 +439,72 @@ static int it82xx2_usb_ep_ctrl(uint8_t ep, enum it82xx2_ep_ctrl ctrl, bool enabl
{
struct usb_it82xx2_regs *const usb_regs = it82xx2_get_usb_regs();
struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs;
uint8_t ep_ctrl_value;
uint8_t ep_idx = USB_EP_GET_IDX(ep);
if (IT8XXX2_IS_EXTEND_ENDPOINT(ep_idx)) {
return -EINVAL;
}
ep_ctrl_value = ep_regs[ep_idx].ep_ctrl.value & ~ENDPOINT_READY_BIT;
switch (ctrl) {
case EP_IN_DIRECTION_SET:
ep_regs[ep_idx].ep_ctrl.fields.direction_bit = enable;
if (enable) {
ep_ctrl_value |= ENDPOINT_DIRECTION_BIT;
} else {
ep_ctrl_value &= ~ENDPOINT_DIRECTION_BIT;
}
break;
case EP_STALL_SEND:
ep_regs[ep_idx].ep_ctrl.fields.send_stall_bit = enable;
if (enable) {
ep_ctrl_value |= ENDPOINT_SEND_STALL_BIT;
} else {
ep_ctrl_value &= ~ENDPOINT_SEND_STALL_BIT;
}
break;
case EP_STALL_CHECK:
return ep_regs[ep_idx].ep_ctrl.fields.send_stall_bit;
case EP_IOS_ENABLE:
ep_regs[ep_idx].ep_ctrl.fields.iso_enable_bit = enable;
if (enable) {
ep_ctrl_value |= ENDPOINT_ISO_ENABLE_BIT;
} else {
ep_ctrl_value &= ~ENDPOINT_ISO_ENABLE_BIT;
}
break;
case EP_ENABLE:
ep_regs[ep_idx].ep_ctrl.fields.enable_bit = enable;
if (enable) {
ep_ctrl_value |= ENDPOINT_ENABLE_BIT;
} else {
ep_ctrl_value &= ~ENDPOINT_ENABLE_BIT;
}
break;
case EP_READY_ENABLE:
unsigned int key;
key = irq_lock();
ep_regs[ep_idx].ep_ctrl.fields.ready_bit = enable;
irq_unlock(key);
if (enable) {
ep_ctrl_value |= ENDPOINT_READY_BIT;
} else {
ep_ctrl_value &= ~ENDPOINT_READY_BIT;
}
break;
case EP_DATA_SEQ_1:
ep_regs[ep_idx].ep_ctrl.fields.outdata_sequence_bit = enable;
if (enable) {
ep_ctrl_value |= ENDPOINT_OUTDATA_SEQ_BIT;
} else {
ep_ctrl_value &= ~ENDPOINT_OUTDATA_SEQ_BIT;
}
break;
case EP_DATA_SEQ_TOGGLE:
if (!enable) {
break;
}
if (ep_regs[ep_idx].ep_ctrl.fields.outdata_sequence_bit) {
ep_regs[ep_idx].ep_ctrl.fields.outdata_sequence_bit = 0;
} else {
ep_regs[ep_idx].ep_ctrl.fields.outdata_sequence_bit = 1;
}
ep_ctrl_value ^= ENDPOINT_OUTDATA_SEQ_BIT;
break;
default:
LOG_ERR("Unknown control type 0x%x", ctrl);
return -EINVAL;
}
ep_regs[ep_idx].ep_ctrl.value = ep_ctrl_value;
return 0;
}
@ -490,12 +512,15 @@ static int it82xx2_usb_set_ep_ctrl(uint8_t ep, enum it82xx2_ep_ctrl ctrl, bool e
{
uint8_t ep_idx = USB_EP_GET_IDX(ep);
int ret = 0;
unsigned int key;
key = irq_lock();
if (IT8XXX2_IS_EXTEND_ENDPOINT(ep_idx)) {
ret = it82xx2_usb_extend_ep_ctrl(ep, ctrl, enable);
} else {
ret = it82xx2_usb_ep_ctrl(ep, ctrl, enable);
}
irq_unlock(key);
return ret;
}
@ -548,7 +573,7 @@ static bool it82xx2_check_setup_following_out(void)
ff_regs[EP0].ep_rx_fifo_dcnt_lsb == SETUP_DATA_CNT));
}
static inline void it82xx2_handler_setup(uint8_t fifo_idx, uint8_t ep_ctrl)
static inline void it82xx2_handler_setup(uint8_t fifo_idx)
{
struct usb_it82xx2_regs *const usb_regs = it82xx2_get_usb_regs();
struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs;
@ -556,8 +581,8 @@ static inline void it82xx2_handler_setup(uint8_t fifo_idx, uint8_t ep_ctrl)
uint8_t ep_idx = fifo_idx;
/* wrong trans */
if (ep_ctrl & EP_SEND_STALL) {
ep_regs[fifo_idx].ep_ctrl.fields.send_stall_bit = 0;
if (ep_regs[ep_idx].ep_ctrl.fields.send_stall_bit) {
it82xx2_usb_set_ep_ctrl(fifo_idx, EP_STALL_SEND, false);
udata0.st_state = STALL_SEND;
ff_regs[fifo_idx].ep_rx_fifo_ctrl = FIFO_FORCE_EMPTY;
LOG_DBG("Clear Stall Bit & RX FIFO");
@ -596,14 +621,14 @@ static inline void it82xx2_handler_setup(uint8_t fifo_idx, uint8_t ep_ctrl)
}
}
static inline void it82xx2_handler_in(const uint8_t ep_idx, const uint8_t ep_ctrl)
static inline void it82xx2_handler_in(const uint8_t ep_idx)
{
struct usb_it82xx2_regs *const usb_regs = it82xx2_get_usb_regs();
struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs;
if (ep_idx == 0) {
if (ep_ctrl & EP_SEND_STALL) {
ep_regs[ep_idx].ep_ctrl.fields.send_stall_bit = 0;
if (ep_regs[ep_idx].ep_ctrl.fields.send_stall_bit) {
it82xx2_usb_set_ep_ctrl(ep_idx, EP_STALL_SEND, false);
udata0.st_state = STALL_SEND;
LOG_DBG("Clear Stall Bit");
return;
@ -657,9 +682,6 @@ static inline void it82xx2_handler_in(const uint8_t ep_idx, const uint8_t ep_ctr
static inline void it82xx2_handler_out(const uint8_t ep_idx)
{
struct usb_it82xx2_regs *const usb_regs = it82xx2_get_usb_regs();
struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs;
if (ep_idx == 0) {
/* ep0 wrong enter check */
if (udata0.st_state >= STATUS_ST) {
@ -688,7 +710,7 @@ static inline void it82xx2_handler_out(const uint8_t ep_idx)
udata0.last_token = udata0.now_token;
udata0.now_token = SETUP_TOKEN;
udata0.st_state = SETUP_ST;
ep_regs[ep_idx].ep_ctrl.fields.outdata_sequence_bit = 1;
it82xx2_usb_set_ep_ctrl(ep_idx, EP_DATA_SEQ_1, true);
udata0.ep_data[ep_idx].cb_out(ep_idx | USB_EP_DIR_OUT, USB_DC_EP_SETUP);
if (udata0.no_data_ctrl) {
@ -799,12 +821,10 @@ static bool it82xx2_usb_fake_token(const uint8_t ep_idx, uint8_t *token_type)
static void it82xx2_usb_dc_trans_done(void)
{
struct usb_it82xx2_regs *const usb_regs = it82xx2_get_usb_regs();
struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs;
struct epn_ext_ctrl_regs *epn_ext_ctrl =
usb_regs->fifo_regs[EP_EXT_REGS_DX].ext_0_3.epn_ext_ctrl;
for (uint8_t fifo_idx = 0; fifo_idx < 4; fifo_idx++) {
uint8_t ep_ctrl = ep_regs[fifo_idx].ep_ctrl.value;
uint8_t ep_idx, token_type;
if (fifo_idx == 0) {
@ -819,10 +839,10 @@ static void it82xx2_usb_dc_trans_done(void)
if (!it82xx2_usb_fake_token(ep_idx, &token_type)) {
switch (token_type) {
case DC_SETUP_TRANS:
it82xx2_handler_setup(fifo_idx, ep_ctrl);
it82xx2_handler_setup(fifo_idx);
break;
case DC_IN_TRANS:
it82xx2_handler_in(ep_idx, ep_ctrl);
it82xx2_handler_in(ep_idx);
break;
case DC_OUTDATA_TRANS:
it82xx2_handler_out(ep_idx);
@ -1001,7 +1021,7 @@ int usb_dc_reset(void)
}
}
ep_regs[0].ep_ctrl.value = ENDPOINT_EN;
ep_regs[0].ep_ctrl.value = ENDPOINT_ENABLE_BIT;
usb_regs->dc_address = DC_ADDR_NULL;
udata0.addr = DC_ADDR_NULL;
usb_regs->dc_interrupt_status = DC_NAK_SENT_INT | DC_SOF_RECEIVED;
@ -1093,9 +1113,7 @@ int usb_dc_ep_configure(const struct usb_dc_ep_cfg_data *const cfg)
it82xx2_usb_set_ep_ctrl(ep_idx, EP_IN_DIRECTION_SET, in);
if (in) {
if (IT8XXX2_IS_EXTEND_ENDPOINT(ep_idx)) {
it82xx2_usb_extend_ep_ctrl(ep_idx, EP_DATA_SEQ_1, false);
}
it82xx2_usb_set_ep_ctrl(ep_idx, EP_DATA_SEQ_1, false);
udata0.ep_data[ep_idx].ep_status = EP_CONFIG_IN;
} else {
udata0.ep_data[ep_idx].ep_status = EP_CONFIG_OUT;
@ -1215,7 +1233,7 @@ int usb_dc_ep_set_stall(const uint8_t ep)
}
if (idx < 198) {
ep_regs[ep_idx].ep_ctrl.fields.send_stall_bit = 0;
it82xx2_usb_set_ep_ctrl(ep_idx, EP_STALL_SEND, false);
}
udata0.no_data_ctrl = false;
@ -1432,7 +1450,7 @@ int usb_dc_ep_read(uint8_t ep, uint8_t *buf, uint32_t max_data_len,
ff_regs[0].ep_tx_fifo_ctrl = FIFO_FORCE_EMPTY;
if (buf[6] != 0 || buf[7] != 0) {
/* set status IN after data OUT */
ep_regs[0].ep_ctrl.fields.outdata_sequence_bit = 1;
it82xx2_usb_set_ep_ctrl(ep_idx, EP_DATA_SEQ_1, true);
it82xx2_usb_set_ep_ctrl(ep_idx, EP_READY_ENABLE, true);
} else {
/* no_data_ctrl status */