Skip to content

Commit

Permalink
drivers: i3c: add controller handoff helper
Browse files Browse the repository at this point in the history
This adds controller handoff according to section 5.1.7.1 of the
I3C specification.

Signed-off-by: Ryan McClelland <[email protected]>
  • Loading branch information
XenuIsWatching committed Sep 12, 2024
1 parent 244a4d5 commit 51d9a71
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 4 deletions.
120 changes: 120 additions & 0 deletions drivers/i3c/i3c_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,126 @@ int i3c_dev_list_daa_addr_helper(struct i3c_addr_slots *addr_slots,
return ret;
}

uint8_t i3c_odd_parity(uint8_t p)
{
p ^= p >> 4;
p &= 0xf;
return (0x9669 >> p) & 1;
}

int i3c_device_controller_handoff(const struct i3c_device_desc *target, bool requested)
{
int ret;
union i3c_ccc_getstatus status = {0};
struct i3c_ccc_events i3c_events;
struct i3c_ccc_address handoff_address;

/* Section 5.1.7.1 of I3C v1.1.1 Specification */

/*
* If the Active Controller intends to pass the Controller Role to a selected Secondary
* Controller that did not send a Controller Role Request, then the Active Controller should
* verify that the selected Secondary Controller is active and ready to respond to
* additional commands
*/
if (!requested) {
ret = i3c_ccc_do_getstatus_fmt1(target, &status);
if (ret != 0) {
return ret;
}

if (I3C_CCC_GETSTATUS_ACTIVITY_MODE(status.fmt1.status) ==
I3C_CCC_GETSTATUS_ACTIVITY_MODE_NCH) {
return -EBUSY;
}
}

/*
* The Active Controller needs to disable Hot-Joins, Target Interrupt Requests, and other
* Bus events that could interfere with the Handoff, then it sends the appropriate
* Broadcast to disable those events before the Handoff. Once the Handoff is complete, the
* new Active Controller should re-enable events that are disabled in this step.
*/
i3c_events.events = I3C_CCC_EVT_ALL;
ret = i3c_ccc_do_events_all_set(target->bus, false, &i3c_events);
if (ret != 0) {
return ret;
}

/** TODO: reconfigure MLANE if needed */

/*
* If the Active Controller knows that the selected Secondary Controller must be put into a
* different Activity State before Handoff, then the Active Controller shall send the
* appropriate Broadcast or Direct CCCs to put the Bus (or selected Devices) into a
* different Activity State
*/
if (target->crhdly1 & I3C_CCC_GETMXDS_CRDHLY1_SET_BUS_ACT_STATE) {
ret = i3c_ccc_do_entas(
target, I3C_CCC_GETMXDS_CRDHLY1_CTRL_HANDOFF_ACT_STATE(target->crhdly1));
if (ret != 0) {
return ret;
}
}

if ((target->getcaps.getcap3 & I3C_CCC_GETCAPS3_GETSTATUS_DEFINING_BYTE_SUPPORT) &&
(target->crcaps.crcaps2 & I3C_CCC_GETCAPS_CRCAPS2_DEEP_SLEEP_CAPABLE)) {
ret = i3c_ccc_do_getstatus_fmt2(target, &status, GETSTATUS_FORMAT_2_PRECR);
if (ret != 0) {
return ret;
}

/*
* If the Active Controller determines that the indicated Secondary Controller has
* been in a “deep sleep” state and may need to be re-synchronized with the most
* current list of I3C Targets and Group Addresses, then the Active Controller
* should send CCC DEFTGTS and DEFGRPA
*/
if (status.fmt2.precr & I3C_CCC_GETSTATUS_PRECR_DEEP_SLEEP_DETECTED) {
ret = i3c_bus_deftgts(target->bus);
if (ret != 0) {
return ret;
}
/* TODO: broadcast DEFGRPA when group address support comes */

/* Check CRCAPS if the device needs additional time to process */
if (target->crcaps.crcaps2 &
I3C_CCC_GETCAPS_CRCAPS2_DELAYED_CONTROLLER_HANDOFF) {
/*
* Afterwards, the Active Controller should poll the Secondary
* Controller to ensure that it has successfully processed this data
* and indicates that it is ready to accept the Controller Role
*/
do {
ret = i3c_ccc_do_getstatus_fmt2(target, &status,
GETSTATUS_FORMAT_2_PRECR);
if (ret != 0) {
return ret;
}
} while (!(status.fmt2.precr &
I3C_CCC_GETSTATUS_PRECR_HANDOFF_DELAY_NACK));
}
}
}

/*
* After the Active Controller has prepared for Handoff, the Active Controller shall
* then issue a GETACCCR CCC
*/
ret = i3c_ccc_do_getacccr(target, &handoff_address);
if (ret != 0) {
return ret;
}

/* Verify Odd Parity and Correct Dynamic Address Reply */
if ((i3c_odd_parity(handoff_address.addr >> 1) != (handoff_address.addr & BIT(0))) ||
(handoff_address.addr >> 1 != target->dynamic_addr)) {
return -EIO;
}

return ret;
}

int i3c_device_basic_info_get(struct i3c_device_desc *target)
{
int ret;
Expand Down
7 changes: 5 additions & 2 deletions drivers/i3c/i3c_ibi_workq.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,11 @@ static void i3c_ibi_work_handler(struct k_work *work)
break;

case I3C_IBI_CONTROLLER_ROLE_REQUEST:
/* TODO: Add support for controller role request */
__fallthrough;
ret = i3c_device_controller_handoff(ibi_node->target, true);
if (ret != 0) {
LOG_ERR("i3c_device_controller_handoff returns %d", ret);
}
break;

default:
/* Unknown IBI type: do nothing */
Expand Down
27 changes: 27 additions & 0 deletions include/zephyr/drivers/i3c.h
Original file line number Diff line number Diff line change
Expand Up @@ -2146,6 +2146,33 @@ bool i3c_bus_has_sec_controller(const struct device *dev);
*/
int i3c_bus_deftgts(const struct device *dev);

/**
* @brief Calculate odd parity
*
* Calculate the Odd Parity of a Target Address.
*
* @param p The 7b target dynamic address
*
* @return The odd parity bit
*/
uint8_t i3c_odd_parity(uint8_t p);

/**
* @brief Perform Controller Handoff
*
* This performs the controller handoff according to 5.1.7.1 of
* I3C v1.1.1 Specification.
*
* @param target I3C target device descriptor.
* @param requested True if the target requested the Handoff, False if
* the active controller is passing it to a secondary controller
*
* @retval 0 if successful.
* @retval -EIO General Input/Output error.
* @retval -EBUSY Target cannot accept Controller Handoff
*/
int i3c_device_controller_handoff(const struct i3c_device_desc *target, bool requested);

/*
* This needs to be after declaration of struct i3c_driver_api,
* or else compiler complains about undefined type inside
Expand Down
7 changes: 5 additions & 2 deletions include/zephyr/drivers/i3c/ccc.h
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ struct i3c_ccc_address {
* @note For SETDATA, SETNEWDA and SETGRAP,
* the address is left-shift by 1, and bit[0] is always 0.
*
* @note Fpr SET GETACCCR, the address is left-shift by 1,
* @note For SET GETACCCR, the address is left-shift by 1,
* and bit[0] is the calculated odd parity bit.
*/
uint8_t addr;
Expand Down Expand Up @@ -623,6 +623,9 @@ union i3c_ccc_getstatus {
#define I3C_CCC_GETSTATUS_ACTIVITY_MODE(status) \
FIELD_GET(I3C_CCC_GETSTATUS_ACTIVITY_MODE_MASK, (status))

/** GETSTATUS Format 1 - Activity Mode Unable to participate in Controller Handoff */
#define I3C_CCC_GETSTATUS_ACTIVITY_MODE_NCH 0x3

/** GETSTATUS Format 1 - Number of Pending Interrupts bitmask. */
#define I3C_CCC_GETSTATUS_NUM_INT_MASK GENMASK(3U, 0U)

Expand Down Expand Up @@ -853,7 +856,7 @@ union i3c_ccc_getmxds {
* @param crhdly1 GETMXDS value.
*/
#define I3C_CCC_GETMXDS_CRDHLY1_CTRL_HANDOFF_ACT_STATE(crhdly1) \
FIELD_GET(I3C_CCC_GETMXDS_CRDHLY1_CTRL_HANDOFF_ACT_STATE_MASK, (chrdly1))
FIELD_GET(I3C_CCC_GETMXDS_CRDHLY1_CTRL_HANDOFF_ACT_STATE_MASK, (crhdly1))

/**
* @brief Indicate which format of GETCAPS to use.
Expand Down

0 comments on commit 51d9a71

Please sign in to comment.