You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by "Donny9 (via GitHub)" <gi...@apache.org> on 2024/09/17 10:58:29 UTC
[PR] drivers/i3c: support i3c driver frameworks [nuttx]
Donny9 opened a new pull request, #13507:
URL: https://github.com/apache/nuttx/pull/13507
## Summary
drivers/i3c: support i3c driver frameworks
The I3C (Improved Inter-Integrated Circuit) driver is a comprehensive software framework designed to support the advanced features of the I3C protocol. It comprises a system framework and underlying IP drivers, both of which are critical components for enabling seamless communication between I3C devices. The system framework, provided by vela os, offers user-level device nodes and kernel-level driver function interfaces. Meanwhile, the underlying IP drivers, developed specifically for the I3C driver framework, handle the implementation of IP functionalities and the adaptation of framework-level interfaces.
## Impact
support driver frameworks: i3c
## Testing
Vela
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] drivers/i3c: support i3c driver frameworks [nuttx]
Posted by "Donny9 (via GitHub)" <gi...@apache.org>.
Donny9 commented on code in PR #13507:
URL: https://github.com/apache/nuttx/pull/13507#discussion_r1766134788
##########
drivers/i3c/master.c:
##########
@@ -0,0 +1,2282 @@
+/****************************************************************************
+ * drivers/i3c/master.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <stdio.h>
+#include <debug.h>
+
+#include <nuttx/list.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/nuttx.h>
+#include <nuttx/mutex.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mm/mm.h>
+#include <nuttx/spinlock.h>
+#include <netinet/in.h>
+
+#include <nuttx/i3c/master.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/i3c/i3c_driver.h>
+#include <nuttx/i3c/device.h>
+#include "internals.h"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_lock
+ *
+ * Description:
+ * Lock the bus for a maintenance operation
+ *
+ * This function takes the bus lock so that no other operations can
+ * occur on the bus. This is needed for all kind of bus maintenance
+ * operation, like
+ * - enabling/disabling slave events
+ * - re-triggering DAA
+ * - changing the dynamic address of a device
+ * - relinquishing mastership
+ *
+ * The reason for this kind of locking is that we don't want drivers
+ * and core logic to rely on I3C device information that could be
+ * changed behind their back.
+ *
+ * Input Parameters:
+ * bus - I3C bus to take the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_unlock
+ *
+ * Description:
+ * Release the bus lock after a maintenance operation.
+ *
+ * Should be called when the bus maintenance operation is done.
+ * See i3c_bus_maintenance_lock() for more details on what these
+ * maintenance operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_to_i3c_master
+ ****************************************************************************/
+
+static FAR struct i3c_master_controller *
+i3c_bus_to_i3c_master(FAR struct i3c_bus *i3cbus)
+{
+ return container_of(i3cbus, struct i3c_master_controller, bus);
+}
+
+static FAR struct i3c_master_controller *
+i2c_adapter_to_i3c_master(FAR struct i2c_master_s *i2c_master)
+{
+ return container_of(i2c_master, struct i3c_master_controller, i2c);
+}
+
+static int i3c_master_to_i2c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i2c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_addr_slot_status
+ ****************************************************************************/
+
+static enum i3c_addr_slot_status
+i3c_bus_get_addr_slot_status(FAR struct i3c_bus *bus, uint16_t addr)
+{
+ int32_t status;
+ int bitpos = addr * 2;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return I3C_ADDR_SLOT_RSVD;
+ }
+
+ status = bus->addrslots[bitpos / I3C_BITS_PER_LONG];
+ status >>= bitpos % I3C_BITS_PER_LONG;
+
+ return status & I3C_ADDR_SLOT_STATUS_MASK;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_addr_slot_status
+ ****************************************************************************/
+
+static void i3c_bus_set_addr_slot_status(FAR struct i3c_bus *bus,
+ uint16_t addr, enum i3c_addr_slot_status status)
+{
+ int bitpos = addr * 2;
+ FAR uint32_t *ptr;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return;
+ }
+
+ ptr = bus->addrslots + (bitpos / I3C_BITS_PER_LONG);
+ *ptr &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK <<
+ (bitpos % I3C_BITS_PER_LONG));
+ *ptr |= (unsigned long)status << (bitpos % I3C_BITS_PER_LONG);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_dev_addr_is_avail
+ ****************************************************************************/
+
+static bool i3c_bus_dev_addr_is_avail(FAR struct i3c_bus *bus,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status status;
+
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ return status == I3C_ADDR_SLOT_FREE;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_free_addr
+ ****************************************************************************/
+
+static int i3c_bus_get_free_addr(FAR struct i3c_bus *bus,
+ unsigned char start_addr)
+{
+ enum i3c_addr_slot_status status;
+ unsigned char addr;
+
+ for (addr = start_addr; addr < I3C_MAX_ADDR; addr++)
+ {
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ if (status == I3C_ADDR_SLOT_FREE)
+ {
+ return addr;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init_addrslots
+ ****************************************************************************/
+
+static void i3c_bus_init_addrslots(FAR struct i3c_bus *bus)
+{
+ int i;
+
+ /* Addresses 0 to 7 are reserved. */
+
+ for (i = 0; i < 8; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, i, I3C_ADDR_SLOT_RSVD);
+ }
+
+ /* Reserve broadcast address and all addresses that might collide
+ * with the broadcast address when facing a single bit error.
+ */
+
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR,
+ I3C_ADDR_SLOT_RSVD);
+ for (i = 0; i < 7; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR ^ I3C_BIT(i),
+ I3C_ADDR_SLOT_RSVD);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init
+ ****************************************************************************/
+
+static void i3c_bus_init(FAR struct i3c_bus *i3cbus)
+{
+ FAR struct i3c_master_controller *master;
+ master = i3c_bus_to_i3c_master(i3cbus);
+
+ nxmutex_init(&i3cbus->lock);
+ list_initialize(&i3cbus->devs.i3c);
+ i3c_bus_init_addrslots(i3cbus);
+ i3cbus->mode = I3C_BUS_MODE_PURE;
+ i3cbus->id = master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_mode
+ ****************************************************************************/
+
+static int i3c_bus_set_mode(FAR struct i3c_bus *i3cbus,
+ enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate)
+{
+ i3cbus->mode = mode;
+
+ switch (i3cbus->mode)
+ {
+ case I3C_BUS_MODE_PURE:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_FAST:
+ case I3C_BUS_MODE_MIXED_LIMITED:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_SLOW:
+ {
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+
+ if (!i3cbus->scl_rate.i3c ||
+ i3cbus->scl_rate.i3c > i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i3c = i3cbus->scl_rate.i2c;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* I3C/I2C frequency may have been overridden, check that user-provided
+ * values are not exceeding max possible frequency.
+ */
+
+ if (i3cbus->scl_rate.i3c > I3C_BUS_MAX_I3C_SCL_RATE ||
+ i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_RATE)
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int i3c_master_to_i3c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_init
+ ****************************************************************************/
+
+static FAR void *i3c_ccc_cmd_dest_init(FAR struct i3c_ccc_cmd_dest *dest,
+ unsigned char addr, uint16_t payloadlen)
+{
+ dest->addr = addr;
+ dest->payload.len = payloadlen;
+ if (payloadlen)
+ {
+ dest->payload.data = kmm_zalloc(payloadlen);
+ }
+ else
+ {
+ dest->payload.data = NULL;
+ }
+
+ return dest->payload.data;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_cleanup
+ ****************************************************************************/
+
+static void i3c_ccc_cmd_dest_cleanup(FAR struct i3c_ccc_cmd_dest *dest)
+{
+ kmm_free(dest->payload.data);
+}
+
+static void i3c_ccc_cmd_init(FAR struct i3c_ccc_cmd *cmd, bool rnw,
+ unsigned char id, FAR struct i3c_ccc_cmd_dest *dests,
+ unsigned int ndests)
+{
+ cmd->rnw = rnw ? 1 : 0;
+ cmd->id = id;
+ cmd->dests = dests;
+ cmd->ndests = ndests;
+ cmd->err = I3C_ERROR_UNKNOWN;
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_alloc_i3c_dev(FAR struct i3c_master_controller *master,
+ FAR const struct i3c_device_info *info)
+{
+ FAR struct i3c_dev_desc *dev;
+
+ dev = kmm_zalloc(sizeof(*dev));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ dev->common.master = master;
+ dev->info = *info;
+ nxmutex_init(&dev->ibi_lock);
+
+ return dev;
+}
+
+static int i3c_master_rstdaa_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status addrstat;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ addrstat = i3c_bus_get_addr_slot_status(&master->bus, addr);
+ if (addr != I3C_BROADCAST_ADDR && addrstat != I3C_ADDR_SLOT_I3C_DEV)
+ {
+ return -EINVAL;
+ }
+
+ i3c_ccc_cmd_dest_init(&dest, addr, 0);
+ i3c_ccc_cmd_init(&cmd, false,
+ I3C_CCC_RSTDAA(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_enec_disec_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char addr, bool enable, unsigned char evts)
+{
+ FAR struct i3c_ccc_events *events;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ events = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*events));
+ if (events == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ events->events = evts;
+ i3c_ccc_cmd_init(&cmd, false,
+ enable ?
+ I3C_CCC_ENEC(addr == I3C_BROADCAST_ADDR) :
+ I3C_CCC_DISEC(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setda_locked(FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr, bool setdasa)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_setda *setda;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (!oldaddr || !newaddr)
+ {
+ return -EINVAL;
+ }
+
+ setda = i3c_ccc_cmd_dest_init(&dest, oldaddr, sizeof(*setda));
+ if (setda == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ setda->addr = newaddr << 1;
+ i3c_ccc_cmd_init(&cmd, false,
+ setdasa ? I3C_CCC_SETDASA : I3C_CCC_SETNEWDA,
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setnewda_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr)
+{
+ return i3c_master_setda_locked(master, oldaddr, newaddr, false);
+}
+
+static int i3c_master_getmrl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mrl *mrl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mrl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mrl));
+ if (mrl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ /* When the device does not have IBI payload GETMRL only returns 2
+ * bytes of data.
+ */
+
+ if (!(info->bcr & I3C_BCR_IBI_PAYLOAD))
+ {
+ dest.payload.len -= 1;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMRL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ switch (dest.payload.len)
+ {
+ case 3:
+ {
+ info->max_ibi_len = mrl->ibi_len;
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ case 2:
+ {
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ default:
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmwl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mwl *mwl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mwl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mwl));
+ if (mwl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMWL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != sizeof(*mwl))
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_write_len = ntohs(mwl->len);
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmxds_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getmxds *getmaxds;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getmaxds = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*getmaxds));
+ if (getmaxds == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 2 && dest.payload.len != 5)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_read_ds = getmaxds->maxrd;
+ info->max_write_ds = getmaxds->maxwr;
+ if (dest.payload.len == 5)
+ {
+ info->max_read_turnaround = getmaxds->maxrdturn[0] |
+ ((uint32_t)getmaxds->maxrdturn[1] << 8) |
+ ((uint32_t)getmaxds->maxrdturn[2] << 16);
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_gethdrcap_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_gethdrcap *gethdrcap;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ gethdrcap = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*gethdrcap));
+ if (gethdrcap == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETHDRCAP, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 1)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->hdr_cap = gethdrcap->modes;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getpid_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getpid *getpid;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+ int i;
+
+ getpid = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getpid));
+ if (getpid == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETPID, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->pid = 0;
+ for (i = 0; i < sizeof(getpid->pid); i++)
+ {
+ int sft = (sizeof(getpid->pid) - i - 1) * 8;
+ info->pid |= (uint64_t)getpid->pid[i] << sft;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getbcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getbcr *getbcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getbcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getbcr));
+ if (getbcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETBCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->bcr = getbcr->bcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getdcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getdcr *getdcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getdcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getdcr));
+ if (getdcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETDCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->dcr = getdcr->dcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_retrieve_dev_info(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status slot_status;
+ int ret;
+
+ if (!dev->info.dyn_addr)
+ {
+ return -EINVAL;
+ }
+
+ slot_status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (slot_status == I3C_ADDR_SLOT_RSVD)
+ {
+ return -EINVAL;
+ }
+
+ ret = i3c_master_getpid_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getbcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getdcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ if (dev->info.bcr & I3C_BCR_MAX_DATA_SPEED_LIM)
+ {
+ ret = i3c_master_getmxds_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
+ {
+ dev->info.max_ibi_len = 1;
+ }
+
+ i3c_master_getmrl_locked(master, &dev->info);
+ i3c_master_getmwl_locked(master, &dev->info);
+
+ if (dev->info.bcr & I3C_BCR_HDR_CAP)
+ {
+ ret = i3c_master_gethdrcap_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_put_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ if (dev->info.dyn_addr)
+ {
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_FREE);
+ }
+}
+
+static int i3c_master_get_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ /* ->init_dyn_addr should have been reserved before that, so, if we're
+ * trying to apply a pre-reserved dynamic address, we should not try
+ * to reserve the address slot a second time.
+ */
+
+ if (dev->info.dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ return 0;
+}
+
+static int i3c_master_attach_i3c_dev(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_dev_desc *dev)
+{
+ int ret;
+
+ /* We don't attach devices to the controller until they are
+ * addressable on the bus.
+ */
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ ret = i3c_master_get_i3c_addrs(dev);
+ if (ret)
+ {
+ return ret;
+ }
+
+ /* Do not attach the master device itself. */
+
+ if (master->this != dev && master->ops->attach_i3c_dev)
+ {
+ ret = master->ops->attach_i3c_dev(dev);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ list_add_tail(&master->bus.devs.i3c, &dev->common.node);
+
+ return 0;
+}
+
+static int i3c_master_reattach_i3c_dev(FAR struct i3c_dev_desc *dev,
+ unsigned char old_dyn_addr)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+ int ret;
+
+ if (dev->info.dyn_addr != old_dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus,
+ dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ if (master->ops->reattach_i3c_dev)
+ {
+ ret = master->ops->reattach_i3c_dev(dev, old_dyn_addr);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_detach_i3c_dev(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ /* Do not detach the master device itself. */
+
+ if (master->this != dev && master->ops->detach_i3c_dev)
+ {
+ master->ops->detach_i3c_dev(dev);
+ }
+
+ i3c_master_put_i3c_addrs(dev);
+ list_delete(&dev->common.node);
+}
+
+static void
+i3c_master_register_new_i3c_devs(FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *desc;
+
+ if (!master->init_done)
+ {
+ return;
+ }
+
+ i3c_bus_for_each_i3cdev(&master->bus, desc)
+ {
+ if (desc->dev || !desc->info.dyn_addr || desc == master->this)
+ {
+ continue;
+ }
+
+ desc->dev = kmm_zalloc(sizeof(*desc->dev));
+ if (desc->dev == NULL)
+ {
+ continue;
+ }
+
+ desc->dev->bus = &master->bus;
+ desc->dev->desc = desc;
+ }
+}
+
+static void i3c_master_detach_free_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+ FAR struct i3c_dev_desc *i3ctmp;
+
+ list_for_every_entry_safe(&master->bus.devs.i3c, i3cdev, i3ctmp,
+ struct i3c_dev_desc, common.node)
+ {
+ i3c_master_detach_i3c_dev(i3cdev);
+ kmm_free(i3cdev);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_master_bus_init()
+ *
+ * Description:
+ * Initialize an I3C bus
+ *
+ * This function is following all initialisation steps described in the I3C
+ * specification:
+ *
+ * 1. Attach I2C devs to the master so that the master can fill its
+ * internal device table appropriately
+ *
+ * 2. Call &i3c_master_controller_ops->bus_init() method to initialize
+ * the master controller. That's usually where the bus mode is selected
+ * (pure bus or mixed fast/slow bus)
+ *
+ * 3. Instruct all devices on the bus to drop their dynamic address. This
+ * is particularly important when the bus was previously configured by
+ * someone else (for example the bootloader)
+ *
+ * 4. Disable all slave events.
+ *
+ * 5. Reserve address slots for I3C devices with init_dyn_addr. And if
+ * devices also have static_addr, try to pre-assign dynamic addresses
+ * requested by the FW with SETDASA and attach corresponding statically
+ * defined I3C devices to the master.
+ *
+ * 6. Do a DAA (Dynamic Address Assignment) to assign dynamic addresses to
+ * all remaining I3C devices
+ *
+ * Once this is done, all I3C and I2C devices should be usable.
+ *
+ * Input Parameters:
+ * master - main master initializing the bus
+ *
+ * Returned Value:
+ * A 0 in case of success, an negative error code otherwise.
+ ****************************************************************************/
+
+static int i3c_master_bus_init(FAR struct i3c_master_controller *master)
+{
+ int ret;
+
+ /* Now execute the controller specific ->bus_init() routine, which
+ * might configure its internal logic to match the bus limitations.
+ */
+
+ ret = master->ops->bus_init(master);
+ if (ret)
+ {
+ goto err_detach_devs;
+ }
+
+ /* The master device should have been instantiated in ->bus_init(),
+ * complain if this was not the case.
+ */
+
+ if (master->this == NULL)
+ {
+ i3cerr("master_set_info() was not called in ->bus_init()\n");
+ ret = -EINVAL;
+ goto err_bus_cleanup;
+ }
+
+ /* Reset all dynamic address that may have been assigned before
+ * (assigned by the bootloader for example).
+ */
+
+ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_rstdaa_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Disable all slave events before starting DAA. */
+
+ ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
+ I3C_CCC_EVENT_HJ);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_disec_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Reserve init_dyn_addr first, and then try to pre-assign dynamic
+ * address and retrieve device information if needed.
+ * In case pre-assign dynamic address fails, setting dynamic address to
+ * the requested init_dyn_addr is retried after DAA is done in
+ * i3c_master_add_i3c_dev_locked().
+ */
+
+ ret = i3c_master_do_daa(master);
+ if (ret)
+ {
+ i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ i3cerr("master_do_daa failed:%d (no device)\n", ret);
+ }
+
+ return 0;
+
+err_bus_cleanup:
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+err_detach_devs:
+ i3c_master_detach_free_devs(master);
+
+ return ret;
+}
+
+static void i3c_master_bus_cleanup(FAR struct i3c_master_controller *master)
+{
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+ i3c_master_detach_free_devs(master);
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_search_i3c_dev_duplicate(FAR struct i3c_dev_desc *refdev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(refdev);
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev != refdev && i3cdev->info.pid == refdev->info.pid)
+ {
+ return i3cdev;
+ }
+ }
+
+ return NULL;
+}
+
+static int i3c_master_i2c_adapter_xfer(FAR struct i2c_master_s *i2c,
+ FAR struct i2c_msg_s *xfers, int nxfers)
+{
+ FAR struct i3c_master_controller *master = i2c_adapter_to_i3c_master(i2c);
+ int i;
+ int ret;
+ uint16_t addr;
+
+ if (xfers == NULL || master == NULL || nxfers <= 0)
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->i2c_xfers == NULL)
+ {
+ return -ENOTSUP;
+ }
+
+ /* Doing transfers to different devices is not supported. */
+
+ addr = xfers[0].addr;
+ for (i = 1; i < nxfers; i++)
+ {
+ if (addr != xfers[i].addr)
+ {
+ return -ENOTSUP;
+ }
+ }
+
+ i3c_bus_normaluse_lock(&master->bus);
+ ret = master->ops->i2c_xfers(master, xfers, nxfers);
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ return ret ? ret : nxfers;
+}
+
+static struct i2c_ops_s i3c_master_i2c_algo =
+{
+ .transfer = i3c_master_i2c_adapter_xfer,
+};
+
+static void i3c_master_unregister_i3c_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev->dev == NULL)
+ {
+ continue;
+ }
+
+ i3cdev->dev->desc = NULL;
+ i3cdev->dev->bus = NULL;
+ kmm_free(i3cdev->dev);
+ i3cdev->dev = NULL;
+ }
+}
+
+static void i3c_master_handle_ibi(FAR void *arg)
+{
+ FAR struct i3c_ibi_slot *slot = (struct i3c_ibi_slot *)arg;
+ FAR struct i3c_dev_desc *dev = slot->dev;
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ struct i3c_ibi_payload payload;
+
+ payload.data = slot->data;
+ payload.len = slot->len;
+
+ if (dev->dev)
+ {
+ dev->ibi->handler(dev->dev, &payload);
+ }
+
+ master->ops->recycle_ibi_slot(dev, slot);
+ atomic_fetch_sub(&dev->ibi->pending_ibis, 1);
+ if (!atomic_load(&dev->ibi->pending_ibis))
+ {
+ sem_post(&dev->ibi->all_ibis_handled);
+ }
+}
+
+static void i3c_master_init_ibi_slot(FAR struct i3c_dev_desc *dev,
+ FAR struct i3c_ibi_slot *slot)
+{
+ slot->dev = dev;
+}
+
+static int i3c_master_check_ops(
+ FAR const struct i3c_master_controller_ops *ops)
+{
+ if (ops == NULL || ops->bus_init == NULL || ops->priv_xfers == NULL ||
+ ops->send_ccc_cmd == NULL || ops->do_daa == NULL ||
+ ops->i2c_xfers == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (ops->request_ibi &&
+ (ops->enable_ibi == NULL || ops->disable_ibi == NULL ||
+ ops->free_ibi == NULL || ops->recycle_ibi_slot == NULL))
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i2c_unregister_driver()
+ *
+ * Description:
+ * Perform an I2C driver unregister operation.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ *
+ ****************************************************************************/
+
+static void i2c_unregister_driver(FAR struct i3c_master_controller *master)
+{
+ char devname[12];
+ int i2c_bus_id = i3c_master_to_i2c_bus_number(master);
+
+ snprintf(devname, 12, "/dev/i2c%d", i2c_bus_id);
+ nx_unlink(devname);
+}
+
+/****************************************************************************
+ * Name: i3c_unregister_driver()
+ *
+ * Description:
+ * Perform an I3C driver unregister operation.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ *
+ ****************************************************************************/
+
+static void i3c_unregister_driver(FAR struct i3c_master_controller *master)
+{
+ char devname[12];
+
+ snprintf(devname, 12, "/dev/i3c%d", i3c_master_to_i3c_bus_number(master));
+ nx_unlink(devname);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_master_send_ccc_cmd_locked
+ ****************************************************************************/
+
+int i3c_master_send_ccc_cmd_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_ccc_cmd *cmd)
+{
+ int ret;
+
+ if (cmd == NULL || master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (master->init_done &&
+ !nxmutex_is_locked(&master->bus.lock))
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->send_ccc_cmd == NULL)
+ {
+ return -ENOTSUP;
+ }
+
+ if ((cmd->id & I3C_CCC_DIRECT) && (cmd->dests == NULL || !cmd->ndests))
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->supports_ccc_cmd &&
+ !master->ops->supports_ccc_cmd(master, cmd))
+ {
+ return -ENOTSUP;
+ }
+
+ ret = master->ops->send_ccc_cmd(master, cmd);
+ if (ret)
+ {
+ if (cmd->err != I3C_ERROR_UNKNOWN)
+ {
+ return cmd->err;
+ }
+
+ return ret;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_normaluse_lock
+ *
+ * Description:
+ * I3C bus to take the lock on.
+ *
+ * This function takes the bus lock for any operation that is not a
+ * maintenance operation (see i3c_bus_maintenance_lock() for
+ * non-exhaustive list of maintenance operations). Basically all
+ * communications with I3C devices are normal operations (HDR, SDR
+ * transfers or CCC commands that do not change bus state or I3C dynamic
+ * address).
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+void i3c_bus_normaluse_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_normaluse_unlock
+ *
+ * Description:
+ * Release the bus lock after a normal operation.
+ *
+ * Should be called when a normal operation is done. See
+ * i3c_bus_normaluse_lock() for more details on what these
+ * normal operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ *
+ ****************************************************************************/
+
+void i3c_bus_normaluse_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_master_get_free_addr()
+ *
+ * Description:
+ * Get a free address on the bus.
+ *
+ * This function must be called with the bus lock held in write mode.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ * start_addr - Where to start searching.
+ *
+ * Returned Value:
+ * The first free address starting at @start_addr (included) or -ENOMEM
+ * if there's no more address available.
+ *
+ ****************************************************************************/
+
+int i3c_master_get_free_addr(FAR struct i3c_master_controller *master,
+ unsigned char start_addr)
+{
+ return i3c_bus_get_free_addr(&master->bus, start_addr);
+}
+
+/****************************************************************************
+ * Name: i3c_master_do_daa()
+ *
+ * Description:
+ * Do a DAA (Dynamic Address Assignment)
+ *
+ * This function is instantiating an I3C device object and adding it to
+ * the I3C device list. All device information are automatically retrieved
+ * using standard CCC commands.
+ *
+ * The I3C device object is returned in case the master wants to attach
+ * private data to it using i3c_dev_set_master_data().
+ *
+ * This function must be called with the bus lock held in write mode.
+ *
+ * Input Parameters:
+ * master - Master doing the DAA
+ *
+ * Returned Value:
+ * A 0 in case of success, an negative error code otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_master_do_daa(FAR struct i3c_master_controller *master)
+{
+ int ret;
+
+ i3c_bus_maintenance_lock(&master->bus);
+ ret = master->ops->do_daa(master);
+ i3c_bus_maintenance_unlock(&master->bus);
+
+ if (ret)
+ {
+ return ret;
+ }
+
+ i3c_bus_normaluse_lock(&master->bus);
+ i3c_master_register_new_i3c_devs(master);
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i3c_master_enec_locked()
+ *
+ * Description:
+ * Send an ENEC CCC command
+ *
+ * Sends an ENEC CCC command to enable some or all events coming from a
+ * specific slave, or all devices if @addr is %I3C_BROADCAST_ADDR.
+ *
+ * This function must be called with the bus lock held in write mode.
+ *
+ * Input Parameters:
+ * master - Master used to send frames on the bus.
+ * addr - A valid I3C slave address or %I3C_BROADCAST_ADDR
+ * evts - Events to enable
+ *
+ * Returned Value:
+ * 0 in case of success, a positive I3C error code if the error is
+ * one of the official Mx error codes, and a negative error code otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_master_enec_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr, unsigned char evts)
+{
+ return i3c_master_enec_disec_locked(master, addr, true, evts);
+}
+
+/****************************************************************************
+ * Name: i3c_master_entdaa_locked()
+ *
+ * Description:
+ * Start a DAA (Dynamic Address Assignment) procedure
+ *
+ * This function must be called with the bus lock held in write mode.
+ *
+ * Note that this function only sends the ENTDAA CCC command, all the logic
+ * behind dynamic address assignment has to be handled in the I3C master
+ * driver.
+ *
+ * Input Parameters:
+ * master - Master used to send frames on the bus.
+ *
+ * Returned Value:
+ * 0 in case of success, a positive I3C error code if the error is one of
+ * the official Mx error codes, and a negative error code otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_master_entdaa_locked(FAR struct i3c_master_controller *master)
+{
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ i3c_ccc_cmd_dest_init(&dest, I3C_BROADCAST_ADDR, 0);
+ i3c_ccc_cmd_init(&cmd, false, I3C_CCC_ENTDAA, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_master_disec_locked()
+ *
+ * Description:
+ * Send a DISEC CCC command
+ *
+ * Send a DISEC CCC command to disable some or all events coming from a
+ * specific slave, or all devices if @addr is %I3C_BROADCAST_ADDR.
+ *
+ * This function must be called with the bus lock held in write mode.
+ *
+ * Input Parameters:
+ * master - Master used to send frames on the bus.
+ * addr - A valid I3C slave address or %I3C_BROADCAST_ADDR
+ * evts - Events to disable
+ *
+ * Returned Value:
+ * 0 in case of success, a positive I3C error code if the error is one of
+ * the official Mx error codes, and a negative error code otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_master_disec_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr, unsigned char evts)
+{
+ return i3c_master_enec_disec_locked(master, addr, false, evts);
+}
+
+/****************************************************************************
+ * Name: i3c_master_set_info()
+ *
+ * Description:
+ * Set master device information
+ *
+ * Set master device info. This should be called from
+ * &i3c_master_controller_ops->bus_init().
+ *
+ * Not all &i3c_device_info fields are meaningful for a master device.
+ * Here is a list of fields that should be properly filled:
+ *
+ * - &i3c_device_info->dyn_addr
+ * - &i3c_device_info->bcr
+ * - &i3c_device_info->dcr
+ * - &i3c_device_info->pid
+ * - &i3c_device_info->hdr_cap if %I3C_BCR_HDR_CAP bit is set in
+ * &i3c_device_info->bcr
+ *
+ * This function must be called with the bus lock held in maintenance mode.
+ *
+ * Input Parameters:
+ * master - Master used to send frames on the bus
+ * info - I3C device information
+ *
+ * Returned Value:
+ * 0 if @info contains valid information (not every piece of
+ * information can be checked, but we can at least make sure info->dyn_addr
+ * and @info->bcr are correct), -EINVAL otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_master_set_info(FAR struct i3c_master_controller *master,
+ FAR const struct i3c_device_info *info)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+ int ret;
+
+ if (!i3c_bus_dev_addr_is_avail(&master->bus, info->dyn_addr))
+ {
+ return -EINVAL;
+ }
+
+ if (I3C_BCR_DEVICE_ROLE(info->bcr) == I3C_BCR_I3C_MASTER &&
+ master->secondary)
+ {
+ return -EINVAL;
+ }
+
+ if (master->this)
+ {
+ return -EINVAL;
+ }
+
+ i3cdev = i3c_master_alloc_i3c_dev(master, info);
+ if (i3cdev == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ master->this = i3cdev;
+ master->bus.cur_master = master->this;
+
+ ret = i3c_master_attach_i3c_dev(master, i3cdev);
+ if (ret)
+ {
+ goto err_free_dev;
+ }
+
+ return 0;
+
+err_free_dev:
+ kmm_free(i3cdev);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_master_add_i3c_dev_locked()
+ *
+ * Description:
+ * Add an I3C slave to the bus
+ *
+ * This function is instantiating an I3C device object and adding it to
+ * the I3C device list. All device information are automatically retrieved
+ * using standard CCC commands.
+ *
+ * The I3C device object is returned in case the master wants to attach
+ * private data to it using i3c_dev_set_master_data().
+ *
+ * This function must be called with the bus lock held in write mode.
+ *
+ * Input Parameters:
+ * master - Master used to send frames on the bus
+ * addr - I3C slave dynamic address assigned to the device
+ *
+ * Returned Value:
+ * A 0 in case of success, an negative error code otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_master_add_i3c_dev_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr)
+{
+ struct i3c_device_info info =
+ {
+ .dyn_addr = addr
+ };
+
+ FAR struct i3c_dev_desc *newdev;
+ FAR struct i3c_dev_desc *olddev;
+ unsigned char old_dyn_addr = addr;
+ unsigned char expected_dyn_addr;
+ struct i3c_ibi_setup ibireq;
+ bool enable_ibi = false;
+ int ret;
+
+ memset(&ibireq, 0, sizeof(struct i3c_ibi_setup));
+ if (master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ newdev = i3c_master_alloc_i3c_dev(master, &info);
+ if (newdev == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ ret = i3c_master_attach_i3c_dev(master, newdev);
+ if (ret)
+ {
+ goto err_free_dev;
+ }
+
+ ret = i3c_master_retrieve_dev_info(newdev);
+ if (ret)
+ {
+ goto err_detach_dev;
+ }
+
+ olddev = i3c_master_search_i3c_dev_duplicate(newdev);
+ if (olddev)
+ {
+ newdev->dev = olddev->dev;
+ if (newdev->dev)
+ {
+ newdev->dev->desc = newdev;
+ }
+
+ /* We need to restore the IBI state too, so let's save the
+ * IBI information and try to restore them after olddev has
+ * been detached+released and its IBI has been stopped and
+ * the associated resources have been freed.
+ */
+
+ nxmutex_lock(&olddev->ibi_lock);
+ if (olddev->ibi)
+ {
+ ibireq.handler = olddev->ibi->handler;
+ ibireq.max_payload_len = olddev->ibi->max_payload_len;
+ ibireq.num_slots = olddev->ibi->num_slots;
+
+ if (olddev->ibi->enabled)
+ {
+ enable_ibi = true;
+ i3c_dev_disable_ibi_locked(olddev);
+ }
+
+ i3c_dev_free_ibi_locked(olddev);
+ }
+
+ nxmutex_unlock(&olddev->ibi_lock);
+
+ old_dyn_addr = olddev->info.dyn_addr;
+
+ i3c_master_detach_i3c_dev(olddev);
+ kmm_free(olddev);
+ }
+
+ ret = i3c_master_reattach_i3c_dev(newdev, old_dyn_addr);
+ if (ret)
+ {
+ goto err_detach_dev;
+ }
+
+ /* Depending on our previous state, the expected dynamic address might
+ * differ:
+ * - if the device already had a dynamic address assigned, let's try to
+ * re-apply this one
+ * - if the device did not have a dynamic address and the firmware
+ * requested a specific address, pick this one
+ * - in any other case, keep the address automatically assigned by the
+ * master
+ */
+
+ if (old_dyn_addr && old_dyn_addr != newdev->info.dyn_addr)
+ {
+ expected_dyn_addr = old_dyn_addr;
+ }
+ else
+ {
+ expected_dyn_addr = newdev->info.dyn_addr;
+ }
+
+ if (newdev->info.dyn_addr != expected_dyn_addr)
+ {
+ /* Try to apply the expected dynamic address. If it fails, keep
+ * the address assigned by the master.
+ */
+
+ ret = i3c_master_setnewda_locked(master,
+ newdev->info.dyn_addr,
+ expected_dyn_addr);
+ if (!ret)
+ {
+ old_dyn_addr = newdev->info.dyn_addr;
+ newdev->info.dyn_addr = expected_dyn_addr;
+ i3c_master_reattach_i3c_dev(newdev, old_dyn_addr);
+ }
+ else
+ {
+ i3cerr("Failed to assign reserved/old address%d%"PRIx64"\n",
+ master->bus.id, newdev->info.pid);
+ }
+ }
+
+ /* Now is time to try to restore the IBI setup. If we're lucky,
+ * everything works as before, otherwise, all we can do is complain.
+ * FIXME: maybe we should add callback to inform the driver that it
+ * should request the IBI again instead of trying to hide that from
+ * him.
+ */
+
+ if (ibireq.handler)
+ {
+ nxmutex_lock(&newdev->ibi_lock);
+ ret = i3c_dev_request_ibi_locked(newdev, &ibireq);
+ if (ret)
+ {
+ i3cerr("Failed to request IBI on device %d-%"PRIx64"\n",
+ master->bus.id, newdev->info.pid);
+ }
+ else if (enable_ibi)
+ {
+ ret = i3c_dev_enable_ibi_locked(newdev);
+ if (ret)
+ {
+ i3cerr("Failed to re-enable IBI on device %d-%"PRIx64"\n",
+ master->bus.id, newdev->info.pid);
+ }
+ }
+
+ nxmutex_unlock(&newdev->ibi_lock);
+ }
+
+ return 0;
+
+err_detach_dev:
+ if (newdev->dev && newdev->dev->desc)
+ {
+ newdev->dev->desc = NULL;
+ }
+
+ i3c_master_detach_i3c_dev(newdev);
+
+err_free_dev:
+ kmm_free(newdev);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_master_queue_ibi()
+ *
+ * Description:
+ * Queue an IBI
+ *
+ * Queue an IBI to the controller workqueue. The IBI handler attached
+ * to the dev will be called from a workqueue context.
+ *
+ * Input Parameters:
+ * dev - The device this IBI is coming from
+ * slot - The IBI slot used to store the payload
+ *
+ ****************************************************************************/
+
+void i3c_master_queue_ibi(FAR struct i3c_dev_desc *dev,
+ FAR struct i3c_ibi_slot *slot)
+{
+ atomic_fetch_add(&dev->ibi->pending_ibis, 1);
+ work_queue(HPWORK, &slot->work, i3c_master_handle_ibi, slot, 0);
+}
+
+/****************************************************************************
+ * Name: i3c_generic_ibi_free_pool()
+ *
+ * Description:
+ * Free a generic IBI pool
+ *
+ * Free all IBI slots allated by a generic IBI pool.
+ *
+ * Input Parameters:
+ * pool - The IBI pool to free
+ *
+ ****************************************************************************/
+
+void i3c_generic_ibi_free_pool(FAR struct i3c_generic_ibi_pool *pool)
+{
+ FAR struct i3c_generic_ibi_slot *slot;
+ unsigned int nslots = 0;
+
+ while (!list_is_empty(&pool->free_slots))
+ {
+ slot = list_first_entry(&pool->free_slots,
+ struct i3c_generic_ibi_slot, node);
+ list_delete(&slot->node);
+ nslots++;
+ }
+
+ if (nslots != pool->num_slots)
+ {
+ i3cwarn("freed slots num is not equal to allocated slots num\n");
+ }
+
+ /* If the number of freed slots is not equal to the number of allocated
+ * slots we have a leak somewhere.
+ */
+
+ kmm_free(pool->payload_buf);
+ kmm_free(pool->slots);
+ kmm_free(pool);
+}
+
+/****************************************************************************
+ * Name: i3c_generic_ibi_alloc_pool()
+ *
+ * Description:
+ * Create a generic IBI pool
+ *
+ * Create a generic IBI pool based on the information provided in @req.
+ *
+ * Input Parameters:
+ * dev - The device this pool will be used for
+ * req - IBI setup request describing what the device driver expects
+ *
+ * Returned Value:
+ * A valid IBI pool in case of success, an ERR_PTR() otherwise.
+ ****************************************************************************/
+
+FAR struct i3c_generic_ibi_pool *
+i3c_generic_ibi_alloc_pool(FAR struct i3c_dev_desc *dev,
+ FAR const struct i3c_ibi_setup *req)
+{
+ FAR struct i3c_generic_ibi_pool *pool;
+ FAR struct i3c_generic_ibi_slot *slot;
+ unsigned int i;
+
+ struct i3c_generic_ibi_pool *ret;
+
+ pool = kmm_zalloc(sizeof(*pool));
+ if (pool == NULL)
+ {
+ return NULL;
+ }
+
+ list_initialize(&pool->free_slots);
+ list_initialize(&pool->pending);
+
+ pool->slots = kmm_calloc(req->num_slots, sizeof(*slot));
+ if (pool->slots == NULL)
+ {
+ ret = NULL;
+ goto err_free_pool;
+ }
+
+ if (req->max_payload_len)
+ {
+ pool->payload_buf = kmm_calloc(req->num_slots,
+ req->max_payload_len);
+ if (pool->payload_buf == NULL)
+ {
+ ret = NULL;
+ goto err_free_pool;
+ }
+ }
+
+ for (i = 0; i < req->num_slots; i++)
+ {
+ slot = &pool->slots[i];
+ i3c_master_init_ibi_slot(dev, &slot->base);
+
+ if (req->max_payload_len)
+ {
+ slot->base.data = pool->payload_buf +
+ (i * req->max_payload_len);
+ }
+
+ list_add_tail(&pool->free_slots, &slot->node);
+ pool->num_slots++;
+ }
+
+ return pool;
+
+err_free_pool:
+ i3c_generic_ibi_free_pool(pool);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_generic_ibi_get_free_slot()
Review Comment:
Done~
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] drivers/i3c: support i3c driver frameworks [nuttx]
Posted by "xiaoxiang781216 (via GitHub)" <gi...@apache.org>.
xiaoxiang781216 merged PR #13507:
URL: https://github.com/apache/nuttx/pull/13507
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] drivers/i3c: support i3c driver frameworks [nuttx]
Posted by "Donny9 (via GitHub)" <gi...@apache.org>.
Donny9 commented on code in PR #13507:
URL: https://github.com/apache/nuttx/pull/13507#discussion_r1770710878
##########
drivers/i3c/master.c:
##########
@@ -0,0 +1,2282 @@
+/****************************************************************************
+ * drivers/i3c/master.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <stdio.h>
+#include <debug.h>
+
+#include <nuttx/list.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/nuttx.h>
+#include <nuttx/mutex.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mm/mm.h>
+#include <nuttx/spinlock.h>
+#include <netinet/in.h>
+
+#include <nuttx/i3c/master.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/i3c/i3c_driver.h>
+#include <nuttx/i3c/device.h>
+#include "internals.h"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_lock
+ *
+ * Description:
+ * Lock the bus for a maintenance operation
+ *
+ * This function takes the bus lock so that no other operations can
+ * occur on the bus. This is needed for all kind of bus maintenance
+ * operation, like
+ * - enabling/disabling slave events
+ * - re-triggering DAA
+ * - changing the dynamic address of a device
+ * - relinquishing mastership
+ *
+ * The reason for this kind of locking is that we don't want drivers
+ * and core logic to rely on I3C device information that could be
+ * changed behind their back.
+ *
+ * Input Parameters:
+ * bus - I3C bus to take the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_unlock
+ *
+ * Description:
+ * Release the bus lock after a maintenance operation.
+ *
+ * Should be called when the bus maintenance operation is done.
+ * See i3c_bus_maintenance_lock() for more details on what these
+ * maintenance operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_to_i3c_master
+ ****************************************************************************/
+
+static FAR struct i3c_master_controller *
+i3c_bus_to_i3c_master(FAR struct i3c_bus *i3cbus)
+{
+ return container_of(i3cbus, struct i3c_master_controller, bus);
+}
+
+static FAR struct i3c_master_controller *
+i2c_adapter_to_i3c_master(FAR struct i2c_master_s *i2c_master)
+{
+ return container_of(i2c_master, struct i3c_master_controller, i2c);
+}
+
+static int i3c_master_to_i2c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i2c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_addr_slot_status
+ ****************************************************************************/
+
+static enum i3c_addr_slot_status
+i3c_bus_get_addr_slot_status(FAR struct i3c_bus *bus, uint16_t addr)
+{
+ int32_t status;
+ int bitpos = addr * 2;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return I3C_ADDR_SLOT_RSVD;
+ }
+
+ status = bus->addrslots[bitpos / I3C_BITS_PER_LONG];
+ status >>= bitpos % I3C_BITS_PER_LONG;
+
+ return status & I3C_ADDR_SLOT_STATUS_MASK;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_addr_slot_status
+ ****************************************************************************/
+
+static void i3c_bus_set_addr_slot_status(FAR struct i3c_bus *bus,
+ uint16_t addr, enum i3c_addr_slot_status status)
+{
+ int bitpos = addr * 2;
+ FAR uint32_t *ptr;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return;
+ }
+
+ ptr = bus->addrslots + (bitpos / I3C_BITS_PER_LONG);
+ *ptr &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK <<
+ (bitpos % I3C_BITS_PER_LONG));
+ *ptr |= (unsigned long)status << (bitpos % I3C_BITS_PER_LONG);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_dev_addr_is_avail
+ ****************************************************************************/
+
+static bool i3c_bus_dev_addr_is_avail(FAR struct i3c_bus *bus,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status status;
+
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ return status == I3C_ADDR_SLOT_FREE;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_free_addr
+ ****************************************************************************/
+
+static int i3c_bus_get_free_addr(FAR struct i3c_bus *bus,
+ unsigned char start_addr)
+{
+ enum i3c_addr_slot_status status;
+ unsigned char addr;
+
+ for (addr = start_addr; addr < I3C_MAX_ADDR; addr++)
+ {
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ if (status == I3C_ADDR_SLOT_FREE)
+ {
+ return addr;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init_addrslots
+ ****************************************************************************/
+
+static void i3c_bus_init_addrslots(FAR struct i3c_bus *bus)
+{
+ int i;
+
+ /* Addresses 0 to 7 are reserved. */
+
+ for (i = 0; i < 8; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, i, I3C_ADDR_SLOT_RSVD);
+ }
+
+ /* Reserve broadcast address and all addresses that might collide
+ * with the broadcast address when facing a single bit error.
+ */
+
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR,
+ I3C_ADDR_SLOT_RSVD);
+ for (i = 0; i < 7; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR ^ I3C_BIT(i),
+ I3C_ADDR_SLOT_RSVD);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init
+ ****************************************************************************/
+
+static void i3c_bus_init(FAR struct i3c_bus *i3cbus)
+{
+ FAR struct i3c_master_controller *master;
+ master = i3c_bus_to_i3c_master(i3cbus);
+
+ nxmutex_init(&i3cbus->lock);
+ list_initialize(&i3cbus->devs.i3c);
+ i3c_bus_init_addrslots(i3cbus);
+ i3cbus->mode = I3C_BUS_MODE_PURE;
+ i3cbus->id = master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_mode
+ ****************************************************************************/
+
+static int i3c_bus_set_mode(FAR struct i3c_bus *i3cbus,
+ enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate)
+{
+ i3cbus->mode = mode;
+
+ switch (i3cbus->mode)
+ {
+ case I3C_BUS_MODE_PURE:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_FAST:
+ case I3C_BUS_MODE_MIXED_LIMITED:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_SLOW:
+ {
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+
+ if (!i3cbus->scl_rate.i3c ||
+ i3cbus->scl_rate.i3c > i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i3c = i3cbus->scl_rate.i2c;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* I3C/I2C frequency may have been overridden, check that user-provided
+ * values are not exceeding max possible frequency.
+ */
+
+ if (i3cbus->scl_rate.i3c > I3C_BUS_MAX_I3C_SCL_RATE ||
+ i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_RATE)
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int i3c_master_to_i3c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_init
+ ****************************************************************************/
+
+static FAR void *i3c_ccc_cmd_dest_init(FAR struct i3c_ccc_cmd_dest *dest,
+ unsigned char addr, uint16_t payloadlen)
+{
+ dest->addr = addr;
+ dest->payload.len = payloadlen;
+ if (payloadlen)
+ {
+ dest->payload.data = kmm_zalloc(payloadlen);
+ }
+ else
+ {
+ dest->payload.data = NULL;
+ }
+
+ return dest->payload.data;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_cleanup
+ ****************************************************************************/
+
+static void i3c_ccc_cmd_dest_cleanup(FAR struct i3c_ccc_cmd_dest *dest)
+{
+ kmm_free(dest->payload.data);
+}
+
+static void i3c_ccc_cmd_init(FAR struct i3c_ccc_cmd *cmd, bool rnw,
+ unsigned char id, FAR struct i3c_ccc_cmd_dest *dests,
+ unsigned int ndests)
+{
+ cmd->rnw = rnw ? 1 : 0;
+ cmd->id = id;
+ cmd->dests = dests;
+ cmd->ndests = ndests;
+ cmd->err = I3C_ERROR_UNKNOWN;
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_alloc_i3c_dev(FAR struct i3c_master_controller *master,
+ FAR const struct i3c_device_info *info)
+{
+ FAR struct i3c_dev_desc *dev;
+
+ dev = kmm_zalloc(sizeof(*dev));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ dev->common.master = master;
+ dev->info = *info;
+ nxmutex_init(&dev->ibi_lock);
+
+ return dev;
+}
+
+static int i3c_master_rstdaa_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status addrstat;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ addrstat = i3c_bus_get_addr_slot_status(&master->bus, addr);
+ if (addr != I3C_BROADCAST_ADDR && addrstat != I3C_ADDR_SLOT_I3C_DEV)
+ {
+ return -EINVAL;
+ }
+
+ i3c_ccc_cmd_dest_init(&dest, addr, 0);
+ i3c_ccc_cmd_init(&cmd, false,
+ I3C_CCC_RSTDAA(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_enec_disec_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char addr, bool enable, unsigned char evts)
+{
+ FAR struct i3c_ccc_events *events;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ events = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*events));
+ if (events == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ events->events = evts;
+ i3c_ccc_cmd_init(&cmd, false,
+ enable ?
+ I3C_CCC_ENEC(addr == I3C_BROADCAST_ADDR) :
+ I3C_CCC_DISEC(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setda_locked(FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr, bool setdasa)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_setda *setda;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (!oldaddr || !newaddr)
+ {
+ return -EINVAL;
+ }
+
+ setda = i3c_ccc_cmd_dest_init(&dest, oldaddr, sizeof(*setda));
+ if (setda == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ setda->addr = newaddr << 1;
+ i3c_ccc_cmd_init(&cmd, false,
+ setdasa ? I3C_CCC_SETDASA : I3C_CCC_SETNEWDA,
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setnewda_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr)
+{
+ return i3c_master_setda_locked(master, oldaddr, newaddr, false);
+}
+
+static int i3c_master_getmrl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mrl *mrl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mrl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mrl));
+ if (mrl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ /* When the device does not have IBI payload GETMRL only returns 2
+ * bytes of data.
+ */
+
+ if (!(info->bcr & I3C_BCR_IBI_PAYLOAD))
+ {
+ dest.payload.len -= 1;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMRL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ switch (dest.payload.len)
+ {
+ case 3:
+ {
+ info->max_ibi_len = mrl->ibi_len;
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ case 2:
+ {
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ default:
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmwl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mwl *mwl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mwl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mwl));
+ if (mwl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMWL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != sizeof(*mwl))
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_write_len = ntohs(mwl->len);
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmxds_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getmxds *getmaxds;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getmaxds = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*getmaxds));
+ if (getmaxds == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 2 && dest.payload.len != 5)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_read_ds = getmaxds->maxrd;
+ info->max_write_ds = getmaxds->maxwr;
+ if (dest.payload.len == 5)
+ {
+ info->max_read_turnaround = getmaxds->maxrdturn[0] |
+ ((uint32_t)getmaxds->maxrdturn[1] << 8) |
+ ((uint32_t)getmaxds->maxrdturn[2] << 16);
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_gethdrcap_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_gethdrcap *gethdrcap;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ gethdrcap = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*gethdrcap));
+ if (gethdrcap == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETHDRCAP, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 1)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->hdr_cap = gethdrcap->modes;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getpid_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getpid *getpid;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+ int i;
+
+ getpid = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getpid));
+ if (getpid == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETPID, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->pid = 0;
+ for (i = 0; i < sizeof(getpid->pid); i++)
+ {
+ int sft = (sizeof(getpid->pid) - i - 1) * 8;
+ info->pid |= (uint64_t)getpid->pid[i] << sft;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getbcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getbcr *getbcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getbcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getbcr));
+ if (getbcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETBCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->bcr = getbcr->bcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getdcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getdcr *getdcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getdcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getdcr));
+ if (getdcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETDCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->dcr = getdcr->dcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_retrieve_dev_info(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status slot_status;
+ int ret;
+
+ if (!dev->info.dyn_addr)
+ {
+ return -EINVAL;
+ }
+
+ slot_status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (slot_status == I3C_ADDR_SLOT_RSVD)
+ {
+ return -EINVAL;
+ }
+
+ ret = i3c_master_getpid_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getbcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getdcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ if (dev->info.bcr & I3C_BCR_MAX_DATA_SPEED_LIM)
+ {
+ ret = i3c_master_getmxds_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
+ {
+ dev->info.max_ibi_len = 1;
+ }
+
+ i3c_master_getmrl_locked(master, &dev->info);
+ i3c_master_getmwl_locked(master, &dev->info);
+
+ if (dev->info.bcr & I3C_BCR_HDR_CAP)
+ {
+ ret = i3c_master_gethdrcap_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_put_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ if (dev->info.dyn_addr)
+ {
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_FREE);
+ }
+}
+
+static int i3c_master_get_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ /* ->init_dyn_addr should have been reserved before that, so, if we're
+ * trying to apply a pre-reserved dynamic address, we should not try
+ * to reserve the address slot a second time.
+ */
+
+ if (dev->info.dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ return 0;
+}
+
+static int i3c_master_attach_i3c_dev(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_dev_desc *dev)
+{
+ int ret;
+
+ /* We don't attach devices to the controller until they are
+ * addressable on the bus.
+ */
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ ret = i3c_master_get_i3c_addrs(dev);
+ if (ret)
+ {
+ return ret;
+ }
+
+ /* Do not attach the master device itself. */
+
+ if (master->this != dev && master->ops->attach_i3c_dev)
+ {
+ ret = master->ops->attach_i3c_dev(dev);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ list_add_tail(&master->bus.devs.i3c, &dev->common.node);
+
+ return 0;
+}
+
+static int i3c_master_reattach_i3c_dev(FAR struct i3c_dev_desc *dev,
+ unsigned char old_dyn_addr)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+ int ret;
+
+ if (dev->info.dyn_addr != old_dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus,
+ dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ if (master->ops->reattach_i3c_dev)
+ {
+ ret = master->ops->reattach_i3c_dev(dev, old_dyn_addr);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_detach_i3c_dev(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ /* Do not detach the master device itself. */
+
+ if (master->this != dev && master->ops->detach_i3c_dev)
+ {
+ master->ops->detach_i3c_dev(dev);
+ }
+
+ i3c_master_put_i3c_addrs(dev);
+ list_delete(&dev->common.node);
+}
+
+static void
+i3c_master_register_new_i3c_devs(FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *desc;
+
+ if (!master->init_done)
+ {
+ return;
+ }
+
+ i3c_bus_for_each_i3cdev(&master->bus, desc)
+ {
+ if (desc->dev || !desc->info.dyn_addr || desc == master->this)
+ {
+ continue;
+ }
+
+ desc->dev = kmm_zalloc(sizeof(*desc->dev));
+ if (desc->dev == NULL)
+ {
+ continue;
+ }
+
+ desc->dev->bus = &master->bus;
+ desc->dev->desc = desc;
+ }
+}
+
+static void i3c_master_detach_free_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+ FAR struct i3c_dev_desc *i3ctmp;
+
+ list_for_every_entry_safe(&master->bus.devs.i3c, i3cdev, i3ctmp,
+ struct i3c_dev_desc, common.node)
+ {
+ i3c_master_detach_i3c_dev(i3cdev);
+ kmm_free(i3cdev);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_master_bus_init()
+ *
+ * Description:
+ * Initialize an I3C bus
+ *
+ * This function is following all initialisation steps described in the I3C
+ * specification:
+ *
+ * 1. Attach I2C devs to the master so that the master can fill its
+ * internal device table appropriately
+ *
+ * 2. Call &i3c_master_controller_ops->bus_init() method to initialize
+ * the master controller. That's usually where the bus mode is selected
+ * (pure bus or mixed fast/slow bus)
+ *
+ * 3. Instruct all devices on the bus to drop their dynamic address. This
+ * is particularly important when the bus was previously configured by
+ * someone else (for example the bootloader)
+ *
+ * 4. Disable all slave events.
+ *
+ * 5. Reserve address slots for I3C devices with init_dyn_addr. And if
+ * devices also have static_addr, try to pre-assign dynamic addresses
+ * requested by the FW with SETDASA and attach corresponding statically
+ * defined I3C devices to the master.
+ *
+ * 6. Do a DAA (Dynamic Address Assignment) to assign dynamic addresses to
+ * all remaining I3C devices
+ *
+ * Once this is done, all I3C and I2C devices should be usable.
+ *
+ * Input Parameters:
+ * master - main master initializing the bus
+ *
+ * Returned Value:
+ * A 0 in case of success, an negative error code otherwise.
+ ****************************************************************************/
+
+static int i3c_master_bus_init(FAR struct i3c_master_controller *master)
+{
+ int ret;
+
+ /* Now execute the controller specific ->bus_init() routine, which
+ * might configure its internal logic to match the bus limitations.
+ */
+
+ ret = master->ops->bus_init(master);
+ if (ret)
+ {
+ goto err_detach_devs;
+ }
+
+ /* The master device should have been instantiated in ->bus_init(),
+ * complain if this was not the case.
+ */
+
+ if (master->this == NULL)
+ {
+ i3cerr("master_set_info() was not called in ->bus_init()\n");
+ ret = -EINVAL;
+ goto err_bus_cleanup;
+ }
+
+ /* Reset all dynamic address that may have been assigned before
+ * (assigned by the bootloader for example).
+ */
+
+ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_rstdaa_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Disable all slave events before starting DAA. */
+
+ ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
+ I3C_CCC_EVENT_HJ);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_disec_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Reserve init_dyn_addr first, and then try to pre-assign dynamic
+ * address and retrieve device information if needed.
+ * In case pre-assign dynamic address fails, setting dynamic address to
+ * the requested init_dyn_addr is retried after DAA is done in
+ * i3c_master_add_i3c_dev_locked().
+ */
+
+ ret = i3c_master_do_daa(master);
+ if (ret)
+ {
+ i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ i3cerr("master_do_daa failed:%d (no device)\n", ret);
+ }
+
+ return 0;
+
+err_bus_cleanup:
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+err_detach_devs:
+ i3c_master_detach_free_devs(master);
+
+ return ret;
+}
+
+static void i3c_master_bus_cleanup(FAR struct i3c_master_controller *master)
+{
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+ i3c_master_detach_free_devs(master);
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_search_i3c_dev_duplicate(FAR struct i3c_dev_desc *refdev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(refdev);
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev != refdev && i3cdev->info.pid == refdev->info.pid)
+ {
+ return i3cdev;
+ }
+ }
+
+ return NULL;
+}
+
+static int i3c_master_i2c_adapter_xfer(FAR struct i2c_master_s *i2c,
+ FAR struct i2c_msg_s *xfers, int nxfers)
+{
+ FAR struct i3c_master_controller *master = i2c_adapter_to_i3c_master(i2c);
+ int i;
+ int ret;
+ uint16_t addr;
+
+ if (xfers == NULL || master == NULL || nxfers <= 0)
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->i2c_xfers == NULL)
+ {
+ return -ENOTSUP;
+ }
+
+ /* Doing transfers to different devices is not supported. */
+
+ addr = xfers[0].addr;
+ for (i = 1; i < nxfers; i++)
+ {
+ if (addr != xfers[i].addr)
+ {
+ return -ENOTSUP;
+ }
+ }
+
+ i3c_bus_normaluse_lock(&master->bus);
+ ret = master->ops->i2c_xfers(master, xfers, nxfers);
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ return ret ? ret : nxfers;
+}
+
+static struct i2c_ops_s i3c_master_i2c_algo =
+{
+ .transfer = i3c_master_i2c_adapter_xfer,
+};
+
+static void i3c_master_unregister_i3c_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev->dev == NULL)
+ {
+ continue;
+ }
+
+ i3cdev->dev->desc = NULL;
+ i3cdev->dev->bus = NULL;
+ kmm_free(i3cdev->dev);
+ i3cdev->dev = NULL;
+ }
+}
+
+static void i3c_master_handle_ibi(FAR void *arg)
+{
+ FAR struct i3c_ibi_slot *slot = (struct i3c_ibi_slot *)arg;
+ FAR struct i3c_dev_desc *dev = slot->dev;
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ struct i3c_ibi_payload payload;
+
+ payload.data = slot->data;
+ payload.len = slot->len;
+
+ if (dev->dev)
+ {
+ dev->ibi->handler(dev->dev, &payload);
+ }
+
+ master->ops->recycle_ibi_slot(dev, slot);
+ atomic_fetch_sub(&dev->ibi->pending_ibis, 1);
+ if (!atomic_load(&dev->ibi->pending_ibis))
+ {
+ sem_post(&dev->ibi->all_ibis_handled);
+ }
+}
+
+static void i3c_master_init_ibi_slot(FAR struct i3c_dev_desc *dev,
+ FAR struct i3c_ibi_slot *slot)
+{
+ slot->dev = dev;
+}
+
+static int i3c_master_check_ops(
+ FAR const struct i3c_master_controller_ops *ops)
+{
+ if (ops == NULL || ops->bus_init == NULL || ops->priv_xfers == NULL ||
+ ops->send_ccc_cmd == NULL || ops->do_daa == NULL ||
+ ops->i2c_xfers == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (ops->request_ibi &&
+ (ops->enable_ibi == NULL || ops->disable_ibi == NULL ||
+ ops->free_ibi == NULL || ops->recycle_ibi_slot == NULL))
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i2c_unregister_driver()
+ *
+ * Description:
+ * Perform an I2C driver unregister operation.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ *
+ ****************************************************************************/
+
+static void i2c_unregister_driver(FAR struct i3c_master_controller *master)
+{
+ char devname[12];
+ int i2c_bus_id = i3c_master_to_i2c_bus_number(master);
+
+ snprintf(devname, 12, "/dev/i2c%d", i2c_bus_id);
Review Comment:
Done~
##########
drivers/i3c/master.c:
##########
@@ -0,0 +1,2282 @@
+/****************************************************************************
+ * drivers/i3c/master.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <stdio.h>
+#include <debug.h>
+
+#include <nuttx/list.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/nuttx.h>
+#include <nuttx/mutex.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mm/mm.h>
+#include <nuttx/spinlock.h>
+#include <netinet/in.h>
+
+#include <nuttx/i3c/master.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/i3c/i3c_driver.h>
+#include <nuttx/i3c/device.h>
+#include "internals.h"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_lock
+ *
+ * Description:
+ * Lock the bus for a maintenance operation
+ *
+ * This function takes the bus lock so that no other operations can
+ * occur on the bus. This is needed for all kind of bus maintenance
+ * operation, like
+ * - enabling/disabling slave events
+ * - re-triggering DAA
+ * - changing the dynamic address of a device
+ * - relinquishing mastership
+ *
+ * The reason for this kind of locking is that we don't want drivers
+ * and core logic to rely on I3C device information that could be
+ * changed behind their back.
+ *
+ * Input Parameters:
+ * bus - I3C bus to take the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_unlock
+ *
+ * Description:
+ * Release the bus lock after a maintenance operation.
+ *
+ * Should be called when the bus maintenance operation is done.
+ * See i3c_bus_maintenance_lock() for more details on what these
+ * maintenance operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_to_i3c_master
+ ****************************************************************************/
+
+static FAR struct i3c_master_controller *
+i3c_bus_to_i3c_master(FAR struct i3c_bus *i3cbus)
+{
+ return container_of(i3cbus, struct i3c_master_controller, bus);
+}
+
+static FAR struct i3c_master_controller *
+i2c_adapter_to_i3c_master(FAR struct i2c_master_s *i2c_master)
+{
+ return container_of(i2c_master, struct i3c_master_controller, i2c);
+}
+
+static int i3c_master_to_i2c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i2c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_addr_slot_status
+ ****************************************************************************/
+
+static enum i3c_addr_slot_status
+i3c_bus_get_addr_slot_status(FAR struct i3c_bus *bus, uint16_t addr)
+{
+ int32_t status;
+ int bitpos = addr * 2;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return I3C_ADDR_SLOT_RSVD;
+ }
+
+ status = bus->addrslots[bitpos / I3C_BITS_PER_LONG];
+ status >>= bitpos % I3C_BITS_PER_LONG;
+
+ return status & I3C_ADDR_SLOT_STATUS_MASK;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_addr_slot_status
+ ****************************************************************************/
+
+static void i3c_bus_set_addr_slot_status(FAR struct i3c_bus *bus,
+ uint16_t addr, enum i3c_addr_slot_status status)
+{
+ int bitpos = addr * 2;
+ FAR uint32_t *ptr;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return;
+ }
+
+ ptr = bus->addrslots + (bitpos / I3C_BITS_PER_LONG);
+ *ptr &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK <<
+ (bitpos % I3C_BITS_PER_LONG));
+ *ptr |= (unsigned long)status << (bitpos % I3C_BITS_PER_LONG);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_dev_addr_is_avail
+ ****************************************************************************/
+
+static bool i3c_bus_dev_addr_is_avail(FAR struct i3c_bus *bus,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status status;
+
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ return status == I3C_ADDR_SLOT_FREE;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_free_addr
+ ****************************************************************************/
+
+static int i3c_bus_get_free_addr(FAR struct i3c_bus *bus,
+ unsigned char start_addr)
+{
+ enum i3c_addr_slot_status status;
+ unsigned char addr;
+
+ for (addr = start_addr; addr < I3C_MAX_ADDR; addr++)
+ {
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ if (status == I3C_ADDR_SLOT_FREE)
+ {
+ return addr;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init_addrslots
+ ****************************************************************************/
+
+static void i3c_bus_init_addrslots(FAR struct i3c_bus *bus)
+{
+ int i;
+
+ /* Addresses 0 to 7 are reserved. */
+
+ for (i = 0; i < 8; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, i, I3C_ADDR_SLOT_RSVD);
+ }
+
+ /* Reserve broadcast address and all addresses that might collide
+ * with the broadcast address when facing a single bit error.
+ */
+
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR,
+ I3C_ADDR_SLOT_RSVD);
+ for (i = 0; i < 7; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR ^ I3C_BIT(i),
+ I3C_ADDR_SLOT_RSVD);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init
+ ****************************************************************************/
+
+static void i3c_bus_init(FAR struct i3c_bus *i3cbus)
+{
+ FAR struct i3c_master_controller *master;
+ master = i3c_bus_to_i3c_master(i3cbus);
+
+ nxmutex_init(&i3cbus->lock);
+ list_initialize(&i3cbus->devs.i3c);
+ i3c_bus_init_addrslots(i3cbus);
+ i3cbus->mode = I3C_BUS_MODE_PURE;
+ i3cbus->id = master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_mode
+ ****************************************************************************/
+
+static int i3c_bus_set_mode(FAR struct i3c_bus *i3cbus,
+ enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate)
+{
+ i3cbus->mode = mode;
+
+ switch (i3cbus->mode)
+ {
+ case I3C_BUS_MODE_PURE:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_FAST:
+ case I3C_BUS_MODE_MIXED_LIMITED:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_SLOW:
+ {
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+
+ if (!i3cbus->scl_rate.i3c ||
+ i3cbus->scl_rate.i3c > i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i3c = i3cbus->scl_rate.i2c;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* I3C/I2C frequency may have been overridden, check that user-provided
+ * values are not exceeding max possible frequency.
+ */
+
+ if (i3cbus->scl_rate.i3c > I3C_BUS_MAX_I3C_SCL_RATE ||
+ i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_RATE)
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int i3c_master_to_i3c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_init
+ ****************************************************************************/
+
+static FAR void *i3c_ccc_cmd_dest_init(FAR struct i3c_ccc_cmd_dest *dest,
+ unsigned char addr, uint16_t payloadlen)
+{
+ dest->addr = addr;
+ dest->payload.len = payloadlen;
+ if (payloadlen)
+ {
+ dest->payload.data = kmm_zalloc(payloadlen);
+ }
+ else
+ {
+ dest->payload.data = NULL;
+ }
+
+ return dest->payload.data;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_cleanup
+ ****************************************************************************/
+
+static void i3c_ccc_cmd_dest_cleanup(FAR struct i3c_ccc_cmd_dest *dest)
+{
+ kmm_free(dest->payload.data);
+}
+
+static void i3c_ccc_cmd_init(FAR struct i3c_ccc_cmd *cmd, bool rnw,
+ unsigned char id, FAR struct i3c_ccc_cmd_dest *dests,
+ unsigned int ndests)
+{
+ cmd->rnw = rnw ? 1 : 0;
+ cmd->id = id;
+ cmd->dests = dests;
+ cmd->ndests = ndests;
+ cmd->err = I3C_ERROR_UNKNOWN;
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_alloc_i3c_dev(FAR struct i3c_master_controller *master,
+ FAR const struct i3c_device_info *info)
+{
+ FAR struct i3c_dev_desc *dev;
+
+ dev = kmm_zalloc(sizeof(*dev));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ dev->common.master = master;
+ dev->info = *info;
+ nxmutex_init(&dev->ibi_lock);
+
+ return dev;
+}
+
+static int i3c_master_rstdaa_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status addrstat;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ addrstat = i3c_bus_get_addr_slot_status(&master->bus, addr);
+ if (addr != I3C_BROADCAST_ADDR && addrstat != I3C_ADDR_SLOT_I3C_DEV)
+ {
+ return -EINVAL;
+ }
+
+ i3c_ccc_cmd_dest_init(&dest, addr, 0);
+ i3c_ccc_cmd_init(&cmd, false,
+ I3C_CCC_RSTDAA(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_enec_disec_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char addr, bool enable, unsigned char evts)
+{
+ FAR struct i3c_ccc_events *events;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ events = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*events));
+ if (events == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ events->events = evts;
+ i3c_ccc_cmd_init(&cmd, false,
+ enable ?
+ I3C_CCC_ENEC(addr == I3C_BROADCAST_ADDR) :
+ I3C_CCC_DISEC(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setda_locked(FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr, bool setdasa)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_setda *setda;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (!oldaddr || !newaddr)
+ {
+ return -EINVAL;
+ }
+
+ setda = i3c_ccc_cmd_dest_init(&dest, oldaddr, sizeof(*setda));
+ if (setda == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ setda->addr = newaddr << 1;
+ i3c_ccc_cmd_init(&cmd, false,
+ setdasa ? I3C_CCC_SETDASA : I3C_CCC_SETNEWDA,
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setnewda_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr)
+{
+ return i3c_master_setda_locked(master, oldaddr, newaddr, false);
+}
+
+static int i3c_master_getmrl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mrl *mrl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mrl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mrl));
+ if (mrl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ /* When the device does not have IBI payload GETMRL only returns 2
+ * bytes of data.
+ */
+
+ if (!(info->bcr & I3C_BCR_IBI_PAYLOAD))
+ {
+ dest.payload.len -= 1;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMRL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ switch (dest.payload.len)
+ {
+ case 3:
+ {
+ info->max_ibi_len = mrl->ibi_len;
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ case 2:
+ {
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ default:
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmwl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mwl *mwl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mwl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mwl));
+ if (mwl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMWL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != sizeof(*mwl))
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_write_len = ntohs(mwl->len);
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmxds_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getmxds *getmaxds;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getmaxds = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*getmaxds));
+ if (getmaxds == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 2 && dest.payload.len != 5)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_read_ds = getmaxds->maxrd;
+ info->max_write_ds = getmaxds->maxwr;
+ if (dest.payload.len == 5)
+ {
+ info->max_read_turnaround = getmaxds->maxrdturn[0] |
+ ((uint32_t)getmaxds->maxrdturn[1] << 8) |
+ ((uint32_t)getmaxds->maxrdturn[2] << 16);
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_gethdrcap_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_gethdrcap *gethdrcap;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ gethdrcap = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*gethdrcap));
+ if (gethdrcap == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETHDRCAP, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 1)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->hdr_cap = gethdrcap->modes;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getpid_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getpid *getpid;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+ int i;
+
+ getpid = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getpid));
+ if (getpid == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETPID, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->pid = 0;
+ for (i = 0; i < sizeof(getpid->pid); i++)
+ {
+ int sft = (sizeof(getpid->pid) - i - 1) * 8;
+ info->pid |= (uint64_t)getpid->pid[i] << sft;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getbcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getbcr *getbcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getbcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getbcr));
+ if (getbcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETBCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->bcr = getbcr->bcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getdcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getdcr *getdcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getdcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getdcr));
+ if (getdcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETDCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->dcr = getdcr->dcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_retrieve_dev_info(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status slot_status;
+ int ret;
+
+ if (!dev->info.dyn_addr)
+ {
+ return -EINVAL;
+ }
+
+ slot_status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (slot_status == I3C_ADDR_SLOT_RSVD)
+ {
+ return -EINVAL;
+ }
+
+ ret = i3c_master_getpid_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getbcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getdcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ if (dev->info.bcr & I3C_BCR_MAX_DATA_SPEED_LIM)
+ {
+ ret = i3c_master_getmxds_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
+ {
+ dev->info.max_ibi_len = 1;
+ }
+
+ i3c_master_getmrl_locked(master, &dev->info);
+ i3c_master_getmwl_locked(master, &dev->info);
+
+ if (dev->info.bcr & I3C_BCR_HDR_CAP)
+ {
+ ret = i3c_master_gethdrcap_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_put_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ if (dev->info.dyn_addr)
+ {
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_FREE);
+ }
+}
+
+static int i3c_master_get_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ /* ->init_dyn_addr should have been reserved before that, so, if we're
+ * trying to apply a pre-reserved dynamic address, we should not try
+ * to reserve the address slot a second time.
+ */
+
+ if (dev->info.dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ return 0;
+}
+
+static int i3c_master_attach_i3c_dev(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_dev_desc *dev)
+{
+ int ret;
+
+ /* We don't attach devices to the controller until they are
+ * addressable on the bus.
+ */
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ ret = i3c_master_get_i3c_addrs(dev);
+ if (ret)
+ {
+ return ret;
+ }
+
+ /* Do not attach the master device itself. */
+
+ if (master->this != dev && master->ops->attach_i3c_dev)
+ {
+ ret = master->ops->attach_i3c_dev(dev);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ list_add_tail(&master->bus.devs.i3c, &dev->common.node);
+
+ return 0;
+}
+
+static int i3c_master_reattach_i3c_dev(FAR struct i3c_dev_desc *dev,
+ unsigned char old_dyn_addr)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+ int ret;
+
+ if (dev->info.dyn_addr != old_dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus,
+ dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ if (master->ops->reattach_i3c_dev)
+ {
+ ret = master->ops->reattach_i3c_dev(dev, old_dyn_addr);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_detach_i3c_dev(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ /* Do not detach the master device itself. */
+
+ if (master->this != dev && master->ops->detach_i3c_dev)
+ {
+ master->ops->detach_i3c_dev(dev);
+ }
+
+ i3c_master_put_i3c_addrs(dev);
+ list_delete(&dev->common.node);
+}
+
+static void
+i3c_master_register_new_i3c_devs(FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *desc;
+
+ if (!master->init_done)
+ {
+ return;
+ }
+
+ i3c_bus_for_each_i3cdev(&master->bus, desc)
+ {
+ if (desc->dev || !desc->info.dyn_addr || desc == master->this)
+ {
+ continue;
+ }
+
+ desc->dev = kmm_zalloc(sizeof(*desc->dev));
+ if (desc->dev == NULL)
+ {
+ continue;
+ }
+
+ desc->dev->bus = &master->bus;
+ desc->dev->desc = desc;
+ }
+}
+
+static void i3c_master_detach_free_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+ FAR struct i3c_dev_desc *i3ctmp;
+
+ list_for_every_entry_safe(&master->bus.devs.i3c, i3cdev, i3ctmp,
+ struct i3c_dev_desc, common.node)
+ {
+ i3c_master_detach_i3c_dev(i3cdev);
+ kmm_free(i3cdev);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_master_bus_init()
+ *
+ * Description:
+ * Initialize an I3C bus
+ *
+ * This function is following all initialisation steps described in the I3C
+ * specification:
+ *
+ * 1. Attach I2C devs to the master so that the master can fill its
+ * internal device table appropriately
+ *
+ * 2. Call &i3c_master_controller_ops->bus_init() method to initialize
+ * the master controller. That's usually where the bus mode is selected
+ * (pure bus or mixed fast/slow bus)
+ *
+ * 3. Instruct all devices on the bus to drop their dynamic address. This
+ * is particularly important when the bus was previously configured by
+ * someone else (for example the bootloader)
+ *
+ * 4. Disable all slave events.
+ *
+ * 5. Reserve address slots for I3C devices with init_dyn_addr. And if
+ * devices also have static_addr, try to pre-assign dynamic addresses
+ * requested by the FW with SETDASA and attach corresponding statically
+ * defined I3C devices to the master.
+ *
+ * 6. Do a DAA (Dynamic Address Assignment) to assign dynamic addresses to
+ * all remaining I3C devices
+ *
+ * Once this is done, all I3C and I2C devices should be usable.
+ *
+ * Input Parameters:
+ * master - main master initializing the bus
+ *
+ * Returned Value:
+ * A 0 in case of success, an negative error code otherwise.
+ ****************************************************************************/
+
+static int i3c_master_bus_init(FAR struct i3c_master_controller *master)
+{
+ int ret;
+
+ /* Now execute the controller specific ->bus_init() routine, which
+ * might configure its internal logic to match the bus limitations.
+ */
+
+ ret = master->ops->bus_init(master);
+ if (ret)
+ {
+ goto err_detach_devs;
+ }
+
+ /* The master device should have been instantiated in ->bus_init(),
+ * complain if this was not the case.
+ */
+
+ if (master->this == NULL)
+ {
+ i3cerr("master_set_info() was not called in ->bus_init()\n");
+ ret = -EINVAL;
+ goto err_bus_cleanup;
+ }
+
+ /* Reset all dynamic address that may have been assigned before
+ * (assigned by the bootloader for example).
+ */
+
+ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_rstdaa_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Disable all slave events before starting DAA. */
+
+ ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
+ I3C_CCC_EVENT_HJ);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_disec_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Reserve init_dyn_addr first, and then try to pre-assign dynamic
+ * address and retrieve device information if needed.
+ * In case pre-assign dynamic address fails, setting dynamic address to
+ * the requested init_dyn_addr is retried after DAA is done in
+ * i3c_master_add_i3c_dev_locked().
+ */
+
+ ret = i3c_master_do_daa(master);
+ if (ret)
+ {
+ i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ i3cerr("master_do_daa failed:%d (no device)\n", ret);
+ }
+
+ return 0;
+
+err_bus_cleanup:
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+err_detach_devs:
+ i3c_master_detach_free_devs(master);
+
+ return ret;
+}
+
+static void i3c_master_bus_cleanup(FAR struct i3c_master_controller *master)
+{
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+ i3c_master_detach_free_devs(master);
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_search_i3c_dev_duplicate(FAR struct i3c_dev_desc *refdev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(refdev);
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev != refdev && i3cdev->info.pid == refdev->info.pid)
+ {
+ return i3cdev;
+ }
+ }
+
+ return NULL;
+}
+
+static int i3c_master_i2c_adapter_xfer(FAR struct i2c_master_s *i2c,
+ FAR struct i2c_msg_s *xfers, int nxfers)
+{
+ FAR struct i3c_master_controller *master = i2c_adapter_to_i3c_master(i2c);
+ int i;
+ int ret;
+ uint16_t addr;
+
+ if (xfers == NULL || master == NULL || nxfers <= 0)
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->i2c_xfers == NULL)
+ {
+ return -ENOTSUP;
+ }
+
+ /* Doing transfers to different devices is not supported. */
+
+ addr = xfers[0].addr;
+ for (i = 1; i < nxfers; i++)
+ {
+ if (addr != xfers[i].addr)
+ {
+ return -ENOTSUP;
+ }
+ }
+
+ i3c_bus_normaluse_lock(&master->bus);
+ ret = master->ops->i2c_xfers(master, xfers, nxfers);
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ return ret ? ret : nxfers;
+}
+
+static struct i2c_ops_s i3c_master_i2c_algo =
+{
+ .transfer = i3c_master_i2c_adapter_xfer,
+};
+
+static void i3c_master_unregister_i3c_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev->dev == NULL)
+ {
+ continue;
+ }
+
+ i3cdev->dev->desc = NULL;
+ i3cdev->dev->bus = NULL;
+ kmm_free(i3cdev->dev);
+ i3cdev->dev = NULL;
+ }
+}
+
+static void i3c_master_handle_ibi(FAR void *arg)
+{
+ FAR struct i3c_ibi_slot *slot = (struct i3c_ibi_slot *)arg;
+ FAR struct i3c_dev_desc *dev = slot->dev;
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ struct i3c_ibi_payload payload;
+
+ payload.data = slot->data;
+ payload.len = slot->len;
+
+ if (dev->dev)
+ {
+ dev->ibi->handler(dev->dev, &payload);
+ }
+
+ master->ops->recycle_ibi_slot(dev, slot);
+ atomic_fetch_sub(&dev->ibi->pending_ibis, 1);
+ if (!atomic_load(&dev->ibi->pending_ibis))
+ {
+ sem_post(&dev->ibi->all_ibis_handled);
+ }
+}
+
+static void i3c_master_init_ibi_slot(FAR struct i3c_dev_desc *dev,
+ FAR struct i3c_ibi_slot *slot)
+{
+ slot->dev = dev;
+}
+
+static int i3c_master_check_ops(
+ FAR const struct i3c_master_controller_ops *ops)
+{
+ if (ops == NULL || ops->bus_init == NULL || ops->priv_xfers == NULL ||
+ ops->send_ccc_cmd == NULL || ops->do_daa == NULL ||
+ ops->i2c_xfers == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (ops->request_ibi &&
+ (ops->enable_ibi == NULL || ops->disable_ibi == NULL ||
+ ops->free_ibi == NULL || ops->recycle_ibi_slot == NULL))
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i2c_unregister_driver()
Review Comment:
Done~
##########
drivers/i3c/master.c:
##########
@@ -0,0 +1,2282 @@
+/****************************************************************************
+ * drivers/i3c/master.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <stdio.h>
+#include <debug.h>
+
+#include <nuttx/list.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/nuttx.h>
+#include <nuttx/mutex.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mm/mm.h>
+#include <nuttx/spinlock.h>
+#include <netinet/in.h>
+
+#include <nuttx/i3c/master.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/i3c/i3c_driver.h>
+#include <nuttx/i3c/device.h>
+#include "internals.h"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_lock
+ *
+ * Description:
+ * Lock the bus for a maintenance operation
+ *
+ * This function takes the bus lock so that no other operations can
+ * occur on the bus. This is needed for all kind of bus maintenance
+ * operation, like
+ * - enabling/disabling slave events
+ * - re-triggering DAA
+ * - changing the dynamic address of a device
+ * - relinquishing mastership
+ *
+ * The reason for this kind of locking is that we don't want drivers
+ * and core logic to rely on I3C device information that could be
+ * changed behind their back.
+ *
+ * Input Parameters:
+ * bus - I3C bus to take the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_unlock
+ *
+ * Description:
+ * Release the bus lock after a maintenance operation.
+ *
+ * Should be called when the bus maintenance operation is done.
+ * See i3c_bus_maintenance_lock() for more details on what these
+ * maintenance operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_to_i3c_master
+ ****************************************************************************/
+
+static FAR struct i3c_master_controller *
+i3c_bus_to_i3c_master(FAR struct i3c_bus *i3cbus)
+{
+ return container_of(i3cbus, struct i3c_master_controller, bus);
+}
+
+static FAR struct i3c_master_controller *
+i2c_adapter_to_i3c_master(FAR struct i2c_master_s *i2c_master)
+{
+ return container_of(i2c_master, struct i3c_master_controller, i2c);
+}
+
+static int i3c_master_to_i2c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i2c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_addr_slot_status
+ ****************************************************************************/
+
+static enum i3c_addr_slot_status
+i3c_bus_get_addr_slot_status(FAR struct i3c_bus *bus, uint16_t addr)
+{
+ int32_t status;
+ int bitpos = addr * 2;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return I3C_ADDR_SLOT_RSVD;
+ }
+
+ status = bus->addrslots[bitpos / I3C_BITS_PER_LONG];
+ status >>= bitpos % I3C_BITS_PER_LONG;
+
+ return status & I3C_ADDR_SLOT_STATUS_MASK;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_addr_slot_status
+ ****************************************************************************/
+
+static void i3c_bus_set_addr_slot_status(FAR struct i3c_bus *bus,
+ uint16_t addr, enum i3c_addr_slot_status status)
+{
+ int bitpos = addr * 2;
+ FAR uint32_t *ptr;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return;
+ }
+
+ ptr = bus->addrslots + (bitpos / I3C_BITS_PER_LONG);
+ *ptr &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK <<
+ (bitpos % I3C_BITS_PER_LONG));
+ *ptr |= (unsigned long)status << (bitpos % I3C_BITS_PER_LONG);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_dev_addr_is_avail
+ ****************************************************************************/
+
+static bool i3c_bus_dev_addr_is_avail(FAR struct i3c_bus *bus,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status status;
+
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ return status == I3C_ADDR_SLOT_FREE;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_free_addr
+ ****************************************************************************/
+
+static int i3c_bus_get_free_addr(FAR struct i3c_bus *bus,
+ unsigned char start_addr)
+{
+ enum i3c_addr_slot_status status;
+ unsigned char addr;
+
+ for (addr = start_addr; addr < I3C_MAX_ADDR; addr++)
+ {
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ if (status == I3C_ADDR_SLOT_FREE)
+ {
+ return addr;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init_addrslots
+ ****************************************************************************/
+
+static void i3c_bus_init_addrslots(FAR struct i3c_bus *bus)
+{
+ int i;
+
+ /* Addresses 0 to 7 are reserved. */
+
+ for (i = 0; i < 8; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, i, I3C_ADDR_SLOT_RSVD);
+ }
+
+ /* Reserve broadcast address and all addresses that might collide
+ * with the broadcast address when facing a single bit error.
+ */
+
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR,
+ I3C_ADDR_SLOT_RSVD);
+ for (i = 0; i < 7; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR ^ I3C_BIT(i),
+ I3C_ADDR_SLOT_RSVD);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init
+ ****************************************************************************/
+
+static void i3c_bus_init(FAR struct i3c_bus *i3cbus)
+{
+ FAR struct i3c_master_controller *master;
+ master = i3c_bus_to_i3c_master(i3cbus);
+
+ nxmutex_init(&i3cbus->lock);
+ list_initialize(&i3cbus->devs.i3c);
+ i3c_bus_init_addrslots(i3cbus);
+ i3cbus->mode = I3C_BUS_MODE_PURE;
+ i3cbus->id = master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_mode
+ ****************************************************************************/
+
+static int i3c_bus_set_mode(FAR struct i3c_bus *i3cbus,
+ enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate)
+{
+ i3cbus->mode = mode;
+
+ switch (i3cbus->mode)
+ {
+ case I3C_BUS_MODE_PURE:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_FAST:
+ case I3C_BUS_MODE_MIXED_LIMITED:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_SLOW:
+ {
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+
+ if (!i3cbus->scl_rate.i3c ||
+ i3cbus->scl_rate.i3c > i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i3c = i3cbus->scl_rate.i2c;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* I3C/I2C frequency may have been overridden, check that user-provided
+ * values are not exceeding max possible frequency.
+ */
+
+ if (i3cbus->scl_rate.i3c > I3C_BUS_MAX_I3C_SCL_RATE ||
+ i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_RATE)
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int i3c_master_to_i3c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_init
+ ****************************************************************************/
+
+static FAR void *i3c_ccc_cmd_dest_init(FAR struct i3c_ccc_cmd_dest *dest,
+ unsigned char addr, uint16_t payloadlen)
+{
+ dest->addr = addr;
+ dest->payload.len = payloadlen;
+ if (payloadlen)
+ {
+ dest->payload.data = kmm_zalloc(payloadlen);
+ }
+ else
+ {
+ dest->payload.data = NULL;
+ }
+
+ return dest->payload.data;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_cleanup
+ ****************************************************************************/
+
+static void i3c_ccc_cmd_dest_cleanup(FAR struct i3c_ccc_cmd_dest *dest)
+{
+ kmm_free(dest->payload.data);
+}
+
+static void i3c_ccc_cmd_init(FAR struct i3c_ccc_cmd *cmd, bool rnw,
+ unsigned char id, FAR struct i3c_ccc_cmd_dest *dests,
+ unsigned int ndests)
+{
+ cmd->rnw = rnw ? 1 : 0;
+ cmd->id = id;
+ cmd->dests = dests;
+ cmd->ndests = ndests;
+ cmd->err = I3C_ERROR_UNKNOWN;
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_alloc_i3c_dev(FAR struct i3c_master_controller *master,
+ FAR const struct i3c_device_info *info)
+{
+ FAR struct i3c_dev_desc *dev;
+
+ dev = kmm_zalloc(sizeof(*dev));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ dev->common.master = master;
+ dev->info = *info;
+ nxmutex_init(&dev->ibi_lock);
+
+ return dev;
+}
+
+static int i3c_master_rstdaa_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status addrstat;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ addrstat = i3c_bus_get_addr_slot_status(&master->bus, addr);
+ if (addr != I3C_BROADCAST_ADDR && addrstat != I3C_ADDR_SLOT_I3C_DEV)
+ {
+ return -EINVAL;
+ }
+
+ i3c_ccc_cmd_dest_init(&dest, addr, 0);
+ i3c_ccc_cmd_init(&cmd, false,
+ I3C_CCC_RSTDAA(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_enec_disec_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char addr, bool enable, unsigned char evts)
+{
+ FAR struct i3c_ccc_events *events;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ events = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*events));
+ if (events == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ events->events = evts;
+ i3c_ccc_cmd_init(&cmd, false,
+ enable ?
+ I3C_CCC_ENEC(addr == I3C_BROADCAST_ADDR) :
+ I3C_CCC_DISEC(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setda_locked(FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr, bool setdasa)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_setda *setda;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (!oldaddr || !newaddr)
+ {
+ return -EINVAL;
+ }
+
+ setda = i3c_ccc_cmd_dest_init(&dest, oldaddr, sizeof(*setda));
+ if (setda == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ setda->addr = newaddr << 1;
+ i3c_ccc_cmd_init(&cmd, false,
+ setdasa ? I3C_CCC_SETDASA : I3C_CCC_SETNEWDA,
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setnewda_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr)
+{
+ return i3c_master_setda_locked(master, oldaddr, newaddr, false);
+}
+
+static int i3c_master_getmrl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mrl *mrl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mrl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mrl));
+ if (mrl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ /* When the device does not have IBI payload GETMRL only returns 2
+ * bytes of data.
+ */
+
+ if (!(info->bcr & I3C_BCR_IBI_PAYLOAD))
+ {
+ dest.payload.len -= 1;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMRL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ switch (dest.payload.len)
+ {
+ case 3:
+ {
+ info->max_ibi_len = mrl->ibi_len;
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ case 2:
+ {
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ default:
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmwl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mwl *mwl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mwl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mwl));
+ if (mwl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMWL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != sizeof(*mwl))
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_write_len = ntohs(mwl->len);
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmxds_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getmxds *getmaxds;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getmaxds = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*getmaxds));
+ if (getmaxds == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 2 && dest.payload.len != 5)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_read_ds = getmaxds->maxrd;
+ info->max_write_ds = getmaxds->maxwr;
+ if (dest.payload.len == 5)
+ {
+ info->max_read_turnaround = getmaxds->maxrdturn[0] |
+ ((uint32_t)getmaxds->maxrdturn[1] << 8) |
+ ((uint32_t)getmaxds->maxrdturn[2] << 16);
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_gethdrcap_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_gethdrcap *gethdrcap;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ gethdrcap = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*gethdrcap));
+ if (gethdrcap == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETHDRCAP, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 1)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->hdr_cap = gethdrcap->modes;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getpid_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getpid *getpid;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+ int i;
+
+ getpid = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getpid));
+ if (getpid == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETPID, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->pid = 0;
+ for (i = 0; i < sizeof(getpid->pid); i++)
+ {
+ int sft = (sizeof(getpid->pid) - i - 1) * 8;
+ info->pid |= (uint64_t)getpid->pid[i] << sft;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getbcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getbcr *getbcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getbcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getbcr));
+ if (getbcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETBCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->bcr = getbcr->bcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getdcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getdcr *getdcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getdcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getdcr));
+ if (getdcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETDCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->dcr = getdcr->dcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_retrieve_dev_info(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status slot_status;
+ int ret;
+
+ if (!dev->info.dyn_addr)
+ {
+ return -EINVAL;
+ }
+
+ slot_status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (slot_status == I3C_ADDR_SLOT_RSVD)
+ {
+ return -EINVAL;
+ }
+
+ ret = i3c_master_getpid_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getbcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getdcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ if (dev->info.bcr & I3C_BCR_MAX_DATA_SPEED_LIM)
+ {
+ ret = i3c_master_getmxds_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
+ {
+ dev->info.max_ibi_len = 1;
+ }
+
+ i3c_master_getmrl_locked(master, &dev->info);
+ i3c_master_getmwl_locked(master, &dev->info);
+
+ if (dev->info.bcr & I3C_BCR_HDR_CAP)
+ {
+ ret = i3c_master_gethdrcap_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_put_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ if (dev->info.dyn_addr)
+ {
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_FREE);
+ }
+}
+
+static int i3c_master_get_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ /* ->init_dyn_addr should have been reserved before that, so, if we're
+ * trying to apply a pre-reserved dynamic address, we should not try
+ * to reserve the address slot a second time.
+ */
+
+ if (dev->info.dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ return 0;
+}
+
+static int i3c_master_attach_i3c_dev(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_dev_desc *dev)
+{
+ int ret;
+
+ /* We don't attach devices to the controller until they are
+ * addressable on the bus.
+ */
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ ret = i3c_master_get_i3c_addrs(dev);
+ if (ret)
+ {
+ return ret;
+ }
+
+ /* Do not attach the master device itself. */
+
+ if (master->this != dev && master->ops->attach_i3c_dev)
+ {
+ ret = master->ops->attach_i3c_dev(dev);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ list_add_tail(&master->bus.devs.i3c, &dev->common.node);
+
+ return 0;
+}
+
+static int i3c_master_reattach_i3c_dev(FAR struct i3c_dev_desc *dev,
+ unsigned char old_dyn_addr)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+ int ret;
+
+ if (dev->info.dyn_addr != old_dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus,
+ dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ if (master->ops->reattach_i3c_dev)
+ {
+ ret = master->ops->reattach_i3c_dev(dev, old_dyn_addr);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_detach_i3c_dev(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ /* Do not detach the master device itself. */
+
+ if (master->this != dev && master->ops->detach_i3c_dev)
+ {
+ master->ops->detach_i3c_dev(dev);
+ }
+
+ i3c_master_put_i3c_addrs(dev);
+ list_delete(&dev->common.node);
+}
+
+static void
+i3c_master_register_new_i3c_devs(FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *desc;
+
+ if (!master->init_done)
+ {
+ return;
+ }
+
+ i3c_bus_for_each_i3cdev(&master->bus, desc)
+ {
+ if (desc->dev || !desc->info.dyn_addr || desc == master->this)
+ {
+ continue;
+ }
+
+ desc->dev = kmm_zalloc(sizeof(*desc->dev));
+ if (desc->dev == NULL)
+ {
+ continue;
+ }
+
+ desc->dev->bus = &master->bus;
+ desc->dev->desc = desc;
+ }
+}
+
+static void i3c_master_detach_free_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+ FAR struct i3c_dev_desc *i3ctmp;
+
+ list_for_every_entry_safe(&master->bus.devs.i3c, i3cdev, i3ctmp,
+ struct i3c_dev_desc, common.node)
+ {
+ i3c_master_detach_i3c_dev(i3cdev);
+ kmm_free(i3cdev);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_master_bus_init()
+ *
+ * Description:
+ * Initialize an I3C bus
+ *
+ * This function is following all initialisation steps described in the I3C
+ * specification:
+ *
+ * 1. Attach I2C devs to the master so that the master can fill its
+ * internal device table appropriately
+ *
+ * 2. Call &i3c_master_controller_ops->bus_init() method to initialize
+ * the master controller. That's usually where the bus mode is selected
+ * (pure bus or mixed fast/slow bus)
+ *
+ * 3. Instruct all devices on the bus to drop their dynamic address. This
+ * is particularly important when the bus was previously configured by
+ * someone else (for example the bootloader)
+ *
+ * 4. Disable all slave events.
+ *
+ * 5. Reserve address slots for I3C devices with init_dyn_addr. And if
+ * devices also have static_addr, try to pre-assign dynamic addresses
+ * requested by the FW with SETDASA and attach corresponding statically
+ * defined I3C devices to the master.
+ *
+ * 6. Do a DAA (Dynamic Address Assignment) to assign dynamic addresses to
+ * all remaining I3C devices
+ *
+ * Once this is done, all I3C and I2C devices should be usable.
+ *
+ * Input Parameters:
+ * master - main master initializing the bus
+ *
+ * Returned Value:
+ * A 0 in case of success, an negative error code otherwise.
+ ****************************************************************************/
+
+static int i3c_master_bus_init(FAR struct i3c_master_controller *master)
+{
+ int ret;
+
+ /* Now execute the controller specific ->bus_init() routine, which
+ * might configure its internal logic to match the bus limitations.
+ */
+
+ ret = master->ops->bus_init(master);
+ if (ret)
+ {
+ goto err_detach_devs;
+ }
+
+ /* The master device should have been instantiated in ->bus_init(),
+ * complain if this was not the case.
+ */
+
+ if (master->this == NULL)
+ {
+ i3cerr("master_set_info() was not called in ->bus_init()\n");
+ ret = -EINVAL;
+ goto err_bus_cleanup;
+ }
+
+ /* Reset all dynamic address that may have been assigned before
+ * (assigned by the bootloader for example).
+ */
+
+ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_rstdaa_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Disable all slave events before starting DAA. */
+
+ ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
+ I3C_CCC_EVENT_HJ);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_disec_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Reserve init_dyn_addr first, and then try to pre-assign dynamic
+ * address and retrieve device information if needed.
+ * In case pre-assign dynamic address fails, setting dynamic address to
+ * the requested init_dyn_addr is retried after DAA is done in
+ * i3c_master_add_i3c_dev_locked().
+ */
+
+ ret = i3c_master_do_daa(master);
+ if (ret)
+ {
+ i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ i3cerr("master_do_daa failed:%d (no device)\n", ret);
+ }
+
+ return 0;
+
+err_bus_cleanup:
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+err_detach_devs:
+ i3c_master_detach_free_devs(master);
+
+ return ret;
+}
+
+static void i3c_master_bus_cleanup(FAR struct i3c_master_controller *master)
+{
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+ i3c_master_detach_free_devs(master);
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_search_i3c_dev_duplicate(FAR struct i3c_dev_desc *refdev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(refdev);
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev != refdev && i3cdev->info.pid == refdev->info.pid)
+ {
+ return i3cdev;
+ }
+ }
+
+ return NULL;
+}
+
+static int i3c_master_i2c_adapter_xfer(FAR struct i2c_master_s *i2c,
+ FAR struct i2c_msg_s *xfers, int nxfers)
+{
+ FAR struct i3c_master_controller *master = i2c_adapter_to_i3c_master(i2c);
+ int i;
+ int ret;
+ uint16_t addr;
+
+ if (xfers == NULL || master == NULL || nxfers <= 0)
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->i2c_xfers == NULL)
+ {
+ return -ENOTSUP;
+ }
+
+ /* Doing transfers to different devices is not supported. */
+
+ addr = xfers[0].addr;
+ for (i = 1; i < nxfers; i++)
+ {
+ if (addr != xfers[i].addr)
+ {
+ return -ENOTSUP;
+ }
+ }
+
+ i3c_bus_normaluse_lock(&master->bus);
+ ret = master->ops->i2c_xfers(master, xfers, nxfers);
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ return ret ? ret : nxfers;
+}
+
+static struct i2c_ops_s i3c_master_i2c_algo =
+{
+ .transfer = i3c_master_i2c_adapter_xfer,
+};
+
+static void i3c_master_unregister_i3c_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev->dev == NULL)
+ {
+ continue;
+ }
+
+ i3cdev->dev->desc = NULL;
+ i3cdev->dev->bus = NULL;
+ kmm_free(i3cdev->dev);
+ i3cdev->dev = NULL;
+ }
+}
+
+static void i3c_master_handle_ibi(FAR void *arg)
+{
+ FAR struct i3c_ibi_slot *slot = (struct i3c_ibi_slot *)arg;
+ FAR struct i3c_dev_desc *dev = slot->dev;
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ struct i3c_ibi_payload payload;
+
+ payload.data = slot->data;
+ payload.len = slot->len;
+
+ if (dev->dev)
+ {
+ dev->ibi->handler(dev->dev, &payload);
+ }
+
+ master->ops->recycle_ibi_slot(dev, slot);
+ atomic_fetch_sub(&dev->ibi->pending_ibis, 1);
+ if (!atomic_load(&dev->ibi->pending_ibis))
+ {
+ sem_post(&dev->ibi->all_ibis_handled);
+ }
+}
+
+static void i3c_master_init_ibi_slot(FAR struct i3c_dev_desc *dev,
+ FAR struct i3c_ibi_slot *slot)
+{
+ slot->dev = dev;
+}
+
+static int i3c_master_check_ops(
+ FAR const struct i3c_master_controller_ops *ops)
+{
+ if (ops == NULL || ops->bus_init == NULL || ops->priv_xfers == NULL ||
+ ops->send_ccc_cmd == NULL || ops->do_daa == NULL ||
+ ops->i2c_xfers == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (ops->request_ibi &&
+ (ops->enable_ibi == NULL || ops->disable_ibi == NULL ||
+ ops->free_ibi == NULL || ops->recycle_ibi_slot == NULL))
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i2c_unregister_driver()
+ *
+ * Description:
+ * Perform an I2C driver unregister operation.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ *
+ ****************************************************************************/
+
+static void i2c_unregister_driver(FAR struct i3c_master_controller *master)
+{
+ char devname[12];
+ int i2c_bus_id = i3c_master_to_i2c_bus_number(master);
+
+ snprintf(devname, 12, "/dev/i2c%d", i2c_bus_id);
+ nx_unlink(devname);
+}
+
+/****************************************************************************
+ * Name: i3c_unregister_driver()
+ *
+ * Description:
+ * Perform an I3C driver unregister operation.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ *
+ ****************************************************************************/
+
+static void i3c_unregister_driver(FAR struct i3c_master_controller *master)
+{
+ char devname[12];
+
+ snprintf(devname, 12, "/dev/i3c%d", i3c_master_to_i3c_bus_number(master));
Review Comment:
Done~
##########
drivers/i3c/master.c:
##########
@@ -0,0 +1,2282 @@
+/****************************************************************************
+ * drivers/i3c/master.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <stdio.h>
+#include <debug.h>
+
+#include <nuttx/list.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/nuttx.h>
+#include <nuttx/mutex.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mm/mm.h>
+#include <nuttx/spinlock.h>
+#include <netinet/in.h>
+
+#include <nuttx/i3c/master.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/i3c/i3c_driver.h>
+#include <nuttx/i3c/device.h>
+#include "internals.h"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_lock
+ *
+ * Description:
+ * Lock the bus for a maintenance operation
+ *
+ * This function takes the bus lock so that no other operations can
+ * occur on the bus. This is needed for all kind of bus maintenance
+ * operation, like
+ * - enabling/disabling slave events
+ * - re-triggering DAA
+ * - changing the dynamic address of a device
+ * - relinquishing mastership
+ *
+ * The reason for this kind of locking is that we don't want drivers
+ * and core logic to rely on I3C device information that could be
+ * changed behind their back.
+ *
+ * Input Parameters:
+ * bus - I3C bus to take the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_unlock
+ *
+ * Description:
+ * Release the bus lock after a maintenance operation.
+ *
+ * Should be called when the bus maintenance operation is done.
+ * See i3c_bus_maintenance_lock() for more details on what these
+ * maintenance operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_to_i3c_master
+ ****************************************************************************/
+
+static FAR struct i3c_master_controller *
+i3c_bus_to_i3c_master(FAR struct i3c_bus *i3cbus)
+{
+ return container_of(i3cbus, struct i3c_master_controller, bus);
+}
+
+static FAR struct i3c_master_controller *
+i2c_adapter_to_i3c_master(FAR struct i2c_master_s *i2c_master)
+{
+ return container_of(i2c_master, struct i3c_master_controller, i2c);
+}
+
+static int i3c_master_to_i2c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i2c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_addr_slot_status
+ ****************************************************************************/
+
+static enum i3c_addr_slot_status
+i3c_bus_get_addr_slot_status(FAR struct i3c_bus *bus, uint16_t addr)
+{
+ int32_t status;
+ int bitpos = addr * 2;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return I3C_ADDR_SLOT_RSVD;
+ }
+
+ status = bus->addrslots[bitpos / I3C_BITS_PER_LONG];
+ status >>= bitpos % I3C_BITS_PER_LONG;
+
+ return status & I3C_ADDR_SLOT_STATUS_MASK;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_addr_slot_status
+ ****************************************************************************/
+
+static void i3c_bus_set_addr_slot_status(FAR struct i3c_bus *bus,
+ uint16_t addr, enum i3c_addr_slot_status status)
+{
+ int bitpos = addr * 2;
+ FAR uint32_t *ptr;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return;
+ }
+
+ ptr = bus->addrslots + (bitpos / I3C_BITS_PER_LONG);
+ *ptr &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK <<
+ (bitpos % I3C_BITS_PER_LONG));
+ *ptr |= (unsigned long)status << (bitpos % I3C_BITS_PER_LONG);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_dev_addr_is_avail
+ ****************************************************************************/
+
+static bool i3c_bus_dev_addr_is_avail(FAR struct i3c_bus *bus,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status status;
+
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ return status == I3C_ADDR_SLOT_FREE;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_free_addr
+ ****************************************************************************/
+
+static int i3c_bus_get_free_addr(FAR struct i3c_bus *bus,
+ unsigned char start_addr)
+{
+ enum i3c_addr_slot_status status;
+ unsigned char addr;
+
+ for (addr = start_addr; addr < I3C_MAX_ADDR; addr++)
+ {
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ if (status == I3C_ADDR_SLOT_FREE)
+ {
+ return addr;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init_addrslots
+ ****************************************************************************/
+
+static void i3c_bus_init_addrslots(FAR struct i3c_bus *bus)
+{
+ int i;
+
+ /* Addresses 0 to 7 are reserved. */
+
+ for (i = 0; i < 8; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, i, I3C_ADDR_SLOT_RSVD);
+ }
+
+ /* Reserve broadcast address and all addresses that might collide
+ * with the broadcast address when facing a single bit error.
+ */
+
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR,
+ I3C_ADDR_SLOT_RSVD);
+ for (i = 0; i < 7; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR ^ I3C_BIT(i),
+ I3C_ADDR_SLOT_RSVD);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init
+ ****************************************************************************/
+
+static void i3c_bus_init(FAR struct i3c_bus *i3cbus)
+{
+ FAR struct i3c_master_controller *master;
+ master = i3c_bus_to_i3c_master(i3cbus);
+
+ nxmutex_init(&i3cbus->lock);
+ list_initialize(&i3cbus->devs.i3c);
+ i3c_bus_init_addrslots(i3cbus);
+ i3cbus->mode = I3C_BUS_MODE_PURE;
+ i3cbus->id = master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_mode
+ ****************************************************************************/
+
+static int i3c_bus_set_mode(FAR struct i3c_bus *i3cbus,
+ enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate)
+{
+ i3cbus->mode = mode;
+
+ switch (i3cbus->mode)
+ {
+ case I3C_BUS_MODE_PURE:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_FAST:
+ case I3C_BUS_MODE_MIXED_LIMITED:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_SLOW:
+ {
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+
+ if (!i3cbus->scl_rate.i3c ||
+ i3cbus->scl_rate.i3c > i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i3c = i3cbus->scl_rate.i2c;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* I3C/I2C frequency may have been overridden, check that user-provided
+ * values are not exceeding max possible frequency.
+ */
+
+ if (i3cbus->scl_rate.i3c > I3C_BUS_MAX_I3C_SCL_RATE ||
+ i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_RATE)
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int i3c_master_to_i3c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_init
+ ****************************************************************************/
+
+static FAR void *i3c_ccc_cmd_dest_init(FAR struct i3c_ccc_cmd_dest *dest,
+ unsigned char addr, uint16_t payloadlen)
+{
+ dest->addr = addr;
+ dest->payload.len = payloadlen;
+ if (payloadlen)
+ {
+ dest->payload.data = kmm_zalloc(payloadlen);
+ }
+ else
+ {
+ dest->payload.data = NULL;
+ }
+
+ return dest->payload.data;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_cleanup
+ ****************************************************************************/
+
+static void i3c_ccc_cmd_dest_cleanup(FAR struct i3c_ccc_cmd_dest *dest)
+{
+ kmm_free(dest->payload.data);
+}
+
+static void i3c_ccc_cmd_init(FAR struct i3c_ccc_cmd *cmd, bool rnw,
+ unsigned char id, FAR struct i3c_ccc_cmd_dest *dests,
+ unsigned int ndests)
+{
+ cmd->rnw = rnw ? 1 : 0;
+ cmd->id = id;
+ cmd->dests = dests;
+ cmd->ndests = ndests;
+ cmd->err = I3C_ERROR_UNKNOWN;
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_alloc_i3c_dev(FAR struct i3c_master_controller *master,
+ FAR const struct i3c_device_info *info)
+{
+ FAR struct i3c_dev_desc *dev;
+
+ dev = kmm_zalloc(sizeof(*dev));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ dev->common.master = master;
+ dev->info = *info;
+ nxmutex_init(&dev->ibi_lock);
+
+ return dev;
+}
+
+static int i3c_master_rstdaa_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status addrstat;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ addrstat = i3c_bus_get_addr_slot_status(&master->bus, addr);
+ if (addr != I3C_BROADCAST_ADDR && addrstat != I3C_ADDR_SLOT_I3C_DEV)
+ {
+ return -EINVAL;
+ }
+
+ i3c_ccc_cmd_dest_init(&dest, addr, 0);
+ i3c_ccc_cmd_init(&cmd, false,
+ I3C_CCC_RSTDAA(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_enec_disec_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char addr, bool enable, unsigned char evts)
+{
+ FAR struct i3c_ccc_events *events;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ events = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*events));
+ if (events == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ events->events = evts;
+ i3c_ccc_cmd_init(&cmd, false,
+ enable ?
+ I3C_CCC_ENEC(addr == I3C_BROADCAST_ADDR) :
+ I3C_CCC_DISEC(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setda_locked(FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr, bool setdasa)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_setda *setda;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (!oldaddr || !newaddr)
+ {
+ return -EINVAL;
+ }
+
+ setda = i3c_ccc_cmd_dest_init(&dest, oldaddr, sizeof(*setda));
+ if (setda == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ setda->addr = newaddr << 1;
+ i3c_ccc_cmd_init(&cmd, false,
+ setdasa ? I3C_CCC_SETDASA : I3C_CCC_SETNEWDA,
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setnewda_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr)
+{
+ return i3c_master_setda_locked(master, oldaddr, newaddr, false);
+}
+
+static int i3c_master_getmrl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mrl *mrl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mrl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mrl));
+ if (mrl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ /* When the device does not have IBI payload GETMRL only returns 2
+ * bytes of data.
+ */
+
+ if (!(info->bcr & I3C_BCR_IBI_PAYLOAD))
+ {
+ dest.payload.len -= 1;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMRL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ switch (dest.payload.len)
+ {
+ case 3:
+ {
+ info->max_ibi_len = mrl->ibi_len;
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ case 2:
+ {
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ default:
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmwl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mwl *mwl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mwl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mwl));
+ if (mwl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMWL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != sizeof(*mwl))
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_write_len = ntohs(mwl->len);
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmxds_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getmxds *getmaxds;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getmaxds = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*getmaxds));
+ if (getmaxds == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 2 && dest.payload.len != 5)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_read_ds = getmaxds->maxrd;
+ info->max_write_ds = getmaxds->maxwr;
+ if (dest.payload.len == 5)
+ {
+ info->max_read_turnaround = getmaxds->maxrdturn[0] |
+ ((uint32_t)getmaxds->maxrdturn[1] << 8) |
+ ((uint32_t)getmaxds->maxrdturn[2] << 16);
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_gethdrcap_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_gethdrcap *gethdrcap;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ gethdrcap = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*gethdrcap));
+ if (gethdrcap == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETHDRCAP, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 1)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->hdr_cap = gethdrcap->modes;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getpid_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getpid *getpid;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+ int i;
+
+ getpid = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getpid));
+ if (getpid == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETPID, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->pid = 0;
+ for (i = 0; i < sizeof(getpid->pid); i++)
+ {
+ int sft = (sizeof(getpid->pid) - i - 1) * 8;
+ info->pid |= (uint64_t)getpid->pid[i] << sft;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getbcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getbcr *getbcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getbcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getbcr));
+ if (getbcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETBCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->bcr = getbcr->bcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getdcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getdcr *getdcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getdcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getdcr));
+ if (getdcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETDCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->dcr = getdcr->dcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_retrieve_dev_info(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status slot_status;
+ int ret;
+
+ if (!dev->info.dyn_addr)
+ {
+ return -EINVAL;
+ }
+
+ slot_status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (slot_status == I3C_ADDR_SLOT_RSVD)
+ {
+ return -EINVAL;
+ }
+
+ ret = i3c_master_getpid_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getbcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getdcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ if (dev->info.bcr & I3C_BCR_MAX_DATA_SPEED_LIM)
+ {
+ ret = i3c_master_getmxds_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
+ {
+ dev->info.max_ibi_len = 1;
+ }
+
+ i3c_master_getmrl_locked(master, &dev->info);
+ i3c_master_getmwl_locked(master, &dev->info);
+
+ if (dev->info.bcr & I3C_BCR_HDR_CAP)
+ {
+ ret = i3c_master_gethdrcap_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_put_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ if (dev->info.dyn_addr)
+ {
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_FREE);
+ }
+}
+
+static int i3c_master_get_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ /* ->init_dyn_addr should have been reserved before that, so, if we're
+ * trying to apply a pre-reserved dynamic address, we should not try
+ * to reserve the address slot a second time.
+ */
+
+ if (dev->info.dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ return 0;
+}
+
+static int i3c_master_attach_i3c_dev(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_dev_desc *dev)
+{
+ int ret;
+
+ /* We don't attach devices to the controller until they are
+ * addressable on the bus.
+ */
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ ret = i3c_master_get_i3c_addrs(dev);
+ if (ret)
+ {
+ return ret;
+ }
+
+ /* Do not attach the master device itself. */
+
+ if (master->this != dev && master->ops->attach_i3c_dev)
+ {
+ ret = master->ops->attach_i3c_dev(dev);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ list_add_tail(&master->bus.devs.i3c, &dev->common.node);
+
+ return 0;
+}
+
+static int i3c_master_reattach_i3c_dev(FAR struct i3c_dev_desc *dev,
+ unsigned char old_dyn_addr)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+ int ret;
+
+ if (dev->info.dyn_addr != old_dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus,
+ dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ if (master->ops->reattach_i3c_dev)
+ {
+ ret = master->ops->reattach_i3c_dev(dev, old_dyn_addr);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_detach_i3c_dev(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ /* Do not detach the master device itself. */
+
+ if (master->this != dev && master->ops->detach_i3c_dev)
+ {
+ master->ops->detach_i3c_dev(dev);
+ }
+
+ i3c_master_put_i3c_addrs(dev);
+ list_delete(&dev->common.node);
+}
+
+static void
+i3c_master_register_new_i3c_devs(FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *desc;
+
+ if (!master->init_done)
+ {
+ return;
+ }
+
+ i3c_bus_for_each_i3cdev(&master->bus, desc)
+ {
+ if (desc->dev || !desc->info.dyn_addr || desc == master->this)
+ {
+ continue;
+ }
+
+ desc->dev = kmm_zalloc(sizeof(*desc->dev));
+ if (desc->dev == NULL)
+ {
+ continue;
+ }
+
+ desc->dev->bus = &master->bus;
+ desc->dev->desc = desc;
+ }
+}
+
+static void i3c_master_detach_free_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+ FAR struct i3c_dev_desc *i3ctmp;
+
+ list_for_every_entry_safe(&master->bus.devs.i3c, i3cdev, i3ctmp,
+ struct i3c_dev_desc, common.node)
+ {
+ i3c_master_detach_i3c_dev(i3cdev);
+ kmm_free(i3cdev);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_master_bus_init()
+ *
+ * Description:
+ * Initialize an I3C bus
+ *
+ * This function is following all initialisation steps described in the I3C
+ * specification:
+ *
+ * 1. Attach I2C devs to the master so that the master can fill its
+ * internal device table appropriately
+ *
+ * 2. Call &i3c_master_controller_ops->bus_init() method to initialize
+ * the master controller. That's usually where the bus mode is selected
+ * (pure bus or mixed fast/slow bus)
+ *
+ * 3. Instruct all devices on the bus to drop their dynamic address. This
+ * is particularly important when the bus was previously configured by
+ * someone else (for example the bootloader)
+ *
+ * 4. Disable all slave events.
+ *
+ * 5. Reserve address slots for I3C devices with init_dyn_addr. And if
+ * devices also have static_addr, try to pre-assign dynamic addresses
+ * requested by the FW with SETDASA and attach corresponding statically
+ * defined I3C devices to the master.
+ *
+ * 6. Do a DAA (Dynamic Address Assignment) to assign dynamic addresses to
+ * all remaining I3C devices
+ *
+ * Once this is done, all I3C and I2C devices should be usable.
+ *
+ * Input Parameters:
+ * master - main master initializing the bus
+ *
+ * Returned Value:
+ * A 0 in case of success, an negative error code otherwise.
+ ****************************************************************************/
+
+static int i3c_master_bus_init(FAR struct i3c_master_controller *master)
+{
+ int ret;
+
+ /* Now execute the controller specific ->bus_init() routine, which
+ * might configure its internal logic to match the bus limitations.
+ */
+
+ ret = master->ops->bus_init(master);
+ if (ret)
+ {
+ goto err_detach_devs;
+ }
+
+ /* The master device should have been instantiated in ->bus_init(),
+ * complain if this was not the case.
+ */
+
+ if (master->this == NULL)
+ {
+ i3cerr("master_set_info() was not called in ->bus_init()\n");
+ ret = -EINVAL;
+ goto err_bus_cleanup;
+ }
+
+ /* Reset all dynamic address that may have been assigned before
+ * (assigned by the bootloader for example).
+ */
+
+ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_rstdaa_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Disable all slave events before starting DAA. */
+
+ ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
+ I3C_CCC_EVENT_HJ);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_disec_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Reserve init_dyn_addr first, and then try to pre-assign dynamic
+ * address and retrieve device information if needed.
+ * In case pre-assign dynamic address fails, setting dynamic address to
+ * the requested init_dyn_addr is retried after DAA is done in
+ * i3c_master_add_i3c_dev_locked().
+ */
+
+ ret = i3c_master_do_daa(master);
+ if (ret)
+ {
+ i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ i3cerr("master_do_daa failed:%d (no device)\n", ret);
+ }
+
+ return 0;
+
+err_bus_cleanup:
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+err_detach_devs:
+ i3c_master_detach_free_devs(master);
+
+ return ret;
+}
+
+static void i3c_master_bus_cleanup(FAR struct i3c_master_controller *master)
+{
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+ i3c_master_detach_free_devs(master);
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_search_i3c_dev_duplicate(FAR struct i3c_dev_desc *refdev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(refdev);
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev != refdev && i3cdev->info.pid == refdev->info.pid)
+ {
+ return i3cdev;
+ }
+ }
+
+ return NULL;
+}
+
+static int i3c_master_i2c_adapter_xfer(FAR struct i2c_master_s *i2c,
+ FAR struct i2c_msg_s *xfers, int nxfers)
+{
+ FAR struct i3c_master_controller *master = i2c_adapter_to_i3c_master(i2c);
+ int i;
+ int ret;
+ uint16_t addr;
+
+ if (xfers == NULL || master == NULL || nxfers <= 0)
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->i2c_xfers == NULL)
+ {
+ return -ENOTSUP;
+ }
+
+ /* Doing transfers to different devices is not supported. */
+
+ addr = xfers[0].addr;
+ for (i = 1; i < nxfers; i++)
+ {
+ if (addr != xfers[i].addr)
+ {
+ return -ENOTSUP;
+ }
+ }
+
+ i3c_bus_normaluse_lock(&master->bus);
+ ret = master->ops->i2c_xfers(master, xfers, nxfers);
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ return ret ? ret : nxfers;
+}
+
+static struct i2c_ops_s i3c_master_i2c_algo =
+{
+ .transfer = i3c_master_i2c_adapter_xfer,
+};
+
+static void i3c_master_unregister_i3c_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev->dev == NULL)
+ {
+ continue;
+ }
+
+ i3cdev->dev->desc = NULL;
+ i3cdev->dev->bus = NULL;
+ kmm_free(i3cdev->dev);
+ i3cdev->dev = NULL;
+ }
+}
+
+static void i3c_master_handle_ibi(FAR void *arg)
+{
+ FAR struct i3c_ibi_slot *slot = (struct i3c_ibi_slot *)arg;
+ FAR struct i3c_dev_desc *dev = slot->dev;
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ struct i3c_ibi_payload payload;
+
+ payload.data = slot->data;
+ payload.len = slot->len;
+
+ if (dev->dev)
+ {
+ dev->ibi->handler(dev->dev, &payload);
+ }
+
+ master->ops->recycle_ibi_slot(dev, slot);
+ atomic_fetch_sub(&dev->ibi->pending_ibis, 1);
+ if (!atomic_load(&dev->ibi->pending_ibis))
+ {
+ sem_post(&dev->ibi->all_ibis_handled);
+ }
+}
+
+static void i3c_master_init_ibi_slot(FAR struct i3c_dev_desc *dev,
+ FAR struct i3c_ibi_slot *slot)
+{
+ slot->dev = dev;
+}
+
+static int i3c_master_check_ops(
+ FAR const struct i3c_master_controller_ops *ops)
+{
+ if (ops == NULL || ops->bus_init == NULL || ops->priv_xfers == NULL ||
+ ops->send_ccc_cmd == NULL || ops->do_daa == NULL ||
+ ops->i2c_xfers == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (ops->request_ibi &&
+ (ops->enable_ibi == NULL || ops->disable_ibi == NULL ||
+ ops->free_ibi == NULL || ops->recycle_ibi_slot == NULL))
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i2c_unregister_driver()
+ *
+ * Description:
+ * Perform an I2C driver unregister operation.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ *
+ ****************************************************************************/
+
+static void i2c_unregister_driver(FAR struct i3c_master_controller *master)
+{
+ char devname[12];
+ int i2c_bus_id = i3c_master_to_i2c_bus_number(master);
+
+ snprintf(devname, 12, "/dev/i2c%d", i2c_bus_id);
+ nx_unlink(devname);
+}
+
+/****************************************************************************
+ * Name: i3c_unregister_driver()
+ *
+ * Description:
+ * Perform an I3C driver unregister operation.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ *
+ ****************************************************************************/
+
+static void i3c_unregister_driver(FAR struct i3c_master_controller *master)
+{
+ char devname[12];
+
+ snprintf(devname, 12, "/dev/i3c%d", i3c_master_to_i3c_bus_number(master));
+ nx_unlink(devname);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_master_send_ccc_cmd_locked
+ ****************************************************************************/
+
+int i3c_master_send_ccc_cmd_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_ccc_cmd *cmd)
+{
+ int ret;
+
+ if (cmd == NULL || master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (master->init_done &&
+ !nxmutex_is_locked(&master->bus.lock))
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->send_ccc_cmd == NULL)
+ {
+ return -ENOTSUP;
+ }
+
+ if ((cmd->id & I3C_CCC_DIRECT) && (cmd->dests == NULL || !cmd->ndests))
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->supports_ccc_cmd &&
+ !master->ops->supports_ccc_cmd(master, cmd))
+ {
+ return -ENOTSUP;
+ }
+
+ ret = master->ops->send_ccc_cmd(master, cmd);
+ if (ret)
+ {
+ if (cmd->err != I3C_ERROR_UNKNOWN)
+ {
+ return cmd->err;
+ }
+
+ return ret;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_normaluse_lock
+ *
+ * Description:
+ * I3C bus to take the lock on.
+ *
+ * This function takes the bus lock for any operation that is not a
+ * maintenance operation (see i3c_bus_maintenance_lock() for
+ * non-exhaustive list of maintenance operations). Basically all
+ * communications with I3C devices are normal operations (HDR, SDR
+ * transfers or CCC commands that do not change bus state or I3C dynamic
+ * address).
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+void i3c_bus_normaluse_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_normaluse_unlock
+ *
+ * Description:
+ * Release the bus lock after a normal operation.
+ *
+ * Should be called when a normal operation is done. See
+ * i3c_bus_normaluse_lock() for more details on what these
+ * normal operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ *
+ ****************************************************************************/
+
+void i3c_bus_normaluse_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_master_get_free_addr()
Review Comment:
Done~
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] drivers/i3c: support i3c driver frameworks [nuttx]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #13507:
URL: https://github.com/apache/nuttx/pull/13507#issuecomment-2368173399
This PR exceeds the recommended size of 1000 lines. Please make sure you are NOT addressing multiple issues with one PR. Note this PR might be rejected due to its size.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] drivers/i3c: support i3c driver frameworks [nuttx]
Posted by "pkarashchenko (via GitHub)" <gi...@apache.org>.
pkarashchenko commented on code in PR #13507:
URL: https://github.com/apache/nuttx/pull/13507#discussion_r1765931637
##########
drivers/i3c/device.c:
##########
@@ -0,0 +1,346 @@
+/****************************************************************************
+ * drivers/i3c/device.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "internals.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_device_do_priv_xfers()
+ *
+ * Description:
+ * Do I3C SDR private transfers directed to a specific device.
+ *
+ * Initiate one or several private SDR transfers with @dev.
+ *
+ * This function can sleep and thus cannot be called in atomic context.
+ *
+ * Input Parameters:
+ * dev - Device with which the transfers should be done
+ * xfers - Array of transfers
+ * nxfers - Number of transfers
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_do_priv_xfers(FAR const struct i3c_device *dev,
+ FAR struct i3c_priv_xfer *xfers,
+ int nxfers)
+{
+ int ret;
+ int i;
+
+ if (nxfers < 1)
+ {
+ return 0;
+ }
+
+ for (i = 0; i < nxfers; i++)
+ {
+ if (!xfers[i].len || !xfers[i].data.in)
+ {
+ return -EINVAL;
+ }
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers);
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_get_info()
+ * Get I3C device information, Retrieve I3C dev info.
+ *
+ * Input Parameters:
+ * dev - Device we want information on.
+ * info - The information object to fill in.
+ *
+ ****************************************************************************/
+
+void i3c_device_get_info(FAR const struct i3c_device *dev,
+ FAR struct i3c_device_info *info)
+{
+ if (info == NULL)
+ {
+ return;
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ *info = dev->desc->info;
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+}
+
+/****************************************************************************
+ * Name: i3c_device_disable_ibi()
Review Comment:
```suggestion
* Name: i3c_device_disable_ibi
```
##########
drivers/i3c/device.c:
##########
@@ -0,0 +1,346 @@
+/****************************************************************************
+ * drivers/i3c/device.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "internals.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_device_do_priv_xfers()
+ *
+ * Description:
+ * Do I3C SDR private transfers directed to a specific device.
+ *
+ * Initiate one or several private SDR transfers with @dev.
+ *
+ * This function can sleep and thus cannot be called in atomic context.
+ *
+ * Input Parameters:
+ * dev - Device with which the transfers should be done
+ * xfers - Array of transfers
+ * nxfers - Number of transfers
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_do_priv_xfers(FAR const struct i3c_device *dev,
+ FAR struct i3c_priv_xfer *xfers,
+ int nxfers)
+{
+ int ret;
+ int i;
+
+ if (nxfers < 1)
+ {
+ return 0;
+ }
+
+ for (i = 0; i < nxfers; i++)
+ {
+ if (!xfers[i].len || !xfers[i].data.in)
+ {
+ return -EINVAL;
+ }
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers);
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_get_info()
Review Comment:
```suggestion
* Name: i3c_device_get_info
```
##########
drivers/i3c/device.c:
##########
@@ -0,0 +1,346 @@
+/****************************************************************************
+ * drivers/i3c/device.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "internals.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_device_do_priv_xfers()
+ *
+ * Description:
+ * Do I3C SDR private transfers directed to a specific device.
+ *
+ * Initiate one or several private SDR transfers with @dev.
+ *
+ * This function can sleep and thus cannot be called in atomic context.
+ *
+ * Input Parameters:
+ * dev - Device with which the transfers should be done
+ * xfers - Array of transfers
+ * nxfers - Number of transfers
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_do_priv_xfers(FAR const struct i3c_device *dev,
+ FAR struct i3c_priv_xfer *xfers,
+ int nxfers)
+{
+ int ret;
+ int i;
+
+ if (nxfers < 1)
+ {
+ return 0;
+ }
+
+ for (i = 0; i < nxfers; i++)
+ {
+ if (!xfers[i].len || !xfers[i].data.in)
+ {
+ return -EINVAL;
+ }
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers);
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_get_info()
+ * Get I3C device information, Retrieve I3C dev info.
+ *
+ * Input Parameters:
+ * dev - Device we want information on.
+ * info - The information object to fill in.
+ *
+ ****************************************************************************/
+
+void i3c_device_get_info(FAR const struct i3c_device *dev,
+ FAR struct i3c_device_info *info)
+{
+ if (info == NULL)
+ {
+ return;
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ *info = dev->desc->info;
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+}
+
+/****************************************************************************
+ * Name: i3c_device_disable_ibi()
+ *
+ * Description:
+ * Disable IBIs coming from a specific device.
+ *
+ * This function disable IBIs coming from a specific device and wait for
+ * all pending IBIs to be processed.
+ *
+ * Input Parameters:
+ * dev - Device on which IBIs should be disabled
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_disable_ibi(FAR const struct i3c_device *dev)
+{
+ int ret = -ENOENT;
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ ret = i3c_dev_disable_ibi_locked(dev->desc);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_enable_ibi()
+ *
+ * Description:
+ * Enable IBIs coming from a specific device.
+ *
+ * This function enable IBIs coming from a specific device and wait for
+ * all pending IBIs to be processed. This should be called on a device
+ * where i3c_device_request_ibi() has succeeded.
+ *
+ * Note that IBIs from this device might be received before this function
+ * returns to its caller.
+ *
+ * Input Parameters:
+ * dev - Device on which IBIs should be enabled
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_enable_ibi(FAR const struct i3c_device *dev)
+{
+ int ret = -ENOENT;
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ ret = i3c_dev_enable_ibi_locked(dev->desc);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_request_ibi()
+ *
+ * Description:
+ * Request an IBI.
+ *
+ * This function is responsible for pre-allocating all resources needed to
+ * process IBIs coming from @dev. When this function returns, the IBI is
+ * not enabled until i3c_device_enable_ibi() is called.
+ *
+ * Input Parameters:
+ * dev - Device for which we should enable IBIs
+ * req - Setup requested for this IBI
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_request_ibi(FAR const struct i3c_device *dev,
+ FAR const struct i3c_ibi_setup *req)
+{
+ int ret = -ENOENT;
+
+ if (req->handler == NULL || !req->num_slots)
+ {
+ return -EINVAL;
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ ret = i3c_dev_request_ibi_locked(dev->desc, req);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_free_ibi()
+ *
+ * Description:
+ * Free all resources needed for IBI handling.
+ *
+ * This function is responsible for de-allocating resources previously
+ * allocated by i3c_device_request_ibi(). It should be called after
+ * disabling IBIs with i3c_device_disable_ibi().
+ *
+ * Input Parameters:
+ * dev - Device on which you want to release IBI resources
+ *
+ ****************************************************************************/
+
+void i3c_device_free_ibi(FAR const struct i3c_device *dev)
+{
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ i3c_dev_free_ibi_locked(dev->desc);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+}
+
+/****************************************************************************
+ * Name: i3c_device_send_ccc_cmd()
Review Comment:
```suggestion
* Name: i3c_device_send_ccc_cmd
```
##########
drivers/i3c/master.c:
##########
@@ -0,0 +1,2282 @@
+/****************************************************************************
+ * drivers/i3c/master.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <stdio.h>
+#include <debug.h>
+
+#include <nuttx/list.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/nuttx.h>
+#include <nuttx/mutex.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mm/mm.h>
+#include <nuttx/spinlock.h>
+#include <netinet/in.h>
+
+#include <nuttx/i3c/master.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/i3c/i3c_driver.h>
+#include <nuttx/i3c/device.h>
+#include "internals.h"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_lock
+ *
+ * Description:
+ * Lock the bus for a maintenance operation
+ *
+ * This function takes the bus lock so that no other operations can
+ * occur on the bus. This is needed for all kind of bus maintenance
+ * operation, like
+ * - enabling/disabling slave events
+ * - re-triggering DAA
+ * - changing the dynamic address of a device
+ * - relinquishing mastership
+ *
+ * The reason for this kind of locking is that we don't want drivers
+ * and core logic to rely on I3C device information that could be
+ * changed behind their back.
+ *
+ * Input Parameters:
+ * bus - I3C bus to take the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_unlock
+ *
+ * Description:
+ * Release the bus lock after a maintenance operation.
+ *
+ * Should be called when the bus maintenance operation is done.
+ * See i3c_bus_maintenance_lock() for more details on what these
+ * maintenance operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_to_i3c_master
+ ****************************************************************************/
+
+static FAR struct i3c_master_controller *
+i3c_bus_to_i3c_master(FAR struct i3c_bus *i3cbus)
+{
+ return container_of(i3cbus, struct i3c_master_controller, bus);
+}
+
+static FAR struct i3c_master_controller *
+i2c_adapter_to_i3c_master(FAR struct i2c_master_s *i2c_master)
+{
+ return container_of(i2c_master, struct i3c_master_controller, i2c);
+}
+
+static int i3c_master_to_i2c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i2c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_addr_slot_status
+ ****************************************************************************/
+
+static enum i3c_addr_slot_status
+i3c_bus_get_addr_slot_status(FAR struct i3c_bus *bus, uint16_t addr)
+{
+ int32_t status;
+ int bitpos = addr * 2;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return I3C_ADDR_SLOT_RSVD;
+ }
+
+ status = bus->addrslots[bitpos / I3C_BITS_PER_LONG];
+ status >>= bitpos % I3C_BITS_PER_LONG;
+
+ return status & I3C_ADDR_SLOT_STATUS_MASK;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_addr_slot_status
+ ****************************************************************************/
+
+static void i3c_bus_set_addr_slot_status(FAR struct i3c_bus *bus,
+ uint16_t addr, enum i3c_addr_slot_status status)
+{
+ int bitpos = addr * 2;
+ FAR uint32_t *ptr;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return;
+ }
+
+ ptr = bus->addrslots + (bitpos / I3C_BITS_PER_LONG);
+ *ptr &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK <<
+ (bitpos % I3C_BITS_PER_LONG));
+ *ptr |= (unsigned long)status << (bitpos % I3C_BITS_PER_LONG);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_dev_addr_is_avail
+ ****************************************************************************/
+
+static bool i3c_bus_dev_addr_is_avail(FAR struct i3c_bus *bus,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status status;
+
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ return status == I3C_ADDR_SLOT_FREE;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_free_addr
+ ****************************************************************************/
+
+static int i3c_bus_get_free_addr(FAR struct i3c_bus *bus,
+ unsigned char start_addr)
+{
+ enum i3c_addr_slot_status status;
+ unsigned char addr;
+
+ for (addr = start_addr; addr < I3C_MAX_ADDR; addr++)
+ {
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ if (status == I3C_ADDR_SLOT_FREE)
+ {
+ return addr;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init_addrslots
+ ****************************************************************************/
+
+static void i3c_bus_init_addrslots(FAR struct i3c_bus *bus)
+{
+ int i;
+
+ /* Addresses 0 to 7 are reserved. */
+
+ for (i = 0; i < 8; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, i, I3C_ADDR_SLOT_RSVD);
+ }
+
+ /* Reserve broadcast address and all addresses that might collide
+ * with the broadcast address when facing a single bit error.
+ */
+
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR,
+ I3C_ADDR_SLOT_RSVD);
+ for (i = 0; i < 7; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR ^ I3C_BIT(i),
+ I3C_ADDR_SLOT_RSVD);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init
+ ****************************************************************************/
+
+static void i3c_bus_init(FAR struct i3c_bus *i3cbus)
+{
+ FAR struct i3c_master_controller *master;
+ master = i3c_bus_to_i3c_master(i3cbus);
+
+ nxmutex_init(&i3cbus->lock);
+ list_initialize(&i3cbus->devs.i3c);
+ i3c_bus_init_addrslots(i3cbus);
+ i3cbus->mode = I3C_BUS_MODE_PURE;
+ i3cbus->id = master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_mode
+ ****************************************************************************/
+
+static int i3c_bus_set_mode(FAR struct i3c_bus *i3cbus,
+ enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate)
+{
+ i3cbus->mode = mode;
+
+ switch (i3cbus->mode)
+ {
+ case I3C_BUS_MODE_PURE:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_FAST:
+ case I3C_BUS_MODE_MIXED_LIMITED:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_SLOW:
+ {
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+
+ if (!i3cbus->scl_rate.i3c ||
+ i3cbus->scl_rate.i3c > i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i3c = i3cbus->scl_rate.i2c;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* I3C/I2C frequency may have been overridden, check that user-provided
+ * values are not exceeding max possible frequency.
+ */
+
+ if (i3cbus->scl_rate.i3c > I3C_BUS_MAX_I3C_SCL_RATE ||
+ i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_RATE)
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int i3c_master_to_i3c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_init
+ ****************************************************************************/
+
+static FAR void *i3c_ccc_cmd_dest_init(FAR struct i3c_ccc_cmd_dest *dest,
+ unsigned char addr, uint16_t payloadlen)
+{
+ dest->addr = addr;
+ dest->payload.len = payloadlen;
+ if (payloadlen)
+ {
+ dest->payload.data = kmm_zalloc(payloadlen);
+ }
+ else
+ {
+ dest->payload.data = NULL;
+ }
+
+ return dest->payload.data;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_cleanup
+ ****************************************************************************/
+
+static void i3c_ccc_cmd_dest_cleanup(FAR struct i3c_ccc_cmd_dest *dest)
+{
+ kmm_free(dest->payload.data);
+}
+
+static void i3c_ccc_cmd_init(FAR struct i3c_ccc_cmd *cmd, bool rnw,
+ unsigned char id, FAR struct i3c_ccc_cmd_dest *dests,
+ unsigned int ndests)
+{
+ cmd->rnw = rnw ? 1 : 0;
+ cmd->id = id;
+ cmd->dests = dests;
+ cmd->ndests = ndests;
+ cmd->err = I3C_ERROR_UNKNOWN;
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_alloc_i3c_dev(FAR struct i3c_master_controller *master,
+ FAR const struct i3c_device_info *info)
+{
+ FAR struct i3c_dev_desc *dev;
+
+ dev = kmm_zalloc(sizeof(*dev));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ dev->common.master = master;
+ dev->info = *info;
+ nxmutex_init(&dev->ibi_lock);
+
+ return dev;
+}
+
+static int i3c_master_rstdaa_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status addrstat;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ addrstat = i3c_bus_get_addr_slot_status(&master->bus, addr);
+ if (addr != I3C_BROADCAST_ADDR && addrstat != I3C_ADDR_SLOT_I3C_DEV)
+ {
+ return -EINVAL;
+ }
+
+ i3c_ccc_cmd_dest_init(&dest, addr, 0);
+ i3c_ccc_cmd_init(&cmd, false,
+ I3C_CCC_RSTDAA(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_enec_disec_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char addr, bool enable, unsigned char evts)
+{
+ FAR struct i3c_ccc_events *events;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ events = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*events));
+ if (events == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ events->events = evts;
+ i3c_ccc_cmd_init(&cmd, false,
+ enable ?
+ I3C_CCC_ENEC(addr == I3C_BROADCAST_ADDR) :
+ I3C_CCC_DISEC(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setda_locked(FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr, bool setdasa)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_setda *setda;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (!oldaddr || !newaddr)
+ {
+ return -EINVAL;
+ }
+
+ setda = i3c_ccc_cmd_dest_init(&dest, oldaddr, sizeof(*setda));
+ if (setda == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ setda->addr = newaddr << 1;
+ i3c_ccc_cmd_init(&cmd, false,
+ setdasa ? I3C_CCC_SETDASA : I3C_CCC_SETNEWDA,
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setnewda_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr)
+{
+ return i3c_master_setda_locked(master, oldaddr, newaddr, false);
+}
+
+static int i3c_master_getmrl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mrl *mrl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mrl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mrl));
+ if (mrl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ /* When the device does not have IBI payload GETMRL only returns 2
+ * bytes of data.
+ */
+
+ if (!(info->bcr & I3C_BCR_IBI_PAYLOAD))
+ {
+ dest.payload.len -= 1;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMRL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ switch (dest.payload.len)
+ {
+ case 3:
+ {
+ info->max_ibi_len = mrl->ibi_len;
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ case 2:
+ {
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ default:
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmwl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mwl *mwl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mwl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mwl));
+ if (mwl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMWL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != sizeof(*mwl))
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_write_len = ntohs(mwl->len);
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmxds_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getmxds *getmaxds;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getmaxds = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*getmaxds));
+ if (getmaxds == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 2 && dest.payload.len != 5)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_read_ds = getmaxds->maxrd;
+ info->max_write_ds = getmaxds->maxwr;
+ if (dest.payload.len == 5)
+ {
+ info->max_read_turnaround = getmaxds->maxrdturn[0] |
+ ((uint32_t)getmaxds->maxrdturn[1] << 8) |
+ ((uint32_t)getmaxds->maxrdturn[2] << 16);
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_gethdrcap_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_gethdrcap *gethdrcap;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ gethdrcap = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*gethdrcap));
+ if (gethdrcap == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETHDRCAP, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 1)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->hdr_cap = gethdrcap->modes;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getpid_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getpid *getpid;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+ int i;
+
+ getpid = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getpid));
+ if (getpid == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETPID, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->pid = 0;
+ for (i = 0; i < sizeof(getpid->pid); i++)
+ {
+ int sft = (sizeof(getpid->pid) - i - 1) * 8;
+ info->pid |= (uint64_t)getpid->pid[i] << sft;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getbcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getbcr *getbcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getbcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getbcr));
+ if (getbcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETBCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->bcr = getbcr->bcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getdcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getdcr *getdcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getdcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getdcr));
+ if (getdcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETDCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->dcr = getdcr->dcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_retrieve_dev_info(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status slot_status;
+ int ret;
+
+ if (!dev->info.dyn_addr)
+ {
+ return -EINVAL;
+ }
+
+ slot_status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (slot_status == I3C_ADDR_SLOT_RSVD)
+ {
+ return -EINVAL;
+ }
+
+ ret = i3c_master_getpid_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getbcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getdcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ if (dev->info.bcr & I3C_BCR_MAX_DATA_SPEED_LIM)
+ {
+ ret = i3c_master_getmxds_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
+ {
+ dev->info.max_ibi_len = 1;
+ }
+
+ i3c_master_getmrl_locked(master, &dev->info);
+ i3c_master_getmwl_locked(master, &dev->info);
+
+ if (dev->info.bcr & I3C_BCR_HDR_CAP)
+ {
+ ret = i3c_master_gethdrcap_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_put_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ if (dev->info.dyn_addr)
+ {
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_FREE);
+ }
+}
+
+static int i3c_master_get_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ /* ->init_dyn_addr should have been reserved before that, so, if we're
+ * trying to apply a pre-reserved dynamic address, we should not try
+ * to reserve the address slot a second time.
+ */
+
+ if (dev->info.dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ return 0;
+}
+
+static int i3c_master_attach_i3c_dev(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_dev_desc *dev)
+{
+ int ret;
+
+ /* We don't attach devices to the controller until they are
+ * addressable on the bus.
+ */
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ ret = i3c_master_get_i3c_addrs(dev);
+ if (ret)
+ {
+ return ret;
+ }
+
+ /* Do not attach the master device itself. */
+
+ if (master->this != dev && master->ops->attach_i3c_dev)
+ {
+ ret = master->ops->attach_i3c_dev(dev);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ list_add_tail(&master->bus.devs.i3c, &dev->common.node);
+
+ return 0;
+}
+
+static int i3c_master_reattach_i3c_dev(FAR struct i3c_dev_desc *dev,
+ unsigned char old_dyn_addr)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+ int ret;
+
+ if (dev->info.dyn_addr != old_dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus,
+ dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ if (master->ops->reattach_i3c_dev)
+ {
+ ret = master->ops->reattach_i3c_dev(dev, old_dyn_addr);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_detach_i3c_dev(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ /* Do not detach the master device itself. */
+
+ if (master->this != dev && master->ops->detach_i3c_dev)
+ {
+ master->ops->detach_i3c_dev(dev);
+ }
+
+ i3c_master_put_i3c_addrs(dev);
+ list_delete(&dev->common.node);
+}
+
+static void
+i3c_master_register_new_i3c_devs(FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *desc;
+
+ if (!master->init_done)
+ {
+ return;
+ }
+
+ i3c_bus_for_each_i3cdev(&master->bus, desc)
+ {
+ if (desc->dev || !desc->info.dyn_addr || desc == master->this)
+ {
+ continue;
+ }
+
+ desc->dev = kmm_zalloc(sizeof(*desc->dev));
+ if (desc->dev == NULL)
+ {
+ continue;
+ }
+
+ desc->dev->bus = &master->bus;
+ desc->dev->desc = desc;
+ }
+}
+
+static void i3c_master_detach_free_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+ FAR struct i3c_dev_desc *i3ctmp;
+
+ list_for_every_entry_safe(&master->bus.devs.i3c, i3cdev, i3ctmp,
+ struct i3c_dev_desc, common.node)
+ {
+ i3c_master_detach_i3c_dev(i3cdev);
+ kmm_free(i3cdev);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_master_bus_init()
Review Comment:
```suggestion
* Name: i3c_master_bus_init
```
##########
drivers/i3c/device.c:
##########
@@ -0,0 +1,346 @@
+/****************************************************************************
+ * drivers/i3c/device.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "internals.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_device_do_priv_xfers()
+ *
+ * Description:
+ * Do I3C SDR private transfers directed to a specific device.
+ *
+ * Initiate one or several private SDR transfers with @dev.
+ *
+ * This function can sleep and thus cannot be called in atomic context.
+ *
+ * Input Parameters:
+ * dev - Device with which the transfers should be done
+ * xfers - Array of transfers
+ * nxfers - Number of transfers
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_do_priv_xfers(FAR const struct i3c_device *dev,
+ FAR struct i3c_priv_xfer *xfers,
+ int nxfers)
+{
+ int ret;
+ int i;
+
+ if (nxfers < 1)
+ {
+ return 0;
+ }
+
+ for (i = 0; i < nxfers; i++)
+ {
+ if (!xfers[i].len || !xfers[i].data.in)
+ {
+ return -EINVAL;
+ }
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers);
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_get_info()
+ * Get I3C device information, Retrieve I3C dev info.
+ *
+ * Input Parameters:
+ * dev - Device we want information on.
+ * info - The information object to fill in.
+ *
+ ****************************************************************************/
+
+void i3c_device_get_info(FAR const struct i3c_device *dev,
+ FAR struct i3c_device_info *info)
+{
+ if (info == NULL)
+ {
+ return;
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ *info = dev->desc->info;
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+}
+
+/****************************************************************************
+ * Name: i3c_device_disable_ibi()
+ *
+ * Description:
+ * Disable IBIs coming from a specific device.
+ *
+ * This function disable IBIs coming from a specific device and wait for
+ * all pending IBIs to be processed.
+ *
+ * Input Parameters:
+ * dev - Device on which IBIs should be disabled
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_disable_ibi(FAR const struct i3c_device *dev)
+{
+ int ret = -ENOENT;
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ ret = i3c_dev_disable_ibi_locked(dev->desc);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_enable_ibi()
+ *
+ * Description:
+ * Enable IBIs coming from a specific device.
+ *
+ * This function enable IBIs coming from a specific device and wait for
+ * all pending IBIs to be processed. This should be called on a device
+ * where i3c_device_request_ibi() has succeeded.
+ *
+ * Note that IBIs from this device might be received before this function
+ * returns to its caller.
+ *
+ * Input Parameters:
+ * dev - Device on which IBIs should be enabled
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_enable_ibi(FAR const struct i3c_device *dev)
+{
+ int ret = -ENOENT;
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ ret = i3c_dev_enable_ibi_locked(dev->desc);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_request_ibi()
Review Comment:
```suggestion
* Name: i3c_device_request_ibi
```
##########
drivers/i3c/device.c:
##########
@@ -0,0 +1,346 @@
+/****************************************************************************
+ * drivers/i3c/device.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "internals.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_device_do_priv_xfers()
Review Comment:
```suggestion
* Name: i3c_device_do_priv_xfers
```
##########
drivers/i3c/device.c:
##########
@@ -0,0 +1,346 @@
+/****************************************************************************
+ * drivers/i3c/device.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "internals.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_device_do_priv_xfers()
+ *
+ * Description:
+ * Do I3C SDR private transfers directed to a specific device.
+ *
+ * Initiate one or several private SDR transfers with @dev.
+ *
+ * This function can sleep and thus cannot be called in atomic context.
+ *
+ * Input Parameters:
+ * dev - Device with which the transfers should be done
+ * xfers - Array of transfers
+ * nxfers - Number of transfers
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_do_priv_xfers(FAR const struct i3c_device *dev,
+ FAR struct i3c_priv_xfer *xfers,
+ int nxfers)
+{
+ int ret;
+ int i;
+
+ if (nxfers < 1)
+ {
+ return 0;
+ }
+
+ for (i = 0; i < nxfers; i++)
+ {
+ if (!xfers[i].len || !xfers[i].data.in)
+ {
+ return -EINVAL;
+ }
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers);
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_get_info()
+ * Get I3C device information, Retrieve I3C dev info.
+ *
+ * Input Parameters:
+ * dev - Device we want information on.
+ * info - The information object to fill in.
+ *
+ ****************************************************************************/
+
+void i3c_device_get_info(FAR const struct i3c_device *dev,
+ FAR struct i3c_device_info *info)
+{
+ if (info == NULL)
+ {
+ return;
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ *info = dev->desc->info;
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+}
+
+/****************************************************************************
+ * Name: i3c_device_disable_ibi()
+ *
+ * Description:
+ * Disable IBIs coming from a specific device.
+ *
+ * This function disable IBIs coming from a specific device and wait for
+ * all pending IBIs to be processed.
+ *
+ * Input Parameters:
+ * dev - Device on which IBIs should be disabled
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_disable_ibi(FAR const struct i3c_device *dev)
+{
+ int ret = -ENOENT;
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ ret = i3c_dev_disable_ibi_locked(dev->desc);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_enable_ibi()
+ *
+ * Description:
+ * Enable IBIs coming from a specific device.
+ *
+ * This function enable IBIs coming from a specific device and wait for
+ * all pending IBIs to be processed. This should be called on a device
+ * where i3c_device_request_ibi() has succeeded.
+ *
+ * Note that IBIs from this device might be received before this function
+ * returns to its caller.
+ *
+ * Input Parameters:
+ * dev - Device on which IBIs should be enabled
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_enable_ibi(FAR const struct i3c_device *dev)
+{
+ int ret = -ENOENT;
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ ret = i3c_dev_enable_ibi_locked(dev->desc);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_request_ibi()
+ *
+ * Description:
+ * Request an IBI.
+ *
+ * This function is responsible for pre-allocating all resources needed to
+ * process IBIs coming from @dev. When this function returns, the IBI is
+ * not enabled until i3c_device_enable_ibi() is called.
+ *
+ * Input Parameters:
+ * dev - Device for which we should enable IBIs
+ * req - Setup requested for this IBI
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_request_ibi(FAR const struct i3c_device *dev,
+ FAR const struct i3c_ibi_setup *req)
+{
+ int ret = -ENOENT;
+
+ if (req->handler == NULL || !req->num_slots)
+ {
+ return -EINVAL;
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ ret = i3c_dev_request_ibi_locked(dev->desc, req);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_free_ibi()
Review Comment:
```suggestion
* Name: i3c_device_free_ibi
```
##########
drivers/i3c/device.c:
##########
@@ -0,0 +1,346 @@
+/****************************************************************************
+ * drivers/i3c/device.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "internals.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_device_do_priv_xfers()
+ *
+ * Description:
+ * Do I3C SDR private transfers directed to a specific device.
+ *
+ * Initiate one or several private SDR transfers with @dev.
+ *
+ * This function can sleep and thus cannot be called in atomic context.
+ *
+ * Input Parameters:
+ * dev - Device with which the transfers should be done
+ * xfers - Array of transfers
+ * nxfers - Number of transfers
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_do_priv_xfers(FAR const struct i3c_device *dev,
+ FAR struct i3c_priv_xfer *xfers,
+ int nxfers)
+{
+ int ret;
+ int i;
+
+ if (nxfers < 1)
+ {
+ return 0;
+ }
+
+ for (i = 0; i < nxfers; i++)
+ {
+ if (!xfers[i].len || !xfers[i].data.in)
+ {
+ return -EINVAL;
+ }
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers);
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_get_info()
+ * Get I3C device information, Retrieve I3C dev info.
+ *
+ * Input Parameters:
+ * dev - Device we want information on.
+ * info - The information object to fill in.
+ *
+ ****************************************************************************/
+
+void i3c_device_get_info(FAR const struct i3c_device *dev,
+ FAR struct i3c_device_info *info)
+{
+ if (info == NULL)
+ {
+ return;
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ *info = dev->desc->info;
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+}
+
+/****************************************************************************
+ * Name: i3c_device_disable_ibi()
+ *
+ * Description:
+ * Disable IBIs coming from a specific device.
+ *
+ * This function disable IBIs coming from a specific device and wait for
+ * all pending IBIs to be processed.
+ *
+ * Input Parameters:
+ * dev - Device on which IBIs should be disabled
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_disable_ibi(FAR const struct i3c_device *dev)
+{
+ int ret = -ENOENT;
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ ret = i3c_dev_disable_ibi_locked(dev->desc);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_enable_ibi()
+ *
+ * Description:
+ * Enable IBIs coming from a specific device.
+ *
+ * This function enable IBIs coming from a specific device and wait for
+ * all pending IBIs to be processed. This should be called on a device
+ * where i3c_device_request_ibi() has succeeded.
+ *
+ * Note that IBIs from this device might be received before this function
+ * returns to its caller.
+ *
+ * Input Parameters:
+ * dev - Device on which IBIs should be enabled
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_enable_ibi(FAR const struct i3c_device *dev)
+{
+ int ret = -ENOENT;
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ ret = i3c_dev_enable_ibi_locked(dev->desc);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_request_ibi()
+ *
+ * Description:
+ * Request an IBI.
+ *
+ * This function is responsible for pre-allocating all resources needed to
+ * process IBIs coming from @dev. When this function returns, the IBI is
+ * not enabled until i3c_device_enable_ibi() is called.
+ *
+ * Input Parameters:
+ * dev - Device for which we should enable IBIs
+ * req - Setup requested for this IBI
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_request_ibi(FAR const struct i3c_device *dev,
+ FAR const struct i3c_ibi_setup *req)
+{
+ int ret = -ENOENT;
+
+ if (req->handler == NULL || !req->num_slots)
+ {
+ return -EINVAL;
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ ret = i3c_dev_request_ibi_locked(dev->desc, req);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_free_ibi()
+ *
+ * Description:
+ * Free all resources needed for IBI handling.
+ *
+ * This function is responsible for de-allocating resources previously
+ * allocated by i3c_device_request_ibi(). It should be called after
+ * disabling IBIs with i3c_device_disable_ibi().
+ *
+ * Input Parameters:
+ * dev - Device on which you want to release IBI resources
+ *
+ ****************************************************************************/
+
+void i3c_device_free_ibi(FAR const struct i3c_device *dev)
+{
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ i3c_dev_free_ibi_locked(dev->desc);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+}
+
+/****************************************************************************
+ * Name: i3c_device_send_ccc_cmd()
+ *
+ * Description:
+ * This function is used to send a common ccc command.
+ *
+ * Input Parameters:
+ * dev - An I3C device descriptor will be used for
+ * cmd - The buf of ccc commands to transfer, only one frame at a time
+ *
+ * Returned Value:
+ * 0 or positive if Success, nagative otherwise.
+ ****************************************************************************/
+
+int i3c_device_send_ccc_cmd(FAR const struct i3c_device *dev,
+ FAR struct i3c_ccc_cmd *cmd)
+{
+ int ret;
+ FAR struct i3c_master_controller *master;
+
+ if (dev == NULL || cmd == NULL)
+ {
+ return -EINVAL;
+ }
+
+ master = i3c_dev_get_master(dev->desc);
+
+ i3c_bus_normaluse_lock(dev->bus);
+
+ ret = i3c_master_send_ccc_cmd_locked(master, cmd);
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_master_find_i3c_dev()
Review Comment:
```suggestion
* Name: i3c_master_find_i3c_dev
```
##########
drivers/i3c/device.c:
##########
@@ -0,0 +1,346 @@
+/****************************************************************************
+ * drivers/i3c/device.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "internals.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_device_do_priv_xfers()
+ *
+ * Description:
+ * Do I3C SDR private transfers directed to a specific device.
+ *
+ * Initiate one or several private SDR transfers with @dev.
+ *
+ * This function can sleep and thus cannot be called in atomic context.
+ *
+ * Input Parameters:
+ * dev - Device with which the transfers should be done
+ * xfers - Array of transfers
+ * nxfers - Number of transfers
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_do_priv_xfers(FAR const struct i3c_device *dev,
+ FAR struct i3c_priv_xfer *xfers,
+ int nxfers)
+{
+ int ret;
+ int i;
+
+ if (nxfers < 1)
+ {
+ return 0;
+ }
+
+ for (i = 0; i < nxfers; i++)
+ {
+ if (!xfers[i].len || !xfers[i].data.in)
+ {
+ return -EINVAL;
+ }
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers);
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_get_info()
+ * Get I3C device information, Retrieve I3C dev info.
+ *
+ * Input Parameters:
+ * dev - Device we want information on.
+ * info - The information object to fill in.
+ *
+ ****************************************************************************/
+
+void i3c_device_get_info(FAR const struct i3c_device *dev,
+ FAR struct i3c_device_info *info)
+{
+ if (info == NULL)
+ {
+ return;
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ *info = dev->desc->info;
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+}
+
+/****************************************************************************
+ * Name: i3c_device_disable_ibi()
+ *
+ * Description:
+ * Disable IBIs coming from a specific device.
+ *
+ * This function disable IBIs coming from a specific device and wait for
+ * all pending IBIs to be processed.
+ *
+ * Input Parameters:
+ * dev - Device on which IBIs should be disabled
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_disable_ibi(FAR const struct i3c_device *dev)
+{
+ int ret = -ENOENT;
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ ret = i3c_dev_disable_ibi_locked(dev->desc);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_enable_ibi()
Review Comment:
```suggestion
* Name: i3c_device_enable_ibi
```
##########
drivers/i3c/i3c_driver.c:
##########
@@ -0,0 +1,398 @@
+/****************************************************************************
+ * drivers/i3c/i3c_driver.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/mutex.h>
+#include <nuttx/i3c/master.h>
+#include <nuttx/i3c/i3c_driver.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DEVNAME_FMT "/dev/i3c%d"
+#define DEVNAME_FMTLEN (8 + 3 + 1)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Driver state structure */
+
+struct i3c_driver_s
+{
+ /* Contained I3C lower half driver */
+
+ FAR struct i3c_master_controller *master;
+
+ /* Mutual exclusion */
+
+ mutex_t lock;
+
+ /* Number of open references */
+
+ int16_t crefs;
+
+ /* True, driver has been unlinked */
+
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+ bool unlinked;
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int i3cdrvr_open(FAR struct file *filep);
+static int i3cdrvr_close(FAR struct file *filep);
+static ssize_t i3cdrvr_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen);
+static ssize_t i3cdrvr_write(FAR struct file *filep, FAR const char *buffer,
+ size_t buflen);
+static int i3cdrvr_ioctl(FAR struct file *filep, int cmd,
+ unsigned long arg);
+static int i3cdrvr_unlink(FAR struct inode *inode);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_i3cdrvr_fops =
+{
+ i3cdrvr_open, /* open */
+ i3cdrvr_close, /* close */
+ i3cdrvr_read, /* read */
+ i3cdrvr_write, /* write */
+ NULL, /* seek */
+ i3cdrvr_ioctl, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* truncate */
+ NULL /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+ , i3cdrvr_unlink /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3cdrvr_open
+ ****************************************************************************/
+
+static int i3cdrvr_open(FAR struct file *filep)
+{
+ FAR struct inode *inode;
+ FAR struct i3c_driver_s *priv;
+ int ret;
+
+ DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
+ inode = filep->f_inode;
+
+ priv = inode->i_private;
+ DEBUGASSERT(priv);
+
+ /* Get exclusive access to the I3C driver state structure */
+
+ ret = nxmutex_lock(&priv->lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Increment the count of open references on the driver */
+
+ priv->crefs++;
+ DEBUGASSERT(priv->crefs > 0);
+
+ nxmutex_unlock(&priv->lock);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: i3cdrvr_close
+ ****************************************************************************/
+
+static int i3cdrvr_close(FAR struct file *filep)
+{
+ FAR struct inode *inode;
+ FAR struct i3c_driver_s *priv;
+ int ret;
+
+ /* Get our private data structure */
+
+ DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
+ inode = filep->f_inode;
+
+ priv = inode->i_private;
+ DEBUGASSERT(priv);
+
+ /* Get exclusive access to the I3C driver state structure */
+
+ ret = nxmutex_lock(&priv->lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Decrement the count of open references on the driver */
+
+ DEBUGASSERT(priv->crefs > 0);
+ priv->crefs--;
+
+ /* If the count has decremented to zero,then commit Hara-Kiri now. */
+
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+ if (priv->crefs <= 0 && priv->unlinked)
+#else
+ if (priv->crefs <= 0)
+#endif
+ {
+ nxmutex_destroy(&priv->lock);
+ kmm_free(priv);
+ return OK;
+ }
+
+ nxmutex_unlock(&priv->lock);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: i3cdrvr_read
+ ****************************************************************************/
+
+static ssize_t i3cdrvr_read(FAR struct file *filep, FAR char *buffer,
+ size_t len)
+{
+ return 0; /* Return EOF */
+}
+
+/****************************************************************************
+ * Name: i3cdrvr_write
+ ****************************************************************************/
+
+static ssize_t i3cdrvr_write(FAR struct file *filep, FAR const char *buffer,
+ size_t len)
+{
+ return len; /* Say that everything was written */
+}
+
+/****************************************************************************
+ * Name: i3cdrvr_ioctl
+ ****************************************************************************/
+
+static int i3cdrvr_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct inode *inode;
+ FAR struct i3c_driver_s *priv;
+ FAR struct i3c_transfer_s *transfer;
+ FAR struct i3c_dev_desc *desc;
+ bool obtain = false;
+ uint16_t manufid;
+ uint16_t partid;
+ int ret;
+
+ DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
+ inode = filep->f_inode;
+
+ priv = inode->i_private;
+ DEBUGASSERT(priv);
+
+ /* Get exclusive access to the I3C driver state structure */
+
+ ret = nxmutex_lock(&priv->lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ transfer = (FAR struct i3c_transfer_s *)((uintptr_t)arg);
+ DEBUGASSERT(transfer != NULL);
+
+ i3c_bus_normaluse_lock(&priv->master->bus);
+ i3c_bus_for_each_i3cdev(&priv->master->bus, desc)
+ {
+ manufid = I3C_PID_MANUF_ID(desc->info.pid);
+ partid = I3C_PID_PART_ID(desc->info.pid);
+
+ if (manufid == transfer->manufid && partid == transfer->partid)
+ {
+ obtain = true;
+ break;
+ }
+ }
+
+ i3c_bus_normaluse_unlock(&priv->master->bus);
+ if (!obtain)
+ {
+ nxmutex_unlock(&priv->lock);
+ return -ENXIO;
+ }
+
+ /* Process the IOCTL command */
+
+ switch (cmd)
+ {
+ case I3CIOC_PRIV_XFERS:
+
+ DEBUGASSERT(transfer->xfers != NULL);
+
+ /* Perform the transfer */
+
+ ret = i3c_device_do_priv_xfers(desc->dev, transfer->xfers,
+ transfer->nxfers);
+ break;
+ case I3CIOC_GET_DEVINFO:
+
+ DEBUGASSERT(transfer->info != NULL);
+
+ /* Perform the get specified i3c device operating */
+
+ i3c_device_get_info(desc->dev, transfer->info);
+ break;
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+ nxmutex_unlock(&priv->lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3cdrvr_unlink
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+static int i3cdrvr_unlink(FAR struct inode *inode)
+{
+ FAR struct i3c_driver_s *priv;
+ int ret;
+
+ /* Get our private data structure */
+
+ DEBUGASSERT(inode != NULL && inode->i_private != NULL);
+ priv = (FAR struct i3c_driver_s *)inode->i_private;
+
+ /* Get exclusive access to the I3C driver state structure */
+
+ ret = nxmutex_lock(&priv->lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Are there open references to the driver data structure? */
+
+ if (priv->crefs <= 0)
+ {
+ nxmutex_destroy(&priv->lock);
+ kmm_free(priv);
+ return OK;
+ }
+
+ /* No... just mark the driver as unlinked and free the resources when the
+ * last client closes their reference to the driver.
+ */
+
+ priv->unlinked = true;
+ nxmutex_unlock(&priv->lock);
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_register
+ *
+ * Description:
+ * Create and register the I3C character driver.
+ *
+ * The I3C character driver is a simple character driver that supports I3C
+ * transfers. The intent of this driver is to support I3C testing. It is
+ * not suitable for use in any real driver application.
+ *
+ * Input Parameters:
+ * master - the lower half of an the controller object about I3C master
+ * driver.
+ * bus - The I3C bus number. This will be used as the I3C device minor
+ * number. The I3C character device will be registered as /dev/i3cN
+ * where N is the minor number
+ *
+ * Returned Value:
+ * OK if the driver was successfully register; A negated errno value is
+ * returned on any failure.
+ *
+ ****************************************************************************/
+
+int i3c_register(FAR struct i3c_master_controller *master, int bus)
+{
+ FAR struct i3c_driver_s *priv;
+ char devname[DEVNAME_FMTLEN];
+ int ret;
+
+ /* Sanity check */
+
+ DEBUGASSERT(master != NULL && (unsigned)bus < 1000);
+
+ priv = kmm_zalloc(sizeof(struct i3c_driver_s));
+ if (priv == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ priv->master = master;
+ nxmutex_init(&priv->lock);
+
+ /* Create the character device name */
+
+ snprintf(devname, DEVNAME_FMTLEN, DEVNAME_FMT, bus);
Review Comment:
```suggestion
snprintf(devname, sizeof(devname), DEVNAME_FMT, bus);
```
##########
drivers/i3c/master.c:
##########
@@ -0,0 +1,2282 @@
+/****************************************************************************
+ * drivers/i3c/master.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <stdio.h>
+#include <debug.h>
+
+#include <nuttx/list.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/nuttx.h>
+#include <nuttx/mutex.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mm/mm.h>
+#include <nuttx/spinlock.h>
+#include <netinet/in.h>
+
+#include <nuttx/i3c/master.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/i3c/i3c_driver.h>
+#include <nuttx/i3c/device.h>
+#include "internals.h"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_lock
+ *
+ * Description:
+ * Lock the bus for a maintenance operation
+ *
+ * This function takes the bus lock so that no other operations can
+ * occur on the bus. This is needed for all kind of bus maintenance
+ * operation, like
+ * - enabling/disabling slave events
+ * - re-triggering DAA
+ * - changing the dynamic address of a device
+ * - relinquishing mastership
+ *
+ * The reason for this kind of locking is that we don't want drivers
+ * and core logic to rely on I3C device information that could be
+ * changed behind their back.
+ *
+ * Input Parameters:
+ * bus - I3C bus to take the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_unlock
+ *
+ * Description:
+ * Release the bus lock after a maintenance operation.
+ *
+ * Should be called when the bus maintenance operation is done.
+ * See i3c_bus_maintenance_lock() for more details on what these
+ * maintenance operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_to_i3c_master
+ ****************************************************************************/
+
+static FAR struct i3c_master_controller *
+i3c_bus_to_i3c_master(FAR struct i3c_bus *i3cbus)
+{
+ return container_of(i3cbus, struct i3c_master_controller, bus);
+}
+
+static FAR struct i3c_master_controller *
+i2c_adapter_to_i3c_master(FAR struct i2c_master_s *i2c_master)
+{
+ return container_of(i2c_master, struct i3c_master_controller, i2c);
+}
+
+static int i3c_master_to_i2c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i2c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_addr_slot_status
+ ****************************************************************************/
+
+static enum i3c_addr_slot_status
+i3c_bus_get_addr_slot_status(FAR struct i3c_bus *bus, uint16_t addr)
+{
+ int32_t status;
+ int bitpos = addr * 2;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return I3C_ADDR_SLOT_RSVD;
+ }
+
+ status = bus->addrslots[bitpos / I3C_BITS_PER_LONG];
+ status >>= bitpos % I3C_BITS_PER_LONG;
+
+ return status & I3C_ADDR_SLOT_STATUS_MASK;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_addr_slot_status
+ ****************************************************************************/
+
+static void i3c_bus_set_addr_slot_status(FAR struct i3c_bus *bus,
+ uint16_t addr, enum i3c_addr_slot_status status)
+{
+ int bitpos = addr * 2;
+ FAR uint32_t *ptr;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return;
+ }
+
+ ptr = bus->addrslots + (bitpos / I3C_BITS_PER_LONG);
+ *ptr &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK <<
+ (bitpos % I3C_BITS_PER_LONG));
+ *ptr |= (unsigned long)status << (bitpos % I3C_BITS_PER_LONG);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_dev_addr_is_avail
+ ****************************************************************************/
+
+static bool i3c_bus_dev_addr_is_avail(FAR struct i3c_bus *bus,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status status;
+
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ return status == I3C_ADDR_SLOT_FREE;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_free_addr
+ ****************************************************************************/
+
+static int i3c_bus_get_free_addr(FAR struct i3c_bus *bus,
+ unsigned char start_addr)
+{
+ enum i3c_addr_slot_status status;
+ unsigned char addr;
+
+ for (addr = start_addr; addr < I3C_MAX_ADDR; addr++)
+ {
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ if (status == I3C_ADDR_SLOT_FREE)
+ {
+ return addr;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init_addrslots
+ ****************************************************************************/
+
+static void i3c_bus_init_addrslots(FAR struct i3c_bus *bus)
+{
+ int i;
+
+ /* Addresses 0 to 7 are reserved. */
+
+ for (i = 0; i < 8; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, i, I3C_ADDR_SLOT_RSVD);
+ }
+
+ /* Reserve broadcast address and all addresses that might collide
+ * with the broadcast address when facing a single bit error.
+ */
+
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR,
+ I3C_ADDR_SLOT_RSVD);
+ for (i = 0; i < 7; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR ^ I3C_BIT(i),
+ I3C_ADDR_SLOT_RSVD);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init
+ ****************************************************************************/
+
+static void i3c_bus_init(FAR struct i3c_bus *i3cbus)
+{
+ FAR struct i3c_master_controller *master;
+ master = i3c_bus_to_i3c_master(i3cbus);
+
+ nxmutex_init(&i3cbus->lock);
+ list_initialize(&i3cbus->devs.i3c);
+ i3c_bus_init_addrslots(i3cbus);
+ i3cbus->mode = I3C_BUS_MODE_PURE;
+ i3cbus->id = master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_mode
+ ****************************************************************************/
+
+static int i3c_bus_set_mode(FAR struct i3c_bus *i3cbus,
+ enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate)
+{
+ i3cbus->mode = mode;
+
+ switch (i3cbus->mode)
+ {
+ case I3C_BUS_MODE_PURE:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_FAST:
+ case I3C_BUS_MODE_MIXED_LIMITED:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_SLOW:
+ {
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+
+ if (!i3cbus->scl_rate.i3c ||
+ i3cbus->scl_rate.i3c > i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i3c = i3cbus->scl_rate.i2c;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* I3C/I2C frequency may have been overridden, check that user-provided
+ * values are not exceeding max possible frequency.
+ */
+
+ if (i3cbus->scl_rate.i3c > I3C_BUS_MAX_I3C_SCL_RATE ||
+ i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_RATE)
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int i3c_master_to_i3c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_init
+ ****************************************************************************/
+
+static FAR void *i3c_ccc_cmd_dest_init(FAR struct i3c_ccc_cmd_dest *dest,
+ unsigned char addr, uint16_t payloadlen)
+{
+ dest->addr = addr;
+ dest->payload.len = payloadlen;
+ if (payloadlen)
+ {
+ dest->payload.data = kmm_zalloc(payloadlen);
+ }
+ else
+ {
+ dest->payload.data = NULL;
+ }
+
+ return dest->payload.data;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_cleanup
+ ****************************************************************************/
+
+static void i3c_ccc_cmd_dest_cleanup(FAR struct i3c_ccc_cmd_dest *dest)
+{
+ kmm_free(dest->payload.data);
+}
+
+static void i3c_ccc_cmd_init(FAR struct i3c_ccc_cmd *cmd, bool rnw,
+ unsigned char id, FAR struct i3c_ccc_cmd_dest *dests,
+ unsigned int ndests)
+{
+ cmd->rnw = rnw ? 1 : 0;
+ cmd->id = id;
+ cmd->dests = dests;
+ cmd->ndests = ndests;
+ cmd->err = I3C_ERROR_UNKNOWN;
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_alloc_i3c_dev(FAR struct i3c_master_controller *master,
+ FAR const struct i3c_device_info *info)
+{
+ FAR struct i3c_dev_desc *dev;
+
+ dev = kmm_zalloc(sizeof(*dev));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ dev->common.master = master;
+ dev->info = *info;
+ nxmutex_init(&dev->ibi_lock);
+
+ return dev;
+}
+
+static int i3c_master_rstdaa_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status addrstat;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ addrstat = i3c_bus_get_addr_slot_status(&master->bus, addr);
+ if (addr != I3C_BROADCAST_ADDR && addrstat != I3C_ADDR_SLOT_I3C_DEV)
+ {
+ return -EINVAL;
+ }
+
+ i3c_ccc_cmd_dest_init(&dest, addr, 0);
+ i3c_ccc_cmd_init(&cmd, false,
+ I3C_CCC_RSTDAA(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_enec_disec_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char addr, bool enable, unsigned char evts)
+{
+ FAR struct i3c_ccc_events *events;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ events = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*events));
+ if (events == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ events->events = evts;
+ i3c_ccc_cmd_init(&cmd, false,
+ enable ?
+ I3C_CCC_ENEC(addr == I3C_BROADCAST_ADDR) :
+ I3C_CCC_DISEC(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setda_locked(FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr, bool setdasa)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_setda *setda;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (!oldaddr || !newaddr)
+ {
+ return -EINVAL;
+ }
+
+ setda = i3c_ccc_cmd_dest_init(&dest, oldaddr, sizeof(*setda));
+ if (setda == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ setda->addr = newaddr << 1;
+ i3c_ccc_cmd_init(&cmd, false,
+ setdasa ? I3C_CCC_SETDASA : I3C_CCC_SETNEWDA,
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setnewda_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr)
+{
+ return i3c_master_setda_locked(master, oldaddr, newaddr, false);
+}
+
+static int i3c_master_getmrl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mrl *mrl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mrl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mrl));
+ if (mrl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ /* When the device does not have IBI payload GETMRL only returns 2
+ * bytes of data.
+ */
+
+ if (!(info->bcr & I3C_BCR_IBI_PAYLOAD))
+ {
+ dest.payload.len -= 1;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMRL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ switch (dest.payload.len)
+ {
+ case 3:
+ {
+ info->max_ibi_len = mrl->ibi_len;
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ case 2:
+ {
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ default:
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmwl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mwl *mwl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mwl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mwl));
+ if (mwl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMWL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != sizeof(*mwl))
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_write_len = ntohs(mwl->len);
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmxds_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getmxds *getmaxds;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getmaxds = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*getmaxds));
+ if (getmaxds == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 2 && dest.payload.len != 5)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_read_ds = getmaxds->maxrd;
+ info->max_write_ds = getmaxds->maxwr;
+ if (dest.payload.len == 5)
+ {
+ info->max_read_turnaround = getmaxds->maxrdturn[0] |
+ ((uint32_t)getmaxds->maxrdturn[1] << 8) |
+ ((uint32_t)getmaxds->maxrdturn[2] << 16);
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_gethdrcap_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_gethdrcap *gethdrcap;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ gethdrcap = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*gethdrcap));
+ if (gethdrcap == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETHDRCAP, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 1)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->hdr_cap = gethdrcap->modes;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getpid_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getpid *getpid;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+ int i;
+
+ getpid = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getpid));
+ if (getpid == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETPID, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->pid = 0;
+ for (i = 0; i < sizeof(getpid->pid); i++)
+ {
+ int sft = (sizeof(getpid->pid) - i - 1) * 8;
+ info->pid |= (uint64_t)getpid->pid[i] << sft;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getbcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getbcr *getbcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getbcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getbcr));
+ if (getbcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETBCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->bcr = getbcr->bcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getdcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getdcr *getdcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getdcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getdcr));
+ if (getdcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETDCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->dcr = getdcr->dcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_retrieve_dev_info(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status slot_status;
+ int ret;
+
+ if (!dev->info.dyn_addr)
+ {
+ return -EINVAL;
+ }
+
+ slot_status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (slot_status == I3C_ADDR_SLOT_RSVD)
+ {
+ return -EINVAL;
+ }
+
+ ret = i3c_master_getpid_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getbcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getdcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ if (dev->info.bcr & I3C_BCR_MAX_DATA_SPEED_LIM)
+ {
+ ret = i3c_master_getmxds_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
+ {
+ dev->info.max_ibi_len = 1;
+ }
+
+ i3c_master_getmrl_locked(master, &dev->info);
+ i3c_master_getmwl_locked(master, &dev->info);
+
+ if (dev->info.bcr & I3C_BCR_HDR_CAP)
+ {
+ ret = i3c_master_gethdrcap_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_put_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ if (dev->info.dyn_addr)
+ {
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_FREE);
+ }
+}
+
+static int i3c_master_get_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ /* ->init_dyn_addr should have been reserved before that, so, if we're
+ * trying to apply a pre-reserved dynamic address, we should not try
+ * to reserve the address slot a second time.
+ */
+
+ if (dev->info.dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ return 0;
+}
+
+static int i3c_master_attach_i3c_dev(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_dev_desc *dev)
+{
+ int ret;
+
+ /* We don't attach devices to the controller until they are
+ * addressable on the bus.
+ */
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ ret = i3c_master_get_i3c_addrs(dev);
+ if (ret)
+ {
+ return ret;
+ }
+
+ /* Do not attach the master device itself. */
+
+ if (master->this != dev && master->ops->attach_i3c_dev)
+ {
+ ret = master->ops->attach_i3c_dev(dev);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ list_add_tail(&master->bus.devs.i3c, &dev->common.node);
+
+ return 0;
+}
+
+static int i3c_master_reattach_i3c_dev(FAR struct i3c_dev_desc *dev,
+ unsigned char old_dyn_addr)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+ int ret;
+
+ if (dev->info.dyn_addr != old_dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus,
+ dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ if (master->ops->reattach_i3c_dev)
+ {
+ ret = master->ops->reattach_i3c_dev(dev, old_dyn_addr);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_detach_i3c_dev(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ /* Do not detach the master device itself. */
+
+ if (master->this != dev && master->ops->detach_i3c_dev)
+ {
+ master->ops->detach_i3c_dev(dev);
+ }
+
+ i3c_master_put_i3c_addrs(dev);
+ list_delete(&dev->common.node);
+}
+
+static void
+i3c_master_register_new_i3c_devs(FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *desc;
+
+ if (!master->init_done)
+ {
+ return;
+ }
+
+ i3c_bus_for_each_i3cdev(&master->bus, desc)
+ {
+ if (desc->dev || !desc->info.dyn_addr || desc == master->this)
+ {
+ continue;
+ }
+
+ desc->dev = kmm_zalloc(sizeof(*desc->dev));
+ if (desc->dev == NULL)
+ {
+ continue;
+ }
+
+ desc->dev->bus = &master->bus;
+ desc->dev->desc = desc;
+ }
+}
+
+static void i3c_master_detach_free_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+ FAR struct i3c_dev_desc *i3ctmp;
+
+ list_for_every_entry_safe(&master->bus.devs.i3c, i3cdev, i3ctmp,
+ struct i3c_dev_desc, common.node)
+ {
+ i3c_master_detach_i3c_dev(i3cdev);
+ kmm_free(i3cdev);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_master_bus_init()
+ *
+ * Description:
+ * Initialize an I3C bus
+ *
+ * This function is following all initialisation steps described in the I3C
+ * specification:
+ *
+ * 1. Attach I2C devs to the master so that the master can fill its
+ * internal device table appropriately
+ *
+ * 2. Call &i3c_master_controller_ops->bus_init() method to initialize
+ * the master controller. That's usually where the bus mode is selected
+ * (pure bus or mixed fast/slow bus)
+ *
+ * 3. Instruct all devices on the bus to drop their dynamic address. This
+ * is particularly important when the bus was previously configured by
+ * someone else (for example the bootloader)
+ *
+ * 4. Disable all slave events.
+ *
+ * 5. Reserve address slots for I3C devices with init_dyn_addr. And if
+ * devices also have static_addr, try to pre-assign dynamic addresses
+ * requested by the FW with SETDASA and attach corresponding statically
+ * defined I3C devices to the master.
+ *
+ * 6. Do a DAA (Dynamic Address Assignment) to assign dynamic addresses to
+ * all remaining I3C devices
+ *
+ * Once this is done, all I3C and I2C devices should be usable.
+ *
+ * Input Parameters:
+ * master - main master initializing the bus
+ *
+ * Returned Value:
+ * A 0 in case of success, an negative error code otherwise.
+ ****************************************************************************/
+
+static int i3c_master_bus_init(FAR struct i3c_master_controller *master)
+{
+ int ret;
+
+ /* Now execute the controller specific ->bus_init() routine, which
+ * might configure its internal logic to match the bus limitations.
+ */
+
+ ret = master->ops->bus_init(master);
+ if (ret)
+ {
+ goto err_detach_devs;
+ }
+
+ /* The master device should have been instantiated in ->bus_init(),
+ * complain if this was not the case.
+ */
+
+ if (master->this == NULL)
+ {
+ i3cerr("master_set_info() was not called in ->bus_init()\n");
+ ret = -EINVAL;
+ goto err_bus_cleanup;
+ }
+
+ /* Reset all dynamic address that may have been assigned before
+ * (assigned by the bootloader for example).
+ */
+
+ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_rstdaa_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Disable all slave events before starting DAA. */
+
+ ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
+ I3C_CCC_EVENT_HJ);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_disec_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Reserve init_dyn_addr first, and then try to pre-assign dynamic
+ * address and retrieve device information if needed.
+ * In case pre-assign dynamic address fails, setting dynamic address to
+ * the requested init_dyn_addr is retried after DAA is done in
+ * i3c_master_add_i3c_dev_locked().
+ */
+
+ ret = i3c_master_do_daa(master);
+ if (ret)
+ {
+ i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ i3cerr("master_do_daa failed:%d (no device)\n", ret);
+ }
+
+ return 0;
+
+err_bus_cleanup:
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+err_detach_devs:
+ i3c_master_detach_free_devs(master);
+
+ return ret;
+}
+
+static void i3c_master_bus_cleanup(FAR struct i3c_master_controller *master)
+{
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+ i3c_master_detach_free_devs(master);
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_search_i3c_dev_duplicate(FAR struct i3c_dev_desc *refdev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(refdev);
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev != refdev && i3cdev->info.pid == refdev->info.pid)
+ {
+ return i3cdev;
+ }
+ }
+
+ return NULL;
+}
+
+static int i3c_master_i2c_adapter_xfer(FAR struct i2c_master_s *i2c,
+ FAR struct i2c_msg_s *xfers, int nxfers)
+{
+ FAR struct i3c_master_controller *master = i2c_adapter_to_i3c_master(i2c);
+ int i;
+ int ret;
+ uint16_t addr;
+
+ if (xfers == NULL || master == NULL || nxfers <= 0)
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->i2c_xfers == NULL)
+ {
+ return -ENOTSUP;
+ }
+
+ /* Doing transfers to different devices is not supported. */
+
+ addr = xfers[0].addr;
+ for (i = 1; i < nxfers; i++)
+ {
+ if (addr != xfers[i].addr)
+ {
+ return -ENOTSUP;
+ }
+ }
+
+ i3c_bus_normaluse_lock(&master->bus);
+ ret = master->ops->i2c_xfers(master, xfers, nxfers);
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ return ret ? ret : nxfers;
+}
+
+static struct i2c_ops_s i3c_master_i2c_algo =
+{
+ .transfer = i3c_master_i2c_adapter_xfer,
+};
+
+static void i3c_master_unregister_i3c_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev->dev == NULL)
+ {
+ continue;
+ }
+
+ i3cdev->dev->desc = NULL;
+ i3cdev->dev->bus = NULL;
+ kmm_free(i3cdev->dev);
+ i3cdev->dev = NULL;
+ }
+}
+
+static void i3c_master_handle_ibi(FAR void *arg)
+{
+ FAR struct i3c_ibi_slot *slot = (struct i3c_ibi_slot *)arg;
+ FAR struct i3c_dev_desc *dev = slot->dev;
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ struct i3c_ibi_payload payload;
+
+ payload.data = slot->data;
+ payload.len = slot->len;
+
+ if (dev->dev)
+ {
+ dev->ibi->handler(dev->dev, &payload);
+ }
+
+ master->ops->recycle_ibi_slot(dev, slot);
+ atomic_fetch_sub(&dev->ibi->pending_ibis, 1);
+ if (!atomic_load(&dev->ibi->pending_ibis))
+ {
+ sem_post(&dev->ibi->all_ibis_handled);
+ }
+}
+
+static void i3c_master_init_ibi_slot(FAR struct i3c_dev_desc *dev,
+ FAR struct i3c_ibi_slot *slot)
+{
+ slot->dev = dev;
+}
+
+static int i3c_master_check_ops(
+ FAR const struct i3c_master_controller_ops *ops)
+{
+ if (ops == NULL || ops->bus_init == NULL || ops->priv_xfers == NULL ||
+ ops->send_ccc_cmd == NULL || ops->do_daa == NULL ||
+ ops->i2c_xfers == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (ops->request_ibi &&
+ (ops->enable_ibi == NULL || ops->disable_ibi == NULL ||
+ ops->free_ibi == NULL || ops->recycle_ibi_slot == NULL))
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i2c_unregister_driver()
+ *
+ * Description:
+ * Perform an I2C driver unregister operation.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ *
+ ****************************************************************************/
+
+static void i2c_unregister_driver(FAR struct i3c_master_controller *master)
+{
+ char devname[12];
+ int i2c_bus_id = i3c_master_to_i2c_bus_number(master);
+
+ snprintf(devname, 12, "/dev/i2c%d", i2c_bus_id);
+ nx_unlink(devname);
+}
+
+/****************************************************************************
+ * Name: i3c_unregister_driver()
Review Comment:
```suggestion
* Name: i3c_unregister_driver
```
##########
drivers/i3c/master.c:
##########
@@ -0,0 +1,2282 @@
+/****************************************************************************
+ * drivers/i3c/master.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <stdio.h>
+#include <debug.h>
+
+#include <nuttx/list.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/nuttx.h>
+#include <nuttx/mutex.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mm/mm.h>
+#include <nuttx/spinlock.h>
+#include <netinet/in.h>
+
+#include <nuttx/i3c/master.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/i3c/i3c_driver.h>
+#include <nuttx/i3c/device.h>
+#include "internals.h"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_lock
+ *
+ * Description:
+ * Lock the bus for a maintenance operation
+ *
+ * This function takes the bus lock so that no other operations can
+ * occur on the bus. This is needed for all kind of bus maintenance
+ * operation, like
+ * - enabling/disabling slave events
+ * - re-triggering DAA
+ * - changing the dynamic address of a device
+ * - relinquishing mastership
+ *
+ * The reason for this kind of locking is that we don't want drivers
+ * and core logic to rely on I3C device information that could be
+ * changed behind their back.
+ *
+ * Input Parameters:
+ * bus - I3C bus to take the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_unlock
+ *
+ * Description:
+ * Release the bus lock after a maintenance operation.
+ *
+ * Should be called when the bus maintenance operation is done.
+ * See i3c_bus_maintenance_lock() for more details on what these
+ * maintenance operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_to_i3c_master
+ ****************************************************************************/
+
+static FAR struct i3c_master_controller *
+i3c_bus_to_i3c_master(FAR struct i3c_bus *i3cbus)
+{
+ return container_of(i3cbus, struct i3c_master_controller, bus);
+}
+
+static FAR struct i3c_master_controller *
+i2c_adapter_to_i3c_master(FAR struct i2c_master_s *i2c_master)
+{
+ return container_of(i2c_master, struct i3c_master_controller, i2c);
+}
+
+static int i3c_master_to_i2c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i2c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_addr_slot_status
+ ****************************************************************************/
+
+static enum i3c_addr_slot_status
+i3c_bus_get_addr_slot_status(FAR struct i3c_bus *bus, uint16_t addr)
+{
+ int32_t status;
+ int bitpos = addr * 2;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return I3C_ADDR_SLOT_RSVD;
+ }
+
+ status = bus->addrslots[bitpos / I3C_BITS_PER_LONG];
+ status >>= bitpos % I3C_BITS_PER_LONG;
+
+ return status & I3C_ADDR_SLOT_STATUS_MASK;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_addr_slot_status
+ ****************************************************************************/
+
+static void i3c_bus_set_addr_slot_status(FAR struct i3c_bus *bus,
+ uint16_t addr, enum i3c_addr_slot_status status)
+{
+ int bitpos = addr * 2;
+ FAR uint32_t *ptr;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return;
+ }
+
+ ptr = bus->addrslots + (bitpos / I3C_BITS_PER_LONG);
+ *ptr &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK <<
+ (bitpos % I3C_BITS_PER_LONG));
+ *ptr |= (unsigned long)status << (bitpos % I3C_BITS_PER_LONG);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_dev_addr_is_avail
+ ****************************************************************************/
+
+static bool i3c_bus_dev_addr_is_avail(FAR struct i3c_bus *bus,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status status;
+
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ return status == I3C_ADDR_SLOT_FREE;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_free_addr
+ ****************************************************************************/
+
+static int i3c_bus_get_free_addr(FAR struct i3c_bus *bus,
+ unsigned char start_addr)
+{
+ enum i3c_addr_slot_status status;
+ unsigned char addr;
+
+ for (addr = start_addr; addr < I3C_MAX_ADDR; addr++)
+ {
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ if (status == I3C_ADDR_SLOT_FREE)
+ {
+ return addr;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init_addrslots
+ ****************************************************************************/
+
+static void i3c_bus_init_addrslots(FAR struct i3c_bus *bus)
+{
+ int i;
+
+ /* Addresses 0 to 7 are reserved. */
+
+ for (i = 0; i < 8; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, i, I3C_ADDR_SLOT_RSVD);
+ }
+
+ /* Reserve broadcast address and all addresses that might collide
+ * with the broadcast address when facing a single bit error.
+ */
+
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR,
+ I3C_ADDR_SLOT_RSVD);
+ for (i = 0; i < 7; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR ^ I3C_BIT(i),
+ I3C_ADDR_SLOT_RSVD);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init
+ ****************************************************************************/
+
+static void i3c_bus_init(FAR struct i3c_bus *i3cbus)
+{
+ FAR struct i3c_master_controller *master;
+ master = i3c_bus_to_i3c_master(i3cbus);
+
+ nxmutex_init(&i3cbus->lock);
+ list_initialize(&i3cbus->devs.i3c);
+ i3c_bus_init_addrslots(i3cbus);
+ i3cbus->mode = I3C_BUS_MODE_PURE;
+ i3cbus->id = master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_mode
+ ****************************************************************************/
+
+static int i3c_bus_set_mode(FAR struct i3c_bus *i3cbus,
+ enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate)
+{
+ i3cbus->mode = mode;
+
+ switch (i3cbus->mode)
+ {
+ case I3C_BUS_MODE_PURE:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_FAST:
+ case I3C_BUS_MODE_MIXED_LIMITED:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_SLOW:
+ {
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+
+ if (!i3cbus->scl_rate.i3c ||
+ i3cbus->scl_rate.i3c > i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i3c = i3cbus->scl_rate.i2c;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* I3C/I2C frequency may have been overridden, check that user-provided
+ * values are not exceeding max possible frequency.
+ */
+
+ if (i3cbus->scl_rate.i3c > I3C_BUS_MAX_I3C_SCL_RATE ||
+ i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_RATE)
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int i3c_master_to_i3c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_init
+ ****************************************************************************/
+
+static FAR void *i3c_ccc_cmd_dest_init(FAR struct i3c_ccc_cmd_dest *dest,
+ unsigned char addr, uint16_t payloadlen)
+{
+ dest->addr = addr;
+ dest->payload.len = payloadlen;
+ if (payloadlen)
+ {
+ dest->payload.data = kmm_zalloc(payloadlen);
+ }
+ else
+ {
+ dest->payload.data = NULL;
+ }
+
+ return dest->payload.data;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_cleanup
+ ****************************************************************************/
+
+static void i3c_ccc_cmd_dest_cleanup(FAR struct i3c_ccc_cmd_dest *dest)
+{
+ kmm_free(dest->payload.data);
+}
+
+static void i3c_ccc_cmd_init(FAR struct i3c_ccc_cmd *cmd, bool rnw,
+ unsigned char id, FAR struct i3c_ccc_cmd_dest *dests,
+ unsigned int ndests)
+{
+ cmd->rnw = rnw ? 1 : 0;
+ cmd->id = id;
+ cmd->dests = dests;
+ cmd->ndests = ndests;
+ cmd->err = I3C_ERROR_UNKNOWN;
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_alloc_i3c_dev(FAR struct i3c_master_controller *master,
+ FAR const struct i3c_device_info *info)
+{
+ FAR struct i3c_dev_desc *dev;
+
+ dev = kmm_zalloc(sizeof(*dev));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ dev->common.master = master;
+ dev->info = *info;
+ nxmutex_init(&dev->ibi_lock);
+
+ return dev;
+}
+
+static int i3c_master_rstdaa_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status addrstat;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ addrstat = i3c_bus_get_addr_slot_status(&master->bus, addr);
+ if (addr != I3C_BROADCAST_ADDR && addrstat != I3C_ADDR_SLOT_I3C_DEV)
+ {
+ return -EINVAL;
+ }
+
+ i3c_ccc_cmd_dest_init(&dest, addr, 0);
+ i3c_ccc_cmd_init(&cmd, false,
+ I3C_CCC_RSTDAA(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_enec_disec_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char addr, bool enable, unsigned char evts)
+{
+ FAR struct i3c_ccc_events *events;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ events = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*events));
+ if (events == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ events->events = evts;
+ i3c_ccc_cmd_init(&cmd, false,
+ enable ?
+ I3C_CCC_ENEC(addr == I3C_BROADCAST_ADDR) :
+ I3C_CCC_DISEC(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setda_locked(FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr, bool setdasa)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_setda *setda;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (!oldaddr || !newaddr)
+ {
+ return -EINVAL;
+ }
+
+ setda = i3c_ccc_cmd_dest_init(&dest, oldaddr, sizeof(*setda));
+ if (setda == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ setda->addr = newaddr << 1;
+ i3c_ccc_cmd_init(&cmd, false,
+ setdasa ? I3C_CCC_SETDASA : I3C_CCC_SETNEWDA,
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setnewda_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr)
+{
+ return i3c_master_setda_locked(master, oldaddr, newaddr, false);
+}
+
+static int i3c_master_getmrl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mrl *mrl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mrl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mrl));
+ if (mrl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ /* When the device does not have IBI payload GETMRL only returns 2
+ * bytes of data.
+ */
+
+ if (!(info->bcr & I3C_BCR_IBI_PAYLOAD))
+ {
+ dest.payload.len -= 1;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMRL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ switch (dest.payload.len)
+ {
+ case 3:
+ {
+ info->max_ibi_len = mrl->ibi_len;
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ case 2:
+ {
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ default:
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmwl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mwl *mwl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mwl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mwl));
+ if (mwl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMWL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != sizeof(*mwl))
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_write_len = ntohs(mwl->len);
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmxds_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getmxds *getmaxds;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getmaxds = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*getmaxds));
+ if (getmaxds == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 2 && dest.payload.len != 5)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_read_ds = getmaxds->maxrd;
+ info->max_write_ds = getmaxds->maxwr;
+ if (dest.payload.len == 5)
+ {
+ info->max_read_turnaround = getmaxds->maxrdturn[0] |
+ ((uint32_t)getmaxds->maxrdturn[1] << 8) |
+ ((uint32_t)getmaxds->maxrdturn[2] << 16);
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_gethdrcap_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_gethdrcap *gethdrcap;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ gethdrcap = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*gethdrcap));
+ if (gethdrcap == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETHDRCAP, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 1)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->hdr_cap = gethdrcap->modes;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getpid_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getpid *getpid;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+ int i;
+
+ getpid = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getpid));
+ if (getpid == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETPID, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->pid = 0;
+ for (i = 0; i < sizeof(getpid->pid); i++)
+ {
+ int sft = (sizeof(getpid->pid) - i - 1) * 8;
+ info->pid |= (uint64_t)getpid->pid[i] << sft;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getbcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getbcr *getbcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getbcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getbcr));
+ if (getbcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETBCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->bcr = getbcr->bcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getdcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getdcr *getdcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getdcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getdcr));
+ if (getdcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETDCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->dcr = getdcr->dcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_retrieve_dev_info(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status slot_status;
+ int ret;
+
+ if (!dev->info.dyn_addr)
+ {
+ return -EINVAL;
+ }
+
+ slot_status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (slot_status == I3C_ADDR_SLOT_RSVD)
+ {
+ return -EINVAL;
+ }
+
+ ret = i3c_master_getpid_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getbcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getdcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ if (dev->info.bcr & I3C_BCR_MAX_DATA_SPEED_LIM)
+ {
+ ret = i3c_master_getmxds_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
+ {
+ dev->info.max_ibi_len = 1;
+ }
+
+ i3c_master_getmrl_locked(master, &dev->info);
+ i3c_master_getmwl_locked(master, &dev->info);
+
+ if (dev->info.bcr & I3C_BCR_HDR_CAP)
+ {
+ ret = i3c_master_gethdrcap_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_put_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ if (dev->info.dyn_addr)
+ {
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_FREE);
+ }
+}
+
+static int i3c_master_get_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ /* ->init_dyn_addr should have been reserved before that, so, if we're
+ * trying to apply a pre-reserved dynamic address, we should not try
+ * to reserve the address slot a second time.
+ */
+
+ if (dev->info.dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ return 0;
+}
+
+static int i3c_master_attach_i3c_dev(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_dev_desc *dev)
+{
+ int ret;
+
+ /* We don't attach devices to the controller until they are
+ * addressable on the bus.
+ */
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ ret = i3c_master_get_i3c_addrs(dev);
+ if (ret)
+ {
+ return ret;
+ }
+
+ /* Do not attach the master device itself. */
+
+ if (master->this != dev && master->ops->attach_i3c_dev)
+ {
+ ret = master->ops->attach_i3c_dev(dev);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ list_add_tail(&master->bus.devs.i3c, &dev->common.node);
+
+ return 0;
+}
+
+static int i3c_master_reattach_i3c_dev(FAR struct i3c_dev_desc *dev,
+ unsigned char old_dyn_addr)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+ int ret;
+
+ if (dev->info.dyn_addr != old_dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus,
+ dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ if (master->ops->reattach_i3c_dev)
+ {
+ ret = master->ops->reattach_i3c_dev(dev, old_dyn_addr);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_detach_i3c_dev(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ /* Do not detach the master device itself. */
+
+ if (master->this != dev && master->ops->detach_i3c_dev)
+ {
+ master->ops->detach_i3c_dev(dev);
+ }
+
+ i3c_master_put_i3c_addrs(dev);
+ list_delete(&dev->common.node);
+}
+
+static void
+i3c_master_register_new_i3c_devs(FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *desc;
+
+ if (!master->init_done)
+ {
+ return;
+ }
+
+ i3c_bus_for_each_i3cdev(&master->bus, desc)
+ {
+ if (desc->dev || !desc->info.dyn_addr || desc == master->this)
+ {
+ continue;
+ }
+
+ desc->dev = kmm_zalloc(sizeof(*desc->dev));
+ if (desc->dev == NULL)
+ {
+ continue;
+ }
+
+ desc->dev->bus = &master->bus;
+ desc->dev->desc = desc;
+ }
+}
+
+static void i3c_master_detach_free_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+ FAR struct i3c_dev_desc *i3ctmp;
+
+ list_for_every_entry_safe(&master->bus.devs.i3c, i3cdev, i3ctmp,
+ struct i3c_dev_desc, common.node)
+ {
+ i3c_master_detach_i3c_dev(i3cdev);
+ kmm_free(i3cdev);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_master_bus_init()
+ *
+ * Description:
+ * Initialize an I3C bus
+ *
+ * This function is following all initialisation steps described in the I3C
+ * specification:
+ *
+ * 1. Attach I2C devs to the master so that the master can fill its
+ * internal device table appropriately
+ *
+ * 2. Call &i3c_master_controller_ops->bus_init() method to initialize
+ * the master controller. That's usually where the bus mode is selected
+ * (pure bus or mixed fast/slow bus)
+ *
+ * 3. Instruct all devices on the bus to drop their dynamic address. This
+ * is particularly important when the bus was previously configured by
+ * someone else (for example the bootloader)
+ *
+ * 4. Disable all slave events.
+ *
+ * 5. Reserve address slots for I3C devices with init_dyn_addr. And if
+ * devices also have static_addr, try to pre-assign dynamic addresses
+ * requested by the FW with SETDASA and attach corresponding statically
+ * defined I3C devices to the master.
+ *
+ * 6. Do a DAA (Dynamic Address Assignment) to assign dynamic addresses to
+ * all remaining I3C devices
+ *
+ * Once this is done, all I3C and I2C devices should be usable.
+ *
+ * Input Parameters:
+ * master - main master initializing the bus
+ *
+ * Returned Value:
+ * A 0 in case of success, an negative error code otherwise.
+ ****************************************************************************/
+
+static int i3c_master_bus_init(FAR struct i3c_master_controller *master)
+{
+ int ret;
+
+ /* Now execute the controller specific ->bus_init() routine, which
+ * might configure its internal logic to match the bus limitations.
+ */
+
+ ret = master->ops->bus_init(master);
+ if (ret)
+ {
+ goto err_detach_devs;
+ }
+
+ /* The master device should have been instantiated in ->bus_init(),
+ * complain if this was not the case.
+ */
+
+ if (master->this == NULL)
+ {
+ i3cerr("master_set_info() was not called in ->bus_init()\n");
+ ret = -EINVAL;
+ goto err_bus_cleanup;
+ }
+
+ /* Reset all dynamic address that may have been assigned before
+ * (assigned by the bootloader for example).
+ */
+
+ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_rstdaa_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Disable all slave events before starting DAA. */
+
+ ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
+ I3C_CCC_EVENT_HJ);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_disec_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Reserve init_dyn_addr first, and then try to pre-assign dynamic
+ * address and retrieve device information if needed.
+ * In case pre-assign dynamic address fails, setting dynamic address to
+ * the requested init_dyn_addr is retried after DAA is done in
+ * i3c_master_add_i3c_dev_locked().
+ */
+
+ ret = i3c_master_do_daa(master);
+ if (ret)
+ {
+ i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ i3cerr("master_do_daa failed:%d (no device)\n", ret);
+ }
+
+ return 0;
+
+err_bus_cleanup:
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+err_detach_devs:
+ i3c_master_detach_free_devs(master);
+
+ return ret;
+}
+
+static void i3c_master_bus_cleanup(FAR struct i3c_master_controller *master)
+{
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+ i3c_master_detach_free_devs(master);
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_search_i3c_dev_duplicate(FAR struct i3c_dev_desc *refdev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(refdev);
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev != refdev && i3cdev->info.pid == refdev->info.pid)
+ {
+ return i3cdev;
+ }
+ }
+
+ return NULL;
+}
+
+static int i3c_master_i2c_adapter_xfer(FAR struct i2c_master_s *i2c,
+ FAR struct i2c_msg_s *xfers, int nxfers)
+{
+ FAR struct i3c_master_controller *master = i2c_adapter_to_i3c_master(i2c);
+ int i;
+ int ret;
+ uint16_t addr;
+
+ if (xfers == NULL || master == NULL || nxfers <= 0)
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->i2c_xfers == NULL)
+ {
+ return -ENOTSUP;
+ }
+
+ /* Doing transfers to different devices is not supported. */
+
+ addr = xfers[0].addr;
+ for (i = 1; i < nxfers; i++)
+ {
+ if (addr != xfers[i].addr)
+ {
+ return -ENOTSUP;
+ }
+ }
+
+ i3c_bus_normaluse_lock(&master->bus);
+ ret = master->ops->i2c_xfers(master, xfers, nxfers);
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ return ret ? ret : nxfers;
+}
+
+static struct i2c_ops_s i3c_master_i2c_algo =
+{
+ .transfer = i3c_master_i2c_adapter_xfer,
+};
+
+static void i3c_master_unregister_i3c_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev->dev == NULL)
+ {
+ continue;
+ }
+
+ i3cdev->dev->desc = NULL;
+ i3cdev->dev->bus = NULL;
+ kmm_free(i3cdev->dev);
+ i3cdev->dev = NULL;
+ }
+}
+
+static void i3c_master_handle_ibi(FAR void *arg)
+{
+ FAR struct i3c_ibi_slot *slot = (struct i3c_ibi_slot *)arg;
+ FAR struct i3c_dev_desc *dev = slot->dev;
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ struct i3c_ibi_payload payload;
+
+ payload.data = slot->data;
+ payload.len = slot->len;
+
+ if (dev->dev)
+ {
+ dev->ibi->handler(dev->dev, &payload);
+ }
+
+ master->ops->recycle_ibi_slot(dev, slot);
+ atomic_fetch_sub(&dev->ibi->pending_ibis, 1);
+ if (!atomic_load(&dev->ibi->pending_ibis))
+ {
+ sem_post(&dev->ibi->all_ibis_handled);
+ }
+}
+
+static void i3c_master_init_ibi_slot(FAR struct i3c_dev_desc *dev,
+ FAR struct i3c_ibi_slot *slot)
+{
+ slot->dev = dev;
+}
+
+static int i3c_master_check_ops(
+ FAR const struct i3c_master_controller_ops *ops)
+{
+ if (ops == NULL || ops->bus_init == NULL || ops->priv_xfers == NULL ||
+ ops->send_ccc_cmd == NULL || ops->do_daa == NULL ||
+ ops->i2c_xfers == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (ops->request_ibi &&
+ (ops->enable_ibi == NULL || ops->disable_ibi == NULL ||
+ ops->free_ibi == NULL || ops->recycle_ibi_slot == NULL))
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i2c_unregister_driver()
+ *
+ * Description:
+ * Perform an I2C driver unregister operation.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ *
+ ****************************************************************************/
+
+static void i2c_unregister_driver(FAR struct i3c_master_controller *master)
+{
+ char devname[12];
+ int i2c_bus_id = i3c_master_to_i2c_bus_number(master);
+
+ snprintf(devname, 12, "/dev/i2c%d", i2c_bus_id);
+ nx_unlink(devname);
+}
+
+/****************************************************************************
+ * Name: i3c_unregister_driver()
+ *
+ * Description:
+ * Perform an I3C driver unregister operation.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ *
+ ****************************************************************************/
+
+static void i3c_unregister_driver(FAR struct i3c_master_controller *master)
+{
+ char devname[12];
+
+ snprintf(devname, 12, "/dev/i3c%d", i3c_master_to_i3c_bus_number(master));
+ nx_unlink(devname);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_master_send_ccc_cmd_locked
+ ****************************************************************************/
+
+int i3c_master_send_ccc_cmd_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_ccc_cmd *cmd)
+{
+ int ret;
+
+ if (cmd == NULL || master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (master->init_done &&
+ !nxmutex_is_locked(&master->bus.lock))
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->send_ccc_cmd == NULL)
+ {
+ return -ENOTSUP;
+ }
+
+ if ((cmd->id & I3C_CCC_DIRECT) && (cmd->dests == NULL || !cmd->ndests))
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->supports_ccc_cmd &&
+ !master->ops->supports_ccc_cmd(master, cmd))
+ {
+ return -ENOTSUP;
+ }
+
+ ret = master->ops->send_ccc_cmd(master, cmd);
+ if (ret)
+ {
+ if (cmd->err != I3C_ERROR_UNKNOWN)
+ {
+ return cmd->err;
+ }
+
+ return ret;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_normaluse_lock
+ *
+ * Description:
+ * I3C bus to take the lock on.
+ *
+ * This function takes the bus lock for any operation that is not a
+ * maintenance operation (see i3c_bus_maintenance_lock() for
+ * non-exhaustive list of maintenance operations). Basically all
+ * communications with I3C devices are normal operations (HDR, SDR
+ * transfers or CCC commands that do not change bus state or I3C dynamic
+ * address).
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+void i3c_bus_normaluse_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_normaluse_unlock
+ *
+ * Description:
+ * Release the bus lock after a normal operation.
+ *
+ * Should be called when a normal operation is done. See
+ * i3c_bus_normaluse_lock() for more details on what these
+ * normal operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ *
+ ****************************************************************************/
+
+void i3c_bus_normaluse_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_master_get_free_addr()
Review Comment:
```suggestion
* Name: i3c_master_get_free_addr
```
##########
drivers/i3c/master.c:
##########
@@ -0,0 +1,2282 @@
+/****************************************************************************
+ * drivers/i3c/master.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <stdio.h>
+#include <debug.h>
+
+#include <nuttx/list.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/nuttx.h>
+#include <nuttx/mutex.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mm/mm.h>
+#include <nuttx/spinlock.h>
+#include <netinet/in.h>
+
+#include <nuttx/i3c/master.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/i3c/i3c_driver.h>
+#include <nuttx/i3c/device.h>
+#include "internals.h"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_lock
+ *
+ * Description:
+ * Lock the bus for a maintenance operation
+ *
+ * This function takes the bus lock so that no other operations can
+ * occur on the bus. This is needed for all kind of bus maintenance
+ * operation, like
+ * - enabling/disabling slave events
+ * - re-triggering DAA
+ * - changing the dynamic address of a device
+ * - relinquishing mastership
+ *
+ * The reason for this kind of locking is that we don't want drivers
+ * and core logic to rely on I3C device information that could be
+ * changed behind their back.
+ *
+ * Input Parameters:
+ * bus - I3C bus to take the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_unlock
+ *
+ * Description:
+ * Release the bus lock after a maintenance operation.
+ *
+ * Should be called when the bus maintenance operation is done.
+ * See i3c_bus_maintenance_lock() for more details on what these
+ * maintenance operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_to_i3c_master
+ ****************************************************************************/
+
+static FAR struct i3c_master_controller *
+i3c_bus_to_i3c_master(FAR struct i3c_bus *i3cbus)
+{
+ return container_of(i3cbus, struct i3c_master_controller, bus);
+}
+
+static FAR struct i3c_master_controller *
+i2c_adapter_to_i3c_master(FAR struct i2c_master_s *i2c_master)
+{
+ return container_of(i2c_master, struct i3c_master_controller, i2c);
+}
+
+static int i3c_master_to_i2c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i2c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_addr_slot_status
+ ****************************************************************************/
+
+static enum i3c_addr_slot_status
+i3c_bus_get_addr_slot_status(FAR struct i3c_bus *bus, uint16_t addr)
+{
+ int32_t status;
+ int bitpos = addr * 2;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return I3C_ADDR_SLOT_RSVD;
+ }
+
+ status = bus->addrslots[bitpos / I3C_BITS_PER_LONG];
+ status >>= bitpos % I3C_BITS_PER_LONG;
+
+ return status & I3C_ADDR_SLOT_STATUS_MASK;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_addr_slot_status
+ ****************************************************************************/
+
+static void i3c_bus_set_addr_slot_status(FAR struct i3c_bus *bus,
+ uint16_t addr, enum i3c_addr_slot_status status)
+{
+ int bitpos = addr * 2;
+ FAR uint32_t *ptr;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return;
+ }
+
+ ptr = bus->addrslots + (bitpos / I3C_BITS_PER_LONG);
+ *ptr &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK <<
+ (bitpos % I3C_BITS_PER_LONG));
+ *ptr |= (unsigned long)status << (bitpos % I3C_BITS_PER_LONG);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_dev_addr_is_avail
+ ****************************************************************************/
+
+static bool i3c_bus_dev_addr_is_avail(FAR struct i3c_bus *bus,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status status;
+
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ return status == I3C_ADDR_SLOT_FREE;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_free_addr
+ ****************************************************************************/
+
+static int i3c_bus_get_free_addr(FAR struct i3c_bus *bus,
+ unsigned char start_addr)
+{
+ enum i3c_addr_slot_status status;
+ unsigned char addr;
+
+ for (addr = start_addr; addr < I3C_MAX_ADDR; addr++)
+ {
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ if (status == I3C_ADDR_SLOT_FREE)
+ {
+ return addr;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init_addrslots
+ ****************************************************************************/
+
+static void i3c_bus_init_addrslots(FAR struct i3c_bus *bus)
+{
+ int i;
+
+ /* Addresses 0 to 7 are reserved. */
+
+ for (i = 0; i < 8; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, i, I3C_ADDR_SLOT_RSVD);
+ }
+
+ /* Reserve broadcast address and all addresses that might collide
+ * with the broadcast address when facing a single bit error.
+ */
+
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR,
+ I3C_ADDR_SLOT_RSVD);
+ for (i = 0; i < 7; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR ^ I3C_BIT(i),
+ I3C_ADDR_SLOT_RSVD);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init
+ ****************************************************************************/
+
+static void i3c_bus_init(FAR struct i3c_bus *i3cbus)
+{
+ FAR struct i3c_master_controller *master;
+ master = i3c_bus_to_i3c_master(i3cbus);
+
+ nxmutex_init(&i3cbus->lock);
+ list_initialize(&i3cbus->devs.i3c);
+ i3c_bus_init_addrslots(i3cbus);
+ i3cbus->mode = I3C_BUS_MODE_PURE;
+ i3cbus->id = master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_mode
+ ****************************************************************************/
+
+static int i3c_bus_set_mode(FAR struct i3c_bus *i3cbus,
+ enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate)
+{
+ i3cbus->mode = mode;
+
+ switch (i3cbus->mode)
+ {
+ case I3C_BUS_MODE_PURE:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_FAST:
+ case I3C_BUS_MODE_MIXED_LIMITED:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_SLOW:
+ {
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+
+ if (!i3cbus->scl_rate.i3c ||
+ i3cbus->scl_rate.i3c > i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i3c = i3cbus->scl_rate.i2c;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* I3C/I2C frequency may have been overridden, check that user-provided
+ * values are not exceeding max possible frequency.
+ */
+
+ if (i3cbus->scl_rate.i3c > I3C_BUS_MAX_I3C_SCL_RATE ||
+ i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_RATE)
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int i3c_master_to_i3c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_init
+ ****************************************************************************/
+
+static FAR void *i3c_ccc_cmd_dest_init(FAR struct i3c_ccc_cmd_dest *dest,
+ unsigned char addr, uint16_t payloadlen)
+{
+ dest->addr = addr;
+ dest->payload.len = payloadlen;
+ if (payloadlen)
+ {
+ dest->payload.data = kmm_zalloc(payloadlen);
+ }
+ else
+ {
+ dest->payload.data = NULL;
+ }
+
+ return dest->payload.data;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_cleanup
+ ****************************************************************************/
+
+static void i3c_ccc_cmd_dest_cleanup(FAR struct i3c_ccc_cmd_dest *dest)
+{
+ kmm_free(dest->payload.data);
+}
+
+static void i3c_ccc_cmd_init(FAR struct i3c_ccc_cmd *cmd, bool rnw,
+ unsigned char id, FAR struct i3c_ccc_cmd_dest *dests,
+ unsigned int ndests)
+{
+ cmd->rnw = rnw ? 1 : 0;
+ cmd->id = id;
+ cmd->dests = dests;
+ cmd->ndests = ndests;
+ cmd->err = I3C_ERROR_UNKNOWN;
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_alloc_i3c_dev(FAR struct i3c_master_controller *master,
+ FAR const struct i3c_device_info *info)
+{
+ FAR struct i3c_dev_desc *dev;
+
+ dev = kmm_zalloc(sizeof(*dev));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ dev->common.master = master;
+ dev->info = *info;
+ nxmutex_init(&dev->ibi_lock);
+
+ return dev;
+}
+
+static int i3c_master_rstdaa_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status addrstat;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ addrstat = i3c_bus_get_addr_slot_status(&master->bus, addr);
+ if (addr != I3C_BROADCAST_ADDR && addrstat != I3C_ADDR_SLOT_I3C_DEV)
+ {
+ return -EINVAL;
+ }
+
+ i3c_ccc_cmd_dest_init(&dest, addr, 0);
+ i3c_ccc_cmd_init(&cmd, false,
+ I3C_CCC_RSTDAA(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_enec_disec_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char addr, bool enable, unsigned char evts)
+{
+ FAR struct i3c_ccc_events *events;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ events = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*events));
+ if (events == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ events->events = evts;
+ i3c_ccc_cmd_init(&cmd, false,
+ enable ?
+ I3C_CCC_ENEC(addr == I3C_BROADCAST_ADDR) :
+ I3C_CCC_DISEC(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setda_locked(FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr, bool setdasa)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_setda *setda;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (!oldaddr || !newaddr)
+ {
+ return -EINVAL;
+ }
+
+ setda = i3c_ccc_cmd_dest_init(&dest, oldaddr, sizeof(*setda));
+ if (setda == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ setda->addr = newaddr << 1;
+ i3c_ccc_cmd_init(&cmd, false,
+ setdasa ? I3C_CCC_SETDASA : I3C_CCC_SETNEWDA,
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setnewda_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr)
+{
+ return i3c_master_setda_locked(master, oldaddr, newaddr, false);
+}
+
+static int i3c_master_getmrl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mrl *mrl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mrl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mrl));
+ if (mrl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ /* When the device does not have IBI payload GETMRL only returns 2
+ * bytes of data.
+ */
+
+ if (!(info->bcr & I3C_BCR_IBI_PAYLOAD))
+ {
+ dest.payload.len -= 1;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMRL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ switch (dest.payload.len)
+ {
+ case 3:
+ {
+ info->max_ibi_len = mrl->ibi_len;
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ case 2:
+ {
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ default:
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmwl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mwl *mwl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mwl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mwl));
+ if (mwl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMWL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != sizeof(*mwl))
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_write_len = ntohs(mwl->len);
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmxds_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getmxds *getmaxds;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getmaxds = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*getmaxds));
+ if (getmaxds == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 2 && dest.payload.len != 5)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_read_ds = getmaxds->maxrd;
+ info->max_write_ds = getmaxds->maxwr;
+ if (dest.payload.len == 5)
+ {
+ info->max_read_turnaround = getmaxds->maxrdturn[0] |
+ ((uint32_t)getmaxds->maxrdturn[1] << 8) |
+ ((uint32_t)getmaxds->maxrdturn[2] << 16);
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_gethdrcap_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_gethdrcap *gethdrcap;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ gethdrcap = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*gethdrcap));
+ if (gethdrcap == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETHDRCAP, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 1)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->hdr_cap = gethdrcap->modes;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getpid_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getpid *getpid;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+ int i;
+
+ getpid = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getpid));
+ if (getpid == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETPID, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->pid = 0;
+ for (i = 0; i < sizeof(getpid->pid); i++)
+ {
+ int sft = (sizeof(getpid->pid) - i - 1) * 8;
+ info->pid |= (uint64_t)getpid->pid[i] << sft;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getbcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getbcr *getbcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getbcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getbcr));
+ if (getbcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETBCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->bcr = getbcr->bcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getdcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getdcr *getdcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getdcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getdcr));
+ if (getdcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETDCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->dcr = getdcr->dcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_retrieve_dev_info(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status slot_status;
+ int ret;
+
+ if (!dev->info.dyn_addr)
+ {
+ return -EINVAL;
+ }
+
+ slot_status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (slot_status == I3C_ADDR_SLOT_RSVD)
+ {
+ return -EINVAL;
+ }
+
+ ret = i3c_master_getpid_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getbcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getdcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ if (dev->info.bcr & I3C_BCR_MAX_DATA_SPEED_LIM)
+ {
+ ret = i3c_master_getmxds_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
+ {
+ dev->info.max_ibi_len = 1;
+ }
+
+ i3c_master_getmrl_locked(master, &dev->info);
+ i3c_master_getmwl_locked(master, &dev->info);
+
+ if (dev->info.bcr & I3C_BCR_HDR_CAP)
+ {
+ ret = i3c_master_gethdrcap_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_put_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ if (dev->info.dyn_addr)
+ {
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_FREE);
+ }
+}
+
+static int i3c_master_get_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ /* ->init_dyn_addr should have been reserved before that, so, if we're
+ * trying to apply a pre-reserved dynamic address, we should not try
+ * to reserve the address slot a second time.
+ */
+
+ if (dev->info.dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ return 0;
+}
+
+static int i3c_master_attach_i3c_dev(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_dev_desc *dev)
+{
+ int ret;
+
+ /* We don't attach devices to the controller until they are
+ * addressable on the bus.
+ */
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ ret = i3c_master_get_i3c_addrs(dev);
+ if (ret)
+ {
+ return ret;
+ }
+
+ /* Do not attach the master device itself. */
+
+ if (master->this != dev && master->ops->attach_i3c_dev)
+ {
+ ret = master->ops->attach_i3c_dev(dev);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ list_add_tail(&master->bus.devs.i3c, &dev->common.node);
+
+ return 0;
+}
+
+static int i3c_master_reattach_i3c_dev(FAR struct i3c_dev_desc *dev,
+ unsigned char old_dyn_addr)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+ int ret;
+
+ if (dev->info.dyn_addr != old_dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus,
+ dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ if (master->ops->reattach_i3c_dev)
+ {
+ ret = master->ops->reattach_i3c_dev(dev, old_dyn_addr);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_detach_i3c_dev(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ /* Do not detach the master device itself. */
+
+ if (master->this != dev && master->ops->detach_i3c_dev)
+ {
+ master->ops->detach_i3c_dev(dev);
+ }
+
+ i3c_master_put_i3c_addrs(dev);
+ list_delete(&dev->common.node);
+}
+
+static void
+i3c_master_register_new_i3c_devs(FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *desc;
+
+ if (!master->init_done)
+ {
+ return;
+ }
+
+ i3c_bus_for_each_i3cdev(&master->bus, desc)
+ {
+ if (desc->dev || !desc->info.dyn_addr || desc == master->this)
+ {
+ continue;
+ }
+
+ desc->dev = kmm_zalloc(sizeof(*desc->dev));
+ if (desc->dev == NULL)
+ {
+ continue;
+ }
+
+ desc->dev->bus = &master->bus;
+ desc->dev->desc = desc;
+ }
+}
+
+static void i3c_master_detach_free_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+ FAR struct i3c_dev_desc *i3ctmp;
+
+ list_for_every_entry_safe(&master->bus.devs.i3c, i3cdev, i3ctmp,
+ struct i3c_dev_desc, common.node)
+ {
+ i3c_master_detach_i3c_dev(i3cdev);
+ kmm_free(i3cdev);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_master_bus_init()
+ *
+ * Description:
+ * Initialize an I3C bus
+ *
+ * This function is following all initialisation steps described in the I3C
+ * specification:
+ *
+ * 1. Attach I2C devs to the master so that the master can fill its
+ * internal device table appropriately
+ *
+ * 2. Call &i3c_master_controller_ops->bus_init() method to initialize
+ * the master controller. That's usually where the bus mode is selected
+ * (pure bus or mixed fast/slow bus)
+ *
+ * 3. Instruct all devices on the bus to drop their dynamic address. This
+ * is particularly important when the bus was previously configured by
+ * someone else (for example the bootloader)
+ *
+ * 4. Disable all slave events.
+ *
+ * 5. Reserve address slots for I3C devices with init_dyn_addr. And if
+ * devices also have static_addr, try to pre-assign dynamic addresses
+ * requested by the FW with SETDASA and attach corresponding statically
+ * defined I3C devices to the master.
+ *
+ * 6. Do a DAA (Dynamic Address Assignment) to assign dynamic addresses to
+ * all remaining I3C devices
+ *
+ * Once this is done, all I3C and I2C devices should be usable.
+ *
+ * Input Parameters:
+ * master - main master initializing the bus
+ *
+ * Returned Value:
+ * A 0 in case of success, an negative error code otherwise.
+ ****************************************************************************/
+
+static int i3c_master_bus_init(FAR struct i3c_master_controller *master)
+{
+ int ret;
+
+ /* Now execute the controller specific ->bus_init() routine, which
+ * might configure its internal logic to match the bus limitations.
+ */
+
+ ret = master->ops->bus_init(master);
+ if (ret)
+ {
+ goto err_detach_devs;
+ }
+
+ /* The master device should have been instantiated in ->bus_init(),
+ * complain if this was not the case.
+ */
+
+ if (master->this == NULL)
+ {
+ i3cerr("master_set_info() was not called in ->bus_init()\n");
+ ret = -EINVAL;
+ goto err_bus_cleanup;
+ }
+
+ /* Reset all dynamic address that may have been assigned before
+ * (assigned by the bootloader for example).
+ */
+
+ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_rstdaa_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Disable all slave events before starting DAA. */
+
+ ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
+ I3C_CCC_EVENT_HJ);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_disec_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Reserve init_dyn_addr first, and then try to pre-assign dynamic
+ * address and retrieve device information if needed.
+ * In case pre-assign dynamic address fails, setting dynamic address to
+ * the requested init_dyn_addr is retried after DAA is done in
+ * i3c_master_add_i3c_dev_locked().
+ */
+
+ ret = i3c_master_do_daa(master);
+ if (ret)
+ {
+ i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ i3cerr("master_do_daa failed:%d (no device)\n", ret);
+ }
+
+ return 0;
+
+err_bus_cleanup:
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+err_detach_devs:
+ i3c_master_detach_free_devs(master);
+
+ return ret;
+}
+
+static void i3c_master_bus_cleanup(FAR struct i3c_master_controller *master)
+{
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+ i3c_master_detach_free_devs(master);
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_search_i3c_dev_duplicate(FAR struct i3c_dev_desc *refdev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(refdev);
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev != refdev && i3cdev->info.pid == refdev->info.pid)
+ {
+ return i3cdev;
+ }
+ }
+
+ return NULL;
+}
+
+static int i3c_master_i2c_adapter_xfer(FAR struct i2c_master_s *i2c,
+ FAR struct i2c_msg_s *xfers, int nxfers)
+{
+ FAR struct i3c_master_controller *master = i2c_adapter_to_i3c_master(i2c);
+ int i;
+ int ret;
+ uint16_t addr;
+
+ if (xfers == NULL || master == NULL || nxfers <= 0)
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->i2c_xfers == NULL)
+ {
+ return -ENOTSUP;
+ }
+
+ /* Doing transfers to different devices is not supported. */
+
+ addr = xfers[0].addr;
+ for (i = 1; i < nxfers; i++)
+ {
+ if (addr != xfers[i].addr)
+ {
+ return -ENOTSUP;
+ }
+ }
+
+ i3c_bus_normaluse_lock(&master->bus);
+ ret = master->ops->i2c_xfers(master, xfers, nxfers);
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ return ret ? ret : nxfers;
+}
+
+static struct i2c_ops_s i3c_master_i2c_algo =
+{
+ .transfer = i3c_master_i2c_adapter_xfer,
+};
+
+static void i3c_master_unregister_i3c_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev->dev == NULL)
+ {
+ continue;
+ }
+
+ i3cdev->dev->desc = NULL;
+ i3cdev->dev->bus = NULL;
+ kmm_free(i3cdev->dev);
+ i3cdev->dev = NULL;
+ }
+}
+
+static void i3c_master_handle_ibi(FAR void *arg)
+{
+ FAR struct i3c_ibi_slot *slot = (struct i3c_ibi_slot *)arg;
+ FAR struct i3c_dev_desc *dev = slot->dev;
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ struct i3c_ibi_payload payload;
+
+ payload.data = slot->data;
+ payload.len = slot->len;
+
+ if (dev->dev)
+ {
+ dev->ibi->handler(dev->dev, &payload);
+ }
+
+ master->ops->recycle_ibi_slot(dev, slot);
+ atomic_fetch_sub(&dev->ibi->pending_ibis, 1);
+ if (!atomic_load(&dev->ibi->pending_ibis))
+ {
+ sem_post(&dev->ibi->all_ibis_handled);
+ }
+}
+
+static void i3c_master_init_ibi_slot(FAR struct i3c_dev_desc *dev,
+ FAR struct i3c_ibi_slot *slot)
+{
+ slot->dev = dev;
+}
+
+static int i3c_master_check_ops(
+ FAR const struct i3c_master_controller_ops *ops)
+{
+ if (ops == NULL || ops->bus_init == NULL || ops->priv_xfers == NULL ||
+ ops->send_ccc_cmd == NULL || ops->do_daa == NULL ||
+ ops->i2c_xfers == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (ops->request_ibi &&
+ (ops->enable_ibi == NULL || ops->disable_ibi == NULL ||
+ ops->free_ibi == NULL || ops->recycle_ibi_slot == NULL))
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i2c_unregister_driver()
+ *
+ * Description:
+ * Perform an I2C driver unregister operation.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ *
+ ****************************************************************************/
+
+static void i2c_unregister_driver(FAR struct i3c_master_controller *master)
+{
+ char devname[12];
+ int i2c_bus_id = i3c_master_to_i2c_bus_number(master);
+
+ snprintf(devname, 12, "/dev/i2c%d", i2c_bus_id);
+ nx_unlink(devname);
+}
+
+/****************************************************************************
+ * Name: i3c_unregister_driver()
+ *
+ * Description:
+ * Perform an I3C driver unregister operation.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ *
+ ****************************************************************************/
+
+static void i3c_unregister_driver(FAR struct i3c_master_controller *master)
+{
+ char devname[12];
+
+ snprintf(devname, 12, "/dev/i3c%d", i3c_master_to_i3c_bus_number(master));
Review Comment:
sizeof
##########
drivers/i3c/master.c:
##########
@@ -0,0 +1,2282 @@
+/****************************************************************************
+ * drivers/i3c/master.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <stdio.h>
+#include <debug.h>
+
+#include <nuttx/list.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/nuttx.h>
+#include <nuttx/mutex.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mm/mm.h>
+#include <nuttx/spinlock.h>
+#include <netinet/in.h>
+
+#include <nuttx/i3c/master.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/i3c/i3c_driver.h>
+#include <nuttx/i3c/device.h>
+#include "internals.h"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_lock
+ *
+ * Description:
+ * Lock the bus for a maintenance operation
+ *
+ * This function takes the bus lock so that no other operations can
+ * occur on the bus. This is needed for all kind of bus maintenance
+ * operation, like
+ * - enabling/disabling slave events
+ * - re-triggering DAA
+ * - changing the dynamic address of a device
+ * - relinquishing mastership
+ *
+ * The reason for this kind of locking is that we don't want drivers
+ * and core logic to rely on I3C device information that could be
+ * changed behind their back.
+ *
+ * Input Parameters:
+ * bus - I3C bus to take the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_unlock
+ *
+ * Description:
+ * Release the bus lock after a maintenance operation.
+ *
+ * Should be called when the bus maintenance operation is done.
+ * See i3c_bus_maintenance_lock() for more details on what these
+ * maintenance operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_to_i3c_master
+ ****************************************************************************/
+
+static FAR struct i3c_master_controller *
+i3c_bus_to_i3c_master(FAR struct i3c_bus *i3cbus)
+{
+ return container_of(i3cbus, struct i3c_master_controller, bus);
+}
+
+static FAR struct i3c_master_controller *
+i2c_adapter_to_i3c_master(FAR struct i2c_master_s *i2c_master)
+{
+ return container_of(i2c_master, struct i3c_master_controller, i2c);
+}
+
+static int i3c_master_to_i2c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i2c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_addr_slot_status
+ ****************************************************************************/
+
+static enum i3c_addr_slot_status
+i3c_bus_get_addr_slot_status(FAR struct i3c_bus *bus, uint16_t addr)
+{
+ int32_t status;
+ int bitpos = addr * 2;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return I3C_ADDR_SLOT_RSVD;
+ }
+
+ status = bus->addrslots[bitpos / I3C_BITS_PER_LONG];
+ status >>= bitpos % I3C_BITS_PER_LONG;
+
+ return status & I3C_ADDR_SLOT_STATUS_MASK;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_addr_slot_status
+ ****************************************************************************/
+
+static void i3c_bus_set_addr_slot_status(FAR struct i3c_bus *bus,
+ uint16_t addr, enum i3c_addr_slot_status status)
+{
+ int bitpos = addr * 2;
+ FAR uint32_t *ptr;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return;
+ }
+
+ ptr = bus->addrslots + (bitpos / I3C_BITS_PER_LONG);
+ *ptr &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK <<
+ (bitpos % I3C_BITS_PER_LONG));
+ *ptr |= (unsigned long)status << (bitpos % I3C_BITS_PER_LONG);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_dev_addr_is_avail
+ ****************************************************************************/
+
+static bool i3c_bus_dev_addr_is_avail(FAR struct i3c_bus *bus,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status status;
+
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ return status == I3C_ADDR_SLOT_FREE;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_free_addr
+ ****************************************************************************/
+
+static int i3c_bus_get_free_addr(FAR struct i3c_bus *bus,
+ unsigned char start_addr)
+{
+ enum i3c_addr_slot_status status;
+ unsigned char addr;
+
+ for (addr = start_addr; addr < I3C_MAX_ADDR; addr++)
+ {
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ if (status == I3C_ADDR_SLOT_FREE)
+ {
+ return addr;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init_addrslots
+ ****************************************************************************/
+
+static void i3c_bus_init_addrslots(FAR struct i3c_bus *bus)
+{
+ int i;
+
+ /* Addresses 0 to 7 are reserved. */
+
+ for (i = 0; i < 8; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, i, I3C_ADDR_SLOT_RSVD);
+ }
+
+ /* Reserve broadcast address and all addresses that might collide
+ * with the broadcast address when facing a single bit error.
+ */
+
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR,
+ I3C_ADDR_SLOT_RSVD);
+ for (i = 0; i < 7; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR ^ I3C_BIT(i),
+ I3C_ADDR_SLOT_RSVD);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init
+ ****************************************************************************/
+
+static void i3c_bus_init(FAR struct i3c_bus *i3cbus)
+{
+ FAR struct i3c_master_controller *master;
+ master = i3c_bus_to_i3c_master(i3cbus);
+
+ nxmutex_init(&i3cbus->lock);
+ list_initialize(&i3cbus->devs.i3c);
+ i3c_bus_init_addrslots(i3cbus);
+ i3cbus->mode = I3C_BUS_MODE_PURE;
+ i3cbus->id = master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_mode
+ ****************************************************************************/
+
+static int i3c_bus_set_mode(FAR struct i3c_bus *i3cbus,
+ enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate)
+{
+ i3cbus->mode = mode;
+
+ switch (i3cbus->mode)
+ {
+ case I3C_BUS_MODE_PURE:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_FAST:
+ case I3C_BUS_MODE_MIXED_LIMITED:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_SLOW:
+ {
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+
+ if (!i3cbus->scl_rate.i3c ||
+ i3cbus->scl_rate.i3c > i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i3c = i3cbus->scl_rate.i2c;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* I3C/I2C frequency may have been overridden, check that user-provided
+ * values are not exceeding max possible frequency.
+ */
+
+ if (i3cbus->scl_rate.i3c > I3C_BUS_MAX_I3C_SCL_RATE ||
+ i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_RATE)
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int i3c_master_to_i3c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_init
+ ****************************************************************************/
+
+static FAR void *i3c_ccc_cmd_dest_init(FAR struct i3c_ccc_cmd_dest *dest,
+ unsigned char addr, uint16_t payloadlen)
+{
+ dest->addr = addr;
+ dest->payload.len = payloadlen;
+ if (payloadlen)
+ {
+ dest->payload.data = kmm_zalloc(payloadlen);
+ }
+ else
+ {
+ dest->payload.data = NULL;
+ }
+
+ return dest->payload.data;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_cleanup
+ ****************************************************************************/
+
+static void i3c_ccc_cmd_dest_cleanup(FAR struct i3c_ccc_cmd_dest *dest)
+{
+ kmm_free(dest->payload.data);
+}
+
+static void i3c_ccc_cmd_init(FAR struct i3c_ccc_cmd *cmd, bool rnw,
+ unsigned char id, FAR struct i3c_ccc_cmd_dest *dests,
+ unsigned int ndests)
+{
+ cmd->rnw = rnw ? 1 : 0;
+ cmd->id = id;
+ cmd->dests = dests;
+ cmd->ndests = ndests;
+ cmd->err = I3C_ERROR_UNKNOWN;
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_alloc_i3c_dev(FAR struct i3c_master_controller *master,
+ FAR const struct i3c_device_info *info)
+{
+ FAR struct i3c_dev_desc *dev;
+
+ dev = kmm_zalloc(sizeof(*dev));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ dev->common.master = master;
+ dev->info = *info;
+ nxmutex_init(&dev->ibi_lock);
+
+ return dev;
+}
+
+static int i3c_master_rstdaa_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status addrstat;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ addrstat = i3c_bus_get_addr_slot_status(&master->bus, addr);
+ if (addr != I3C_BROADCAST_ADDR && addrstat != I3C_ADDR_SLOT_I3C_DEV)
+ {
+ return -EINVAL;
+ }
+
+ i3c_ccc_cmd_dest_init(&dest, addr, 0);
+ i3c_ccc_cmd_init(&cmd, false,
+ I3C_CCC_RSTDAA(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_enec_disec_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char addr, bool enable, unsigned char evts)
+{
+ FAR struct i3c_ccc_events *events;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ events = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*events));
+ if (events == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ events->events = evts;
+ i3c_ccc_cmd_init(&cmd, false,
+ enable ?
+ I3C_CCC_ENEC(addr == I3C_BROADCAST_ADDR) :
+ I3C_CCC_DISEC(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setda_locked(FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr, bool setdasa)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_setda *setda;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (!oldaddr || !newaddr)
+ {
+ return -EINVAL;
+ }
+
+ setda = i3c_ccc_cmd_dest_init(&dest, oldaddr, sizeof(*setda));
+ if (setda == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ setda->addr = newaddr << 1;
+ i3c_ccc_cmd_init(&cmd, false,
+ setdasa ? I3C_CCC_SETDASA : I3C_CCC_SETNEWDA,
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setnewda_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr)
+{
+ return i3c_master_setda_locked(master, oldaddr, newaddr, false);
+}
+
+static int i3c_master_getmrl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mrl *mrl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mrl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mrl));
+ if (mrl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ /* When the device does not have IBI payload GETMRL only returns 2
+ * bytes of data.
+ */
+
+ if (!(info->bcr & I3C_BCR_IBI_PAYLOAD))
+ {
+ dest.payload.len -= 1;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMRL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ switch (dest.payload.len)
+ {
+ case 3:
+ {
+ info->max_ibi_len = mrl->ibi_len;
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ case 2:
+ {
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ default:
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmwl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mwl *mwl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mwl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mwl));
+ if (mwl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMWL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != sizeof(*mwl))
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_write_len = ntohs(mwl->len);
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmxds_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getmxds *getmaxds;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getmaxds = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*getmaxds));
+ if (getmaxds == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 2 && dest.payload.len != 5)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_read_ds = getmaxds->maxrd;
+ info->max_write_ds = getmaxds->maxwr;
+ if (dest.payload.len == 5)
+ {
+ info->max_read_turnaround = getmaxds->maxrdturn[0] |
+ ((uint32_t)getmaxds->maxrdturn[1] << 8) |
+ ((uint32_t)getmaxds->maxrdturn[2] << 16);
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_gethdrcap_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_gethdrcap *gethdrcap;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ gethdrcap = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*gethdrcap));
+ if (gethdrcap == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETHDRCAP, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 1)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->hdr_cap = gethdrcap->modes;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getpid_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getpid *getpid;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+ int i;
+
+ getpid = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getpid));
+ if (getpid == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETPID, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->pid = 0;
+ for (i = 0; i < sizeof(getpid->pid); i++)
+ {
+ int sft = (sizeof(getpid->pid) - i - 1) * 8;
+ info->pid |= (uint64_t)getpid->pid[i] << sft;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getbcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getbcr *getbcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getbcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getbcr));
+ if (getbcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETBCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->bcr = getbcr->bcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getdcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getdcr *getdcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getdcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getdcr));
+ if (getdcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETDCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->dcr = getdcr->dcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_retrieve_dev_info(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status slot_status;
+ int ret;
+
+ if (!dev->info.dyn_addr)
+ {
+ return -EINVAL;
+ }
+
+ slot_status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (slot_status == I3C_ADDR_SLOT_RSVD)
+ {
+ return -EINVAL;
+ }
+
+ ret = i3c_master_getpid_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getbcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getdcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ if (dev->info.bcr & I3C_BCR_MAX_DATA_SPEED_LIM)
+ {
+ ret = i3c_master_getmxds_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
+ {
+ dev->info.max_ibi_len = 1;
+ }
+
+ i3c_master_getmrl_locked(master, &dev->info);
+ i3c_master_getmwl_locked(master, &dev->info);
+
+ if (dev->info.bcr & I3C_BCR_HDR_CAP)
+ {
+ ret = i3c_master_gethdrcap_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_put_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ if (dev->info.dyn_addr)
+ {
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_FREE);
+ }
+}
+
+static int i3c_master_get_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ /* ->init_dyn_addr should have been reserved before that, so, if we're
+ * trying to apply a pre-reserved dynamic address, we should not try
+ * to reserve the address slot a second time.
+ */
+
+ if (dev->info.dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ return 0;
+}
+
+static int i3c_master_attach_i3c_dev(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_dev_desc *dev)
+{
+ int ret;
+
+ /* We don't attach devices to the controller until they are
+ * addressable on the bus.
+ */
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ ret = i3c_master_get_i3c_addrs(dev);
+ if (ret)
+ {
+ return ret;
+ }
+
+ /* Do not attach the master device itself. */
+
+ if (master->this != dev && master->ops->attach_i3c_dev)
+ {
+ ret = master->ops->attach_i3c_dev(dev);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ list_add_tail(&master->bus.devs.i3c, &dev->common.node);
+
+ return 0;
+}
+
+static int i3c_master_reattach_i3c_dev(FAR struct i3c_dev_desc *dev,
+ unsigned char old_dyn_addr)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+ int ret;
+
+ if (dev->info.dyn_addr != old_dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus,
+ dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ if (master->ops->reattach_i3c_dev)
+ {
+ ret = master->ops->reattach_i3c_dev(dev, old_dyn_addr);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_detach_i3c_dev(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ /* Do not detach the master device itself. */
+
+ if (master->this != dev && master->ops->detach_i3c_dev)
+ {
+ master->ops->detach_i3c_dev(dev);
+ }
+
+ i3c_master_put_i3c_addrs(dev);
+ list_delete(&dev->common.node);
+}
+
+static void
+i3c_master_register_new_i3c_devs(FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *desc;
+
+ if (!master->init_done)
+ {
+ return;
+ }
+
+ i3c_bus_for_each_i3cdev(&master->bus, desc)
+ {
+ if (desc->dev || !desc->info.dyn_addr || desc == master->this)
+ {
+ continue;
+ }
+
+ desc->dev = kmm_zalloc(sizeof(*desc->dev));
+ if (desc->dev == NULL)
+ {
+ continue;
+ }
+
+ desc->dev->bus = &master->bus;
+ desc->dev->desc = desc;
+ }
+}
+
+static void i3c_master_detach_free_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+ FAR struct i3c_dev_desc *i3ctmp;
+
+ list_for_every_entry_safe(&master->bus.devs.i3c, i3cdev, i3ctmp,
+ struct i3c_dev_desc, common.node)
+ {
+ i3c_master_detach_i3c_dev(i3cdev);
+ kmm_free(i3cdev);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_master_bus_init()
+ *
+ * Description:
+ * Initialize an I3C bus
+ *
+ * This function is following all initialisation steps described in the I3C
+ * specification:
+ *
+ * 1. Attach I2C devs to the master so that the master can fill its
+ * internal device table appropriately
+ *
+ * 2. Call &i3c_master_controller_ops->bus_init() method to initialize
+ * the master controller. That's usually where the bus mode is selected
+ * (pure bus or mixed fast/slow bus)
+ *
+ * 3. Instruct all devices on the bus to drop their dynamic address. This
+ * is particularly important when the bus was previously configured by
+ * someone else (for example the bootloader)
+ *
+ * 4. Disable all slave events.
+ *
+ * 5. Reserve address slots for I3C devices with init_dyn_addr. And if
+ * devices also have static_addr, try to pre-assign dynamic addresses
+ * requested by the FW with SETDASA and attach corresponding statically
+ * defined I3C devices to the master.
+ *
+ * 6. Do a DAA (Dynamic Address Assignment) to assign dynamic addresses to
+ * all remaining I3C devices
+ *
+ * Once this is done, all I3C and I2C devices should be usable.
+ *
+ * Input Parameters:
+ * master - main master initializing the bus
+ *
+ * Returned Value:
+ * A 0 in case of success, an negative error code otherwise.
+ ****************************************************************************/
+
+static int i3c_master_bus_init(FAR struct i3c_master_controller *master)
+{
+ int ret;
+
+ /* Now execute the controller specific ->bus_init() routine, which
+ * might configure its internal logic to match the bus limitations.
+ */
+
+ ret = master->ops->bus_init(master);
+ if (ret)
+ {
+ goto err_detach_devs;
+ }
+
+ /* The master device should have been instantiated in ->bus_init(),
+ * complain if this was not the case.
+ */
+
+ if (master->this == NULL)
+ {
+ i3cerr("master_set_info() was not called in ->bus_init()\n");
+ ret = -EINVAL;
+ goto err_bus_cleanup;
+ }
+
+ /* Reset all dynamic address that may have been assigned before
+ * (assigned by the bootloader for example).
+ */
+
+ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_rstdaa_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Disable all slave events before starting DAA. */
+
+ ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
+ I3C_CCC_EVENT_HJ);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_disec_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Reserve init_dyn_addr first, and then try to pre-assign dynamic
+ * address and retrieve device information if needed.
+ * In case pre-assign dynamic address fails, setting dynamic address to
+ * the requested init_dyn_addr is retried after DAA is done in
+ * i3c_master_add_i3c_dev_locked().
+ */
+
+ ret = i3c_master_do_daa(master);
+ if (ret)
+ {
+ i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ i3cerr("master_do_daa failed:%d (no device)\n", ret);
+ }
+
+ return 0;
+
+err_bus_cleanup:
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+err_detach_devs:
+ i3c_master_detach_free_devs(master);
+
+ return ret;
+}
+
+static void i3c_master_bus_cleanup(FAR struct i3c_master_controller *master)
+{
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+ i3c_master_detach_free_devs(master);
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_search_i3c_dev_duplicate(FAR struct i3c_dev_desc *refdev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(refdev);
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev != refdev && i3cdev->info.pid == refdev->info.pid)
+ {
+ return i3cdev;
+ }
+ }
+
+ return NULL;
+}
+
+static int i3c_master_i2c_adapter_xfer(FAR struct i2c_master_s *i2c,
+ FAR struct i2c_msg_s *xfers, int nxfers)
+{
+ FAR struct i3c_master_controller *master = i2c_adapter_to_i3c_master(i2c);
+ int i;
+ int ret;
+ uint16_t addr;
+
+ if (xfers == NULL || master == NULL || nxfers <= 0)
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->i2c_xfers == NULL)
+ {
+ return -ENOTSUP;
+ }
+
+ /* Doing transfers to different devices is not supported. */
+
+ addr = xfers[0].addr;
+ for (i = 1; i < nxfers; i++)
+ {
+ if (addr != xfers[i].addr)
+ {
+ return -ENOTSUP;
+ }
+ }
+
+ i3c_bus_normaluse_lock(&master->bus);
+ ret = master->ops->i2c_xfers(master, xfers, nxfers);
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ return ret ? ret : nxfers;
+}
+
+static struct i2c_ops_s i3c_master_i2c_algo =
+{
+ .transfer = i3c_master_i2c_adapter_xfer,
+};
+
+static void i3c_master_unregister_i3c_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev->dev == NULL)
+ {
+ continue;
+ }
+
+ i3cdev->dev->desc = NULL;
+ i3cdev->dev->bus = NULL;
+ kmm_free(i3cdev->dev);
+ i3cdev->dev = NULL;
+ }
+}
+
+static void i3c_master_handle_ibi(FAR void *arg)
+{
+ FAR struct i3c_ibi_slot *slot = (struct i3c_ibi_slot *)arg;
+ FAR struct i3c_dev_desc *dev = slot->dev;
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ struct i3c_ibi_payload payload;
+
+ payload.data = slot->data;
+ payload.len = slot->len;
+
+ if (dev->dev)
+ {
+ dev->ibi->handler(dev->dev, &payload);
+ }
+
+ master->ops->recycle_ibi_slot(dev, slot);
+ atomic_fetch_sub(&dev->ibi->pending_ibis, 1);
+ if (!atomic_load(&dev->ibi->pending_ibis))
+ {
+ sem_post(&dev->ibi->all_ibis_handled);
+ }
+}
+
+static void i3c_master_init_ibi_slot(FAR struct i3c_dev_desc *dev,
+ FAR struct i3c_ibi_slot *slot)
+{
+ slot->dev = dev;
+}
+
+static int i3c_master_check_ops(
+ FAR const struct i3c_master_controller_ops *ops)
+{
+ if (ops == NULL || ops->bus_init == NULL || ops->priv_xfers == NULL ||
+ ops->send_ccc_cmd == NULL || ops->do_daa == NULL ||
+ ops->i2c_xfers == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (ops->request_ibi &&
+ (ops->enable_ibi == NULL || ops->disable_ibi == NULL ||
+ ops->free_ibi == NULL || ops->recycle_ibi_slot == NULL))
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i2c_unregister_driver()
+ *
+ * Description:
+ * Perform an I2C driver unregister operation.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ *
+ ****************************************************************************/
+
+static void i2c_unregister_driver(FAR struct i3c_master_controller *master)
+{
+ char devname[12];
+ int i2c_bus_id = i3c_master_to_i2c_bus_number(master);
+
+ snprintf(devname, 12, "/dev/i2c%d", i2c_bus_id);
Review Comment:
```suggestion
snprintf(devname, sizeof(devname), "/dev/i2c%d", i2c_bus_id);
```
##########
drivers/i3c/master.c:
##########
@@ -0,0 +1,2282 @@
+/****************************************************************************
+ * drivers/i3c/master.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <stdio.h>
+#include <debug.h>
+
+#include <nuttx/list.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/nuttx.h>
+#include <nuttx/mutex.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mm/mm.h>
+#include <nuttx/spinlock.h>
+#include <netinet/in.h>
+
+#include <nuttx/i3c/master.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/i3c/i3c_driver.h>
+#include <nuttx/i3c/device.h>
+#include "internals.h"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_lock
+ *
+ * Description:
+ * Lock the bus for a maintenance operation
+ *
+ * This function takes the bus lock so that no other operations can
+ * occur on the bus. This is needed for all kind of bus maintenance
+ * operation, like
+ * - enabling/disabling slave events
+ * - re-triggering DAA
+ * - changing the dynamic address of a device
+ * - relinquishing mastership
+ *
+ * The reason for this kind of locking is that we don't want drivers
+ * and core logic to rely on I3C device information that could be
+ * changed behind their back.
+ *
+ * Input Parameters:
+ * bus - I3C bus to take the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_unlock
+ *
+ * Description:
+ * Release the bus lock after a maintenance operation.
+ *
+ * Should be called when the bus maintenance operation is done.
+ * See i3c_bus_maintenance_lock() for more details on what these
+ * maintenance operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_to_i3c_master
+ ****************************************************************************/
+
+static FAR struct i3c_master_controller *
+i3c_bus_to_i3c_master(FAR struct i3c_bus *i3cbus)
+{
+ return container_of(i3cbus, struct i3c_master_controller, bus);
+}
+
+static FAR struct i3c_master_controller *
+i2c_adapter_to_i3c_master(FAR struct i2c_master_s *i2c_master)
+{
+ return container_of(i2c_master, struct i3c_master_controller, i2c);
+}
+
+static int i3c_master_to_i2c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i2c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_addr_slot_status
+ ****************************************************************************/
+
+static enum i3c_addr_slot_status
+i3c_bus_get_addr_slot_status(FAR struct i3c_bus *bus, uint16_t addr)
+{
+ int32_t status;
+ int bitpos = addr * 2;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return I3C_ADDR_SLOT_RSVD;
+ }
+
+ status = bus->addrslots[bitpos / I3C_BITS_PER_LONG];
+ status >>= bitpos % I3C_BITS_PER_LONG;
+
+ return status & I3C_ADDR_SLOT_STATUS_MASK;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_addr_slot_status
+ ****************************************************************************/
+
+static void i3c_bus_set_addr_slot_status(FAR struct i3c_bus *bus,
+ uint16_t addr, enum i3c_addr_slot_status status)
+{
+ int bitpos = addr * 2;
+ FAR uint32_t *ptr;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return;
+ }
+
+ ptr = bus->addrslots + (bitpos / I3C_BITS_PER_LONG);
+ *ptr &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK <<
+ (bitpos % I3C_BITS_PER_LONG));
+ *ptr |= (unsigned long)status << (bitpos % I3C_BITS_PER_LONG);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_dev_addr_is_avail
+ ****************************************************************************/
+
+static bool i3c_bus_dev_addr_is_avail(FAR struct i3c_bus *bus,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status status;
+
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ return status == I3C_ADDR_SLOT_FREE;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_free_addr
+ ****************************************************************************/
+
+static int i3c_bus_get_free_addr(FAR struct i3c_bus *bus,
+ unsigned char start_addr)
+{
+ enum i3c_addr_slot_status status;
+ unsigned char addr;
+
+ for (addr = start_addr; addr < I3C_MAX_ADDR; addr++)
+ {
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ if (status == I3C_ADDR_SLOT_FREE)
+ {
+ return addr;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init_addrslots
+ ****************************************************************************/
+
+static void i3c_bus_init_addrslots(FAR struct i3c_bus *bus)
+{
+ int i;
+
+ /* Addresses 0 to 7 are reserved. */
+
+ for (i = 0; i < 8; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, i, I3C_ADDR_SLOT_RSVD);
+ }
+
+ /* Reserve broadcast address and all addresses that might collide
+ * with the broadcast address when facing a single bit error.
+ */
+
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR,
+ I3C_ADDR_SLOT_RSVD);
+ for (i = 0; i < 7; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR ^ I3C_BIT(i),
+ I3C_ADDR_SLOT_RSVD);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init
+ ****************************************************************************/
+
+static void i3c_bus_init(FAR struct i3c_bus *i3cbus)
+{
+ FAR struct i3c_master_controller *master;
+ master = i3c_bus_to_i3c_master(i3cbus);
+
+ nxmutex_init(&i3cbus->lock);
+ list_initialize(&i3cbus->devs.i3c);
+ i3c_bus_init_addrslots(i3cbus);
+ i3cbus->mode = I3C_BUS_MODE_PURE;
+ i3cbus->id = master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_mode
+ ****************************************************************************/
+
+static int i3c_bus_set_mode(FAR struct i3c_bus *i3cbus,
+ enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate)
+{
+ i3cbus->mode = mode;
+
+ switch (i3cbus->mode)
+ {
+ case I3C_BUS_MODE_PURE:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_FAST:
+ case I3C_BUS_MODE_MIXED_LIMITED:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_SLOW:
+ {
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+
+ if (!i3cbus->scl_rate.i3c ||
+ i3cbus->scl_rate.i3c > i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i3c = i3cbus->scl_rate.i2c;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* I3C/I2C frequency may have been overridden, check that user-provided
+ * values are not exceeding max possible frequency.
+ */
+
+ if (i3cbus->scl_rate.i3c > I3C_BUS_MAX_I3C_SCL_RATE ||
+ i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_RATE)
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int i3c_master_to_i3c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_init
+ ****************************************************************************/
+
+static FAR void *i3c_ccc_cmd_dest_init(FAR struct i3c_ccc_cmd_dest *dest,
+ unsigned char addr, uint16_t payloadlen)
+{
+ dest->addr = addr;
+ dest->payload.len = payloadlen;
+ if (payloadlen)
+ {
+ dest->payload.data = kmm_zalloc(payloadlen);
+ }
+ else
+ {
+ dest->payload.data = NULL;
+ }
+
+ return dest->payload.data;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_cleanup
+ ****************************************************************************/
+
+static void i3c_ccc_cmd_dest_cleanup(FAR struct i3c_ccc_cmd_dest *dest)
+{
+ kmm_free(dest->payload.data);
+}
+
+static void i3c_ccc_cmd_init(FAR struct i3c_ccc_cmd *cmd, bool rnw,
+ unsigned char id, FAR struct i3c_ccc_cmd_dest *dests,
+ unsigned int ndests)
+{
+ cmd->rnw = rnw ? 1 : 0;
+ cmd->id = id;
+ cmd->dests = dests;
+ cmd->ndests = ndests;
+ cmd->err = I3C_ERROR_UNKNOWN;
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_alloc_i3c_dev(FAR struct i3c_master_controller *master,
+ FAR const struct i3c_device_info *info)
+{
+ FAR struct i3c_dev_desc *dev;
+
+ dev = kmm_zalloc(sizeof(*dev));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ dev->common.master = master;
+ dev->info = *info;
+ nxmutex_init(&dev->ibi_lock);
+
+ return dev;
+}
+
+static int i3c_master_rstdaa_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status addrstat;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ addrstat = i3c_bus_get_addr_slot_status(&master->bus, addr);
+ if (addr != I3C_BROADCAST_ADDR && addrstat != I3C_ADDR_SLOT_I3C_DEV)
+ {
+ return -EINVAL;
+ }
+
+ i3c_ccc_cmd_dest_init(&dest, addr, 0);
+ i3c_ccc_cmd_init(&cmd, false,
+ I3C_CCC_RSTDAA(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_enec_disec_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char addr, bool enable, unsigned char evts)
+{
+ FAR struct i3c_ccc_events *events;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ events = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*events));
+ if (events == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ events->events = evts;
+ i3c_ccc_cmd_init(&cmd, false,
+ enable ?
+ I3C_CCC_ENEC(addr == I3C_BROADCAST_ADDR) :
+ I3C_CCC_DISEC(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setda_locked(FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr, bool setdasa)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_setda *setda;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (!oldaddr || !newaddr)
+ {
+ return -EINVAL;
+ }
+
+ setda = i3c_ccc_cmd_dest_init(&dest, oldaddr, sizeof(*setda));
+ if (setda == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ setda->addr = newaddr << 1;
+ i3c_ccc_cmd_init(&cmd, false,
+ setdasa ? I3C_CCC_SETDASA : I3C_CCC_SETNEWDA,
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setnewda_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr)
+{
+ return i3c_master_setda_locked(master, oldaddr, newaddr, false);
+}
+
+static int i3c_master_getmrl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mrl *mrl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mrl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mrl));
+ if (mrl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ /* When the device does not have IBI payload GETMRL only returns 2
+ * bytes of data.
+ */
+
+ if (!(info->bcr & I3C_BCR_IBI_PAYLOAD))
+ {
+ dest.payload.len -= 1;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMRL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ switch (dest.payload.len)
+ {
+ case 3:
+ {
+ info->max_ibi_len = mrl->ibi_len;
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ case 2:
+ {
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ default:
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmwl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mwl *mwl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mwl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mwl));
+ if (mwl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMWL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != sizeof(*mwl))
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_write_len = ntohs(mwl->len);
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmxds_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getmxds *getmaxds;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getmaxds = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*getmaxds));
+ if (getmaxds == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 2 && dest.payload.len != 5)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_read_ds = getmaxds->maxrd;
+ info->max_write_ds = getmaxds->maxwr;
+ if (dest.payload.len == 5)
+ {
+ info->max_read_turnaround = getmaxds->maxrdturn[0] |
+ ((uint32_t)getmaxds->maxrdturn[1] << 8) |
+ ((uint32_t)getmaxds->maxrdturn[2] << 16);
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_gethdrcap_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_gethdrcap *gethdrcap;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ gethdrcap = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*gethdrcap));
+ if (gethdrcap == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETHDRCAP, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 1)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->hdr_cap = gethdrcap->modes;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getpid_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getpid *getpid;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+ int i;
+
+ getpid = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getpid));
+ if (getpid == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETPID, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->pid = 0;
+ for (i = 0; i < sizeof(getpid->pid); i++)
+ {
+ int sft = (sizeof(getpid->pid) - i - 1) * 8;
+ info->pid |= (uint64_t)getpid->pid[i] << sft;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getbcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getbcr *getbcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getbcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getbcr));
+ if (getbcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETBCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->bcr = getbcr->bcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getdcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getdcr *getdcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getdcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getdcr));
+ if (getdcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETDCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->dcr = getdcr->dcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_retrieve_dev_info(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status slot_status;
+ int ret;
+
+ if (!dev->info.dyn_addr)
+ {
+ return -EINVAL;
+ }
+
+ slot_status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (slot_status == I3C_ADDR_SLOT_RSVD)
+ {
+ return -EINVAL;
+ }
+
+ ret = i3c_master_getpid_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getbcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getdcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ if (dev->info.bcr & I3C_BCR_MAX_DATA_SPEED_LIM)
+ {
+ ret = i3c_master_getmxds_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
+ {
+ dev->info.max_ibi_len = 1;
+ }
+
+ i3c_master_getmrl_locked(master, &dev->info);
+ i3c_master_getmwl_locked(master, &dev->info);
+
+ if (dev->info.bcr & I3C_BCR_HDR_CAP)
+ {
+ ret = i3c_master_gethdrcap_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_put_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ if (dev->info.dyn_addr)
+ {
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_FREE);
+ }
+}
+
+static int i3c_master_get_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ /* ->init_dyn_addr should have been reserved before that, so, if we're
+ * trying to apply a pre-reserved dynamic address, we should not try
+ * to reserve the address slot a second time.
+ */
+
+ if (dev->info.dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ return 0;
+}
+
+static int i3c_master_attach_i3c_dev(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_dev_desc *dev)
+{
+ int ret;
+
+ /* We don't attach devices to the controller until they are
+ * addressable on the bus.
+ */
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ ret = i3c_master_get_i3c_addrs(dev);
+ if (ret)
+ {
+ return ret;
+ }
+
+ /* Do not attach the master device itself. */
+
+ if (master->this != dev && master->ops->attach_i3c_dev)
+ {
+ ret = master->ops->attach_i3c_dev(dev);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ list_add_tail(&master->bus.devs.i3c, &dev->common.node);
+
+ return 0;
+}
+
+static int i3c_master_reattach_i3c_dev(FAR struct i3c_dev_desc *dev,
+ unsigned char old_dyn_addr)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+ int ret;
+
+ if (dev->info.dyn_addr != old_dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus,
+ dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ if (master->ops->reattach_i3c_dev)
+ {
+ ret = master->ops->reattach_i3c_dev(dev, old_dyn_addr);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_detach_i3c_dev(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ /* Do not detach the master device itself. */
+
+ if (master->this != dev && master->ops->detach_i3c_dev)
+ {
+ master->ops->detach_i3c_dev(dev);
+ }
+
+ i3c_master_put_i3c_addrs(dev);
+ list_delete(&dev->common.node);
+}
+
+static void
+i3c_master_register_new_i3c_devs(FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *desc;
+
+ if (!master->init_done)
+ {
+ return;
+ }
+
+ i3c_bus_for_each_i3cdev(&master->bus, desc)
+ {
+ if (desc->dev || !desc->info.dyn_addr || desc == master->this)
+ {
+ continue;
+ }
+
+ desc->dev = kmm_zalloc(sizeof(*desc->dev));
+ if (desc->dev == NULL)
+ {
+ continue;
+ }
+
+ desc->dev->bus = &master->bus;
+ desc->dev->desc = desc;
+ }
+}
+
+static void i3c_master_detach_free_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+ FAR struct i3c_dev_desc *i3ctmp;
+
+ list_for_every_entry_safe(&master->bus.devs.i3c, i3cdev, i3ctmp,
+ struct i3c_dev_desc, common.node)
+ {
+ i3c_master_detach_i3c_dev(i3cdev);
+ kmm_free(i3cdev);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_master_bus_init()
+ *
+ * Description:
+ * Initialize an I3C bus
+ *
+ * This function is following all initialisation steps described in the I3C
+ * specification:
+ *
+ * 1. Attach I2C devs to the master so that the master can fill its
+ * internal device table appropriately
+ *
+ * 2. Call &i3c_master_controller_ops->bus_init() method to initialize
+ * the master controller. That's usually where the bus mode is selected
+ * (pure bus or mixed fast/slow bus)
+ *
+ * 3. Instruct all devices on the bus to drop their dynamic address. This
+ * is particularly important when the bus was previously configured by
+ * someone else (for example the bootloader)
+ *
+ * 4. Disable all slave events.
+ *
+ * 5. Reserve address slots for I3C devices with init_dyn_addr. And if
+ * devices also have static_addr, try to pre-assign dynamic addresses
+ * requested by the FW with SETDASA and attach corresponding statically
+ * defined I3C devices to the master.
+ *
+ * 6. Do a DAA (Dynamic Address Assignment) to assign dynamic addresses to
+ * all remaining I3C devices
+ *
+ * Once this is done, all I3C and I2C devices should be usable.
+ *
+ * Input Parameters:
+ * master - main master initializing the bus
+ *
+ * Returned Value:
+ * A 0 in case of success, an negative error code otherwise.
+ ****************************************************************************/
+
+static int i3c_master_bus_init(FAR struct i3c_master_controller *master)
+{
+ int ret;
+
+ /* Now execute the controller specific ->bus_init() routine, which
+ * might configure its internal logic to match the bus limitations.
+ */
+
+ ret = master->ops->bus_init(master);
+ if (ret)
+ {
+ goto err_detach_devs;
+ }
+
+ /* The master device should have been instantiated in ->bus_init(),
+ * complain if this was not the case.
+ */
+
+ if (master->this == NULL)
+ {
+ i3cerr("master_set_info() was not called in ->bus_init()\n");
+ ret = -EINVAL;
+ goto err_bus_cleanup;
+ }
+
+ /* Reset all dynamic address that may have been assigned before
+ * (assigned by the bootloader for example).
+ */
+
+ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_rstdaa_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Disable all slave events before starting DAA. */
+
+ ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
+ I3C_CCC_EVENT_HJ);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_disec_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Reserve init_dyn_addr first, and then try to pre-assign dynamic
+ * address and retrieve device information if needed.
+ * In case pre-assign dynamic address fails, setting dynamic address to
+ * the requested init_dyn_addr is retried after DAA is done in
+ * i3c_master_add_i3c_dev_locked().
+ */
+
+ ret = i3c_master_do_daa(master);
+ if (ret)
+ {
+ i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ i3cerr("master_do_daa failed:%d (no device)\n", ret);
+ }
+
+ return 0;
+
+err_bus_cleanup:
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+err_detach_devs:
+ i3c_master_detach_free_devs(master);
+
+ return ret;
+}
+
+static void i3c_master_bus_cleanup(FAR struct i3c_master_controller *master)
+{
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+ i3c_master_detach_free_devs(master);
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_search_i3c_dev_duplicate(FAR struct i3c_dev_desc *refdev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(refdev);
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev != refdev && i3cdev->info.pid == refdev->info.pid)
+ {
+ return i3cdev;
+ }
+ }
+
+ return NULL;
+}
+
+static int i3c_master_i2c_adapter_xfer(FAR struct i2c_master_s *i2c,
+ FAR struct i2c_msg_s *xfers, int nxfers)
+{
+ FAR struct i3c_master_controller *master = i2c_adapter_to_i3c_master(i2c);
+ int i;
+ int ret;
+ uint16_t addr;
+
+ if (xfers == NULL || master == NULL || nxfers <= 0)
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->i2c_xfers == NULL)
+ {
+ return -ENOTSUP;
+ }
+
+ /* Doing transfers to different devices is not supported. */
+
+ addr = xfers[0].addr;
+ for (i = 1; i < nxfers; i++)
+ {
+ if (addr != xfers[i].addr)
+ {
+ return -ENOTSUP;
+ }
+ }
+
+ i3c_bus_normaluse_lock(&master->bus);
+ ret = master->ops->i2c_xfers(master, xfers, nxfers);
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ return ret ? ret : nxfers;
+}
+
+static struct i2c_ops_s i3c_master_i2c_algo =
+{
+ .transfer = i3c_master_i2c_adapter_xfer,
+};
+
+static void i3c_master_unregister_i3c_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev->dev == NULL)
+ {
+ continue;
+ }
+
+ i3cdev->dev->desc = NULL;
+ i3cdev->dev->bus = NULL;
+ kmm_free(i3cdev->dev);
+ i3cdev->dev = NULL;
+ }
+}
+
+static void i3c_master_handle_ibi(FAR void *arg)
+{
+ FAR struct i3c_ibi_slot *slot = (struct i3c_ibi_slot *)arg;
+ FAR struct i3c_dev_desc *dev = slot->dev;
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ struct i3c_ibi_payload payload;
+
+ payload.data = slot->data;
+ payload.len = slot->len;
+
+ if (dev->dev)
+ {
+ dev->ibi->handler(dev->dev, &payload);
+ }
+
+ master->ops->recycle_ibi_slot(dev, slot);
+ atomic_fetch_sub(&dev->ibi->pending_ibis, 1);
+ if (!atomic_load(&dev->ibi->pending_ibis))
+ {
+ sem_post(&dev->ibi->all_ibis_handled);
+ }
+}
+
+static void i3c_master_init_ibi_slot(FAR struct i3c_dev_desc *dev,
+ FAR struct i3c_ibi_slot *slot)
+{
+ slot->dev = dev;
+}
+
+static int i3c_master_check_ops(
+ FAR const struct i3c_master_controller_ops *ops)
+{
+ if (ops == NULL || ops->bus_init == NULL || ops->priv_xfers == NULL ||
+ ops->send_ccc_cmd == NULL || ops->do_daa == NULL ||
+ ops->i2c_xfers == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (ops->request_ibi &&
+ (ops->enable_ibi == NULL || ops->disable_ibi == NULL ||
+ ops->free_ibi == NULL || ops->recycle_ibi_slot == NULL))
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i2c_unregister_driver()
Review Comment:
```suggestion
* Name: i2c_unregister_driver
```
##########
drivers/i3c/master.c:
##########
@@ -0,0 +1,2282 @@
+/****************************************************************************
+ * drivers/i3c/master.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <errno.h>
+#include <stdio.h>
+#include <debug.h>
+
+#include <nuttx/list.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/nuttx.h>
+#include <nuttx/mutex.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mm/mm.h>
+#include <nuttx/spinlock.h>
+#include <netinet/in.h>
+
+#include <nuttx/i3c/master.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/i3c/i3c_driver.h>
+#include <nuttx/i3c/device.h>
+#include "internals.h"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_lock
+ *
+ * Description:
+ * Lock the bus for a maintenance operation
+ *
+ * This function takes the bus lock so that no other operations can
+ * occur on the bus. This is needed for all kind of bus maintenance
+ * operation, like
+ * - enabling/disabling slave events
+ * - re-triggering DAA
+ * - changing the dynamic address of a device
+ * - relinquishing mastership
+ *
+ * The reason for this kind of locking is that we don't want drivers
+ * and core logic to rely on I3C device information that could be
+ * changed behind their back.
+ *
+ * Input Parameters:
+ * bus - I3C bus to take the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_maintenance_unlock
+ *
+ * Description:
+ * Release the bus lock after a maintenance operation.
+ *
+ * Should be called when the bus maintenance operation is done.
+ * See i3c_bus_maintenance_lock() for more details on what these
+ * maintenance operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+static void i3c_bus_maintenance_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_to_i3c_master
+ ****************************************************************************/
+
+static FAR struct i3c_master_controller *
+i3c_bus_to_i3c_master(FAR struct i3c_bus *i3cbus)
+{
+ return container_of(i3cbus, struct i3c_master_controller, bus);
+}
+
+static FAR struct i3c_master_controller *
+i2c_adapter_to_i3c_master(FAR struct i2c_master_s *i2c_master)
+{
+ return container_of(i2c_master, struct i3c_master_controller, i2c);
+}
+
+static int i3c_master_to_i2c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i2c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_addr_slot_status
+ ****************************************************************************/
+
+static enum i3c_addr_slot_status
+i3c_bus_get_addr_slot_status(FAR struct i3c_bus *bus, uint16_t addr)
+{
+ int32_t status;
+ int bitpos = addr * 2;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return I3C_ADDR_SLOT_RSVD;
+ }
+
+ status = bus->addrslots[bitpos / I3C_BITS_PER_LONG];
+ status >>= bitpos % I3C_BITS_PER_LONG;
+
+ return status & I3C_ADDR_SLOT_STATUS_MASK;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_addr_slot_status
+ ****************************************************************************/
+
+static void i3c_bus_set_addr_slot_status(FAR struct i3c_bus *bus,
+ uint16_t addr, enum i3c_addr_slot_status status)
+{
+ int bitpos = addr * 2;
+ FAR uint32_t *ptr;
+
+ if (addr > I3C_I2C_MAX_ADDR)
+ {
+ return;
+ }
+
+ ptr = bus->addrslots + (bitpos / I3C_BITS_PER_LONG);
+ *ptr &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK <<
+ (bitpos % I3C_BITS_PER_LONG));
+ *ptr |= (unsigned long)status << (bitpos % I3C_BITS_PER_LONG);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_dev_addr_is_avail
+ ****************************************************************************/
+
+static bool i3c_bus_dev_addr_is_avail(FAR struct i3c_bus *bus,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status status;
+
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ return status == I3C_ADDR_SLOT_FREE;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_get_free_addr
+ ****************************************************************************/
+
+static int i3c_bus_get_free_addr(FAR struct i3c_bus *bus,
+ unsigned char start_addr)
+{
+ enum i3c_addr_slot_status status;
+ unsigned char addr;
+
+ for (addr = start_addr; addr < I3C_MAX_ADDR; addr++)
+ {
+ status = i3c_bus_get_addr_slot_status(bus, addr);
+ if (status == I3C_ADDR_SLOT_FREE)
+ {
+ return addr;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init_addrslots
+ ****************************************************************************/
+
+static void i3c_bus_init_addrslots(FAR struct i3c_bus *bus)
+{
+ int i;
+
+ /* Addresses 0 to 7 are reserved. */
+
+ for (i = 0; i < 8; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, i, I3C_ADDR_SLOT_RSVD);
+ }
+
+ /* Reserve broadcast address and all addresses that might collide
+ * with the broadcast address when facing a single bit error.
+ */
+
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR,
+ I3C_ADDR_SLOT_RSVD);
+ for (i = 0; i < 7; i++)
+ {
+ i3c_bus_set_addr_slot_status(bus, I3C_BROADCAST_ADDR ^ I3C_BIT(i),
+ I3C_ADDR_SLOT_RSVD);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_bus_init
+ ****************************************************************************/
+
+static void i3c_bus_init(FAR struct i3c_bus *i3cbus)
+{
+ FAR struct i3c_master_controller *master;
+ master = i3c_bus_to_i3c_master(i3cbus);
+
+ nxmutex_init(&i3cbus->lock);
+ list_initialize(&i3cbus->devs.i3c);
+ i3c_bus_init_addrslots(i3cbus);
+ i3cbus->mode = I3C_BUS_MODE_PURE;
+ i3cbus->id = master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_set_mode
+ ****************************************************************************/
+
+static int i3c_bus_set_mode(FAR struct i3c_bus *i3cbus,
+ enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate)
+{
+ i3cbus->mode = mode;
+
+ switch (i3cbus->mode)
+ {
+ case I3C_BUS_MODE_PURE:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_FAST:
+ case I3C_BUS_MODE_MIXED_LIMITED:
+ {
+ if (!i3cbus->scl_rate.i3c)
+ {
+ i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
+ }
+
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+ }
+ break;
+ case I3C_BUS_MODE_MIXED_SLOW:
+ {
+ if (!i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i2c = max_i2c_scl_rate;
+ }
+
+ if (!i3cbus->scl_rate.i3c ||
+ i3cbus->scl_rate.i3c > i3cbus->scl_rate.i2c)
+ {
+ i3cbus->scl_rate.i3c = i3cbus->scl_rate.i2c;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* I3C/I2C frequency may have been overridden, check that user-provided
+ * values are not exceeding max possible frequency.
+ */
+
+ if (i3cbus->scl_rate.i3c > I3C_BUS_MAX_I3C_SCL_RATE ||
+ i3cbus->scl_rate.i2c > I3C_BUS_I2C_FM_PLUS_SCL_RATE)
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int i3c_master_to_i3c_bus_number(
+ FAR struct i3c_master_controller *master)
+{
+ return master->i3c_bus_id;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_init
+ ****************************************************************************/
+
+static FAR void *i3c_ccc_cmd_dest_init(FAR struct i3c_ccc_cmd_dest *dest,
+ unsigned char addr, uint16_t payloadlen)
+{
+ dest->addr = addr;
+ dest->payload.len = payloadlen;
+ if (payloadlen)
+ {
+ dest->payload.data = kmm_zalloc(payloadlen);
+ }
+ else
+ {
+ dest->payload.data = NULL;
+ }
+
+ return dest->payload.data;
+}
+
+/****************************************************************************
+ * Name: i3c_ccc_cmd_dest_cleanup
+ ****************************************************************************/
+
+static void i3c_ccc_cmd_dest_cleanup(FAR struct i3c_ccc_cmd_dest *dest)
+{
+ kmm_free(dest->payload.data);
+}
+
+static void i3c_ccc_cmd_init(FAR struct i3c_ccc_cmd *cmd, bool rnw,
+ unsigned char id, FAR struct i3c_ccc_cmd_dest *dests,
+ unsigned int ndests)
+{
+ cmd->rnw = rnw ? 1 : 0;
+ cmd->id = id;
+ cmd->dests = dests;
+ cmd->ndests = ndests;
+ cmd->err = I3C_ERROR_UNKNOWN;
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_alloc_i3c_dev(FAR struct i3c_master_controller *master,
+ FAR const struct i3c_device_info *info)
+{
+ FAR struct i3c_dev_desc *dev;
+
+ dev = kmm_zalloc(sizeof(*dev));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+
+ dev->common.master = master;
+ dev->info = *info;
+ nxmutex_init(&dev->ibi_lock);
+
+ return dev;
+}
+
+static int i3c_master_rstdaa_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr)
+{
+ enum i3c_addr_slot_status addrstat;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ addrstat = i3c_bus_get_addr_slot_status(&master->bus, addr);
+ if (addr != I3C_BROADCAST_ADDR && addrstat != I3C_ADDR_SLOT_I3C_DEV)
+ {
+ return -EINVAL;
+ }
+
+ i3c_ccc_cmd_dest_init(&dest, addr, 0);
+ i3c_ccc_cmd_init(&cmd, false,
+ I3C_CCC_RSTDAA(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_enec_disec_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char addr, bool enable, unsigned char evts)
+{
+ FAR struct i3c_ccc_events *events;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ events = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*events));
+ if (events == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ events->events = evts;
+ i3c_ccc_cmd_init(&cmd, false,
+ enable ?
+ I3C_CCC_ENEC(addr == I3C_BROADCAST_ADDR) :
+ I3C_CCC_DISEC(addr == I3C_BROADCAST_ADDR),
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setda_locked(FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr, bool setdasa)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_setda *setda;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ if (!oldaddr || !newaddr)
+ {
+ return -EINVAL;
+ }
+
+ setda = i3c_ccc_cmd_dest_init(&dest, oldaddr, sizeof(*setda));
+ if (setda == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ setda->addr = newaddr << 1;
+ i3c_ccc_cmd_init(&cmd, false,
+ setdasa ? I3C_CCC_SETDASA : I3C_CCC_SETNEWDA,
+ &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_setnewda_locked(
+ FAR struct i3c_master_controller *master,
+ unsigned char oldaddr, unsigned char newaddr)
+{
+ return i3c_master_setda_locked(master, oldaddr, newaddr, false);
+}
+
+static int i3c_master_getmrl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mrl *mrl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mrl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mrl));
+ if (mrl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ /* When the device does not have IBI payload GETMRL only returns 2
+ * bytes of data.
+ */
+
+ if (!(info->bcr & I3C_BCR_IBI_PAYLOAD))
+ {
+ dest.payload.len -= 1;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMRL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ switch (dest.payload.len)
+ {
+ case 3:
+ {
+ info->max_ibi_len = mrl->ibi_len;
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ case 2:
+ {
+ info->max_read_len = ntohs(mrl->read_len);
+ }
+ break;
+ default:
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmwl_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ struct i3c_ccc_cmd_dest dest;
+ FAR struct i3c_ccc_mwl *mwl;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ mwl = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*mwl));
+ if (mwl == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMWL, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != sizeof(*mwl))
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_write_len = ntohs(mwl->len);
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getmxds_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getmxds *getmaxds;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getmaxds = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*getmaxds));
+ if (getmaxds == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 2 && dest.payload.len != 5)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->max_read_ds = getmaxds->maxrd;
+ info->max_write_ds = getmaxds->maxwr;
+ if (dest.payload.len == 5)
+ {
+ info->max_read_turnaround = getmaxds->maxrdturn[0] |
+ ((uint32_t)getmaxds->maxrdturn[1] << 8) |
+ ((uint32_t)getmaxds->maxrdturn[2] << 16);
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_gethdrcap_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_gethdrcap *gethdrcap;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ gethdrcap = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr,
+ sizeof(*gethdrcap));
+ if (gethdrcap == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETHDRCAP, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ if (dest.payload.len != 1)
+ {
+ ret = -EIO;
+ goto out;
+ }
+
+ info->hdr_cap = gethdrcap->modes;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getpid_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getpid *getpid;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+ int i;
+
+ getpid = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getpid));
+ if (getpid == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETPID, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->pid = 0;
+ for (i = 0; i < sizeof(getpid->pid); i++)
+ {
+ int sft = (sizeof(getpid->pid) - i - 1) * 8;
+ info->pid |= (uint64_t)getpid->pid[i] << sft;
+ }
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getbcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getbcr *getbcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getbcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getbcr));
+ if (getbcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETBCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->bcr = getbcr->bcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_getdcr_locked(FAR struct i3c_master_controller *master,
+ FAR struct i3c_device_info *info)
+{
+ FAR struct i3c_ccc_getdcr *getdcr;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getdcr = i3c_ccc_cmd_dest_init(&dest, info->dyn_addr, sizeof(*getdcr));
+ if (getdcr == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETDCR, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ {
+ goto out;
+ }
+
+ info->dcr = getdcr->dcr;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_retrieve_dev_info(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status slot_status;
+ int ret;
+
+ if (!dev->info.dyn_addr)
+ {
+ return -EINVAL;
+ }
+
+ slot_status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (slot_status == I3C_ADDR_SLOT_RSVD)
+ {
+ return -EINVAL;
+ }
+
+ ret = i3c_master_getpid_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getbcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ ret = i3c_master_getdcr_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+
+ if (dev->info.bcr & I3C_BCR_MAX_DATA_SPEED_LIM)
+ {
+ ret = i3c_master_getmxds_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
+ {
+ dev->info.max_ibi_len = 1;
+ }
+
+ i3c_master_getmrl_locked(master, &dev->info);
+ i3c_master_getmwl_locked(master, &dev->info);
+
+ if (dev->info.bcr & I3C_BCR_HDR_CAP)
+ {
+ ret = i3c_master_gethdrcap_locked(master, &dev->info);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_put_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ if (dev->info.dyn_addr)
+ {
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_FREE);
+ }
+}
+
+static int i3c_master_get_i3c_addrs(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ /* ->init_dyn_addr should have been reserved before that, so, if we're
+ * trying to apply a pre-reserved dynamic address, we should not try
+ * to reserve the address slot a second time.
+ */
+
+ if (dev->info.dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ return 0;
+}
+
+static int i3c_master_attach_i3c_dev(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_dev_desc *dev)
+{
+ int ret;
+
+ /* We don't attach devices to the controller until they are
+ * addressable on the bus.
+ */
+
+ if (!dev->info.dyn_addr)
+ {
+ return 0;
+ }
+
+ ret = i3c_master_get_i3c_addrs(dev);
+ if (ret)
+ {
+ return ret;
+ }
+
+ /* Do not attach the master device itself. */
+
+ if (master->this != dev && master->ops->attach_i3c_dev)
+ {
+ ret = master->ops->attach_i3c_dev(dev);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ list_add_tail(&master->bus.devs.i3c, &dev->common.node);
+
+ return 0;
+}
+
+static int i3c_master_reattach_i3c_dev(FAR struct i3c_dev_desc *dev,
+ unsigned char old_dyn_addr)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ enum i3c_addr_slot_status status;
+ int ret;
+
+ if (dev->info.dyn_addr != old_dyn_addr)
+ {
+ status = i3c_bus_get_addr_slot_status(&master->bus,
+ dev->info.dyn_addr);
+ if (status != I3C_ADDR_SLOT_FREE)
+ {
+ return -EBUSY;
+ }
+
+ i3c_bus_set_addr_slot_status(&master->bus,
+ dev->info.dyn_addr,
+ I3C_ADDR_SLOT_I3C_DEV);
+ }
+
+ if (master->ops->reattach_i3c_dev)
+ {
+ ret = master->ops->reattach_i3c_dev(dev, old_dyn_addr);
+ if (ret)
+ {
+ i3c_master_put_i3c_addrs(dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void i3c_master_detach_i3c_dev(FAR struct i3c_dev_desc *dev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+
+ /* Do not detach the master device itself. */
+
+ if (master->this != dev && master->ops->detach_i3c_dev)
+ {
+ master->ops->detach_i3c_dev(dev);
+ }
+
+ i3c_master_put_i3c_addrs(dev);
+ list_delete(&dev->common.node);
+}
+
+static void
+i3c_master_register_new_i3c_devs(FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *desc;
+
+ if (!master->init_done)
+ {
+ return;
+ }
+
+ i3c_bus_for_each_i3cdev(&master->bus, desc)
+ {
+ if (desc->dev || !desc->info.dyn_addr || desc == master->this)
+ {
+ continue;
+ }
+
+ desc->dev = kmm_zalloc(sizeof(*desc->dev));
+ if (desc->dev == NULL)
+ {
+ continue;
+ }
+
+ desc->dev->bus = &master->bus;
+ desc->dev->desc = desc;
+ }
+}
+
+static void i3c_master_detach_free_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+ FAR struct i3c_dev_desc *i3ctmp;
+
+ list_for_every_entry_safe(&master->bus.devs.i3c, i3cdev, i3ctmp,
+ struct i3c_dev_desc, common.node)
+ {
+ i3c_master_detach_i3c_dev(i3cdev);
+ kmm_free(i3cdev);
+ }
+}
+
+/****************************************************************************
+ * Name: i3c_master_bus_init()
+ *
+ * Description:
+ * Initialize an I3C bus
+ *
+ * This function is following all initialisation steps described in the I3C
+ * specification:
+ *
+ * 1. Attach I2C devs to the master so that the master can fill its
+ * internal device table appropriately
+ *
+ * 2. Call &i3c_master_controller_ops->bus_init() method to initialize
+ * the master controller. That's usually where the bus mode is selected
+ * (pure bus or mixed fast/slow bus)
+ *
+ * 3. Instruct all devices on the bus to drop their dynamic address. This
+ * is particularly important when the bus was previously configured by
+ * someone else (for example the bootloader)
+ *
+ * 4. Disable all slave events.
+ *
+ * 5. Reserve address slots for I3C devices with init_dyn_addr. And if
+ * devices also have static_addr, try to pre-assign dynamic addresses
+ * requested by the FW with SETDASA and attach corresponding statically
+ * defined I3C devices to the master.
+ *
+ * 6. Do a DAA (Dynamic Address Assignment) to assign dynamic addresses to
+ * all remaining I3C devices
+ *
+ * Once this is done, all I3C and I2C devices should be usable.
+ *
+ * Input Parameters:
+ * master - main master initializing the bus
+ *
+ * Returned Value:
+ * A 0 in case of success, an negative error code otherwise.
+ ****************************************************************************/
+
+static int i3c_master_bus_init(FAR struct i3c_master_controller *master)
+{
+ int ret;
+
+ /* Now execute the controller specific ->bus_init() routine, which
+ * might configure its internal logic to match the bus limitations.
+ */
+
+ ret = master->ops->bus_init(master);
+ if (ret)
+ {
+ goto err_detach_devs;
+ }
+
+ /* The master device should have been instantiated in ->bus_init(),
+ * complain if this was not the case.
+ */
+
+ if (master->this == NULL)
+ {
+ i3cerr("master_set_info() was not called in ->bus_init()\n");
+ ret = -EINVAL;
+ goto err_bus_cleanup;
+ }
+
+ /* Reset all dynamic address that may have been assigned before
+ * (assigned by the bootloader for example).
+ */
+
+ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_rstdaa_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Disable all slave events before starting DAA. */
+
+ ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
+ I3C_CCC_EVENT_HJ);
+ if (ret && ret != I3C_ERROR_M2)
+ {
+ i3cerr("master_disec_locked() failed:%d (no device or timeout)\n",
+ ret);
+ }
+
+ /* Reserve init_dyn_addr first, and then try to pre-assign dynamic
+ * address and retrieve device information if needed.
+ * In case pre-assign dynamic address fails, setting dynamic address to
+ * the requested init_dyn_addr is retried after DAA is done in
+ * i3c_master_add_i3c_dev_locked().
+ */
+
+ ret = i3c_master_do_daa(master);
+ if (ret)
+ {
+ i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ i3cerr("master_do_daa failed:%d (no device)\n", ret);
+ }
+
+ return 0;
+
+err_bus_cleanup:
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+err_detach_devs:
+ i3c_master_detach_free_devs(master);
+
+ return ret;
+}
+
+static void i3c_master_bus_cleanup(FAR struct i3c_master_controller *master)
+{
+ if (master->ops->bus_cleanup)
+ {
+ master->ops->bus_cleanup(master);
+ }
+
+ i3c_master_detach_free_devs(master);
+}
+
+static FAR struct i3c_dev_desc *
+i3c_master_search_i3c_dev_duplicate(FAR struct i3c_dev_desc *refdev)
+{
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(refdev);
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev != refdev && i3cdev->info.pid == refdev->info.pid)
+ {
+ return i3cdev;
+ }
+ }
+
+ return NULL;
+}
+
+static int i3c_master_i2c_adapter_xfer(FAR struct i2c_master_s *i2c,
+ FAR struct i2c_msg_s *xfers, int nxfers)
+{
+ FAR struct i3c_master_controller *master = i2c_adapter_to_i3c_master(i2c);
+ int i;
+ int ret;
+ uint16_t addr;
+
+ if (xfers == NULL || master == NULL || nxfers <= 0)
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->i2c_xfers == NULL)
+ {
+ return -ENOTSUP;
+ }
+
+ /* Doing transfers to different devices is not supported. */
+
+ addr = xfers[0].addr;
+ for (i = 1; i < nxfers; i++)
+ {
+ if (addr != xfers[i].addr)
+ {
+ return -ENOTSUP;
+ }
+ }
+
+ i3c_bus_normaluse_lock(&master->bus);
+ ret = master->ops->i2c_xfers(master, xfers, nxfers);
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ return ret ? ret : nxfers;
+}
+
+static struct i2c_ops_s i3c_master_i2c_algo =
+{
+ .transfer = i3c_master_i2c_adapter_xfer,
+};
+
+static void i3c_master_unregister_i3c_devs(
+ FAR struct i3c_master_controller *master)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+
+ i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
+ {
+ if (i3cdev->dev == NULL)
+ {
+ continue;
+ }
+
+ i3cdev->dev->desc = NULL;
+ i3cdev->dev->bus = NULL;
+ kmm_free(i3cdev->dev);
+ i3cdev->dev = NULL;
+ }
+}
+
+static void i3c_master_handle_ibi(FAR void *arg)
+{
+ FAR struct i3c_ibi_slot *slot = (struct i3c_ibi_slot *)arg;
+ FAR struct i3c_dev_desc *dev = slot->dev;
+ FAR struct i3c_master_controller *master = i3c_dev_get_master(dev);
+ struct i3c_ibi_payload payload;
+
+ payload.data = slot->data;
+ payload.len = slot->len;
+
+ if (dev->dev)
+ {
+ dev->ibi->handler(dev->dev, &payload);
+ }
+
+ master->ops->recycle_ibi_slot(dev, slot);
+ atomic_fetch_sub(&dev->ibi->pending_ibis, 1);
+ if (!atomic_load(&dev->ibi->pending_ibis))
+ {
+ sem_post(&dev->ibi->all_ibis_handled);
+ }
+}
+
+static void i3c_master_init_ibi_slot(FAR struct i3c_dev_desc *dev,
+ FAR struct i3c_ibi_slot *slot)
+{
+ slot->dev = dev;
+}
+
+static int i3c_master_check_ops(
+ FAR const struct i3c_master_controller_ops *ops)
+{
+ if (ops == NULL || ops->bus_init == NULL || ops->priv_xfers == NULL ||
+ ops->send_ccc_cmd == NULL || ops->do_daa == NULL ||
+ ops->i2c_xfers == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (ops->request_ibi &&
+ (ops->enable_ibi == NULL || ops->disable_ibi == NULL ||
+ ops->free_ibi == NULL || ops->recycle_ibi_slot == NULL))
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i2c_unregister_driver()
+ *
+ * Description:
+ * Perform an I2C driver unregister operation.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ *
+ ****************************************************************************/
+
+static void i2c_unregister_driver(FAR struct i3c_master_controller *master)
+{
+ char devname[12];
+ int i2c_bus_id = i3c_master_to_i2c_bus_number(master);
+
+ snprintf(devname, 12, "/dev/i2c%d", i2c_bus_id);
+ nx_unlink(devname);
+}
+
+/****************************************************************************
+ * Name: i3c_unregister_driver()
+ *
+ * Description:
+ * Perform an I3C driver unregister operation.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ *
+ ****************************************************************************/
+
+static void i3c_unregister_driver(FAR struct i3c_master_controller *master)
+{
+ char devname[12];
+
+ snprintf(devname, 12, "/dev/i3c%d", i3c_master_to_i3c_bus_number(master));
+ nx_unlink(devname);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_master_send_ccc_cmd_locked
+ ****************************************************************************/
+
+int i3c_master_send_ccc_cmd_locked(
+ FAR struct i3c_master_controller *master,
+ FAR struct i3c_ccc_cmd *cmd)
+{
+ int ret;
+
+ if (cmd == NULL || master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (master->init_done &&
+ !nxmutex_is_locked(&master->bus.lock))
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->send_ccc_cmd == NULL)
+ {
+ return -ENOTSUP;
+ }
+
+ if ((cmd->id & I3C_CCC_DIRECT) && (cmd->dests == NULL || !cmd->ndests))
+ {
+ return -EINVAL;
+ }
+
+ if (master->ops->supports_ccc_cmd &&
+ !master->ops->supports_ccc_cmd(master, cmd))
+ {
+ return -ENOTSUP;
+ }
+
+ ret = master->ops->send_ccc_cmd(master, cmd);
+ if (ret)
+ {
+ if (cmd->err != I3C_ERROR_UNKNOWN)
+ {
+ return cmd->err;
+ }
+
+ return ret;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i3c_bus_normaluse_lock
+ *
+ * Description:
+ * I3C bus to take the lock on.
+ *
+ * This function takes the bus lock for any operation that is not a
+ * maintenance operation (see i3c_bus_maintenance_lock() for
+ * non-exhaustive list of maintenance operations). Basically all
+ * communications with I3C devices are normal operations (HDR, SDR
+ * transfers or CCC commands that do not change bus state or I3C dynamic
+ * address).
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ ****************************************************************************/
+
+void i3c_bus_normaluse_lock(FAR struct i3c_bus *bus)
+{
+ nxmutex_lock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_bus_normaluse_unlock
+ *
+ * Description:
+ * Release the bus lock after a normal operation.
+ *
+ * Should be called when a normal operation is done. See
+ * i3c_bus_normaluse_lock() for more details on what these
+ * normal operations are.
+ *
+ * Input Parameters:
+ * bus - I3C bus to release the lock on.
+ *
+ *
+ ****************************************************************************/
+
+void i3c_bus_normaluse_unlock(FAR struct i3c_bus *bus)
+{
+ nxmutex_unlock(&bus->lock);
+}
+
+/****************************************************************************
+ * Name: i3c_master_get_free_addr()
+ *
+ * Description:
+ * Get a free address on the bus.
+ *
+ * This function must be called with the bus lock held in write mode.
+ *
+ * Input Parameters:
+ * master - I3C master object.
+ * start_addr - Where to start searching.
+ *
+ * Returned Value:
+ * The first free address starting at @start_addr (included) or -ENOMEM
+ * if there's no more address available.
+ *
+ ****************************************************************************/
+
+int i3c_master_get_free_addr(FAR struct i3c_master_controller *master,
+ unsigned char start_addr)
+{
+ return i3c_bus_get_free_addr(&master->bus, start_addr);
+}
+
+/****************************************************************************
+ * Name: i3c_master_do_daa()
+ *
+ * Description:
+ * Do a DAA (Dynamic Address Assignment)
+ *
+ * This function is instantiating an I3C device object and adding it to
+ * the I3C device list. All device information are automatically retrieved
+ * using standard CCC commands.
+ *
+ * The I3C device object is returned in case the master wants to attach
+ * private data to it using i3c_dev_set_master_data().
+ *
+ * This function must be called with the bus lock held in write mode.
+ *
+ * Input Parameters:
+ * master - Master doing the DAA
+ *
+ * Returned Value:
+ * A 0 in case of success, an negative error code otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_master_do_daa(FAR struct i3c_master_controller *master)
+{
+ int ret;
+
+ i3c_bus_maintenance_lock(&master->bus);
+ ret = master->ops->do_daa(master);
+ i3c_bus_maintenance_unlock(&master->bus);
+
+ if (ret)
+ {
+ return ret;
+ }
+
+ i3c_bus_normaluse_lock(&master->bus);
+ i3c_master_register_new_i3c_devs(master);
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: i3c_master_enec_locked()
+ *
+ * Description:
+ * Send an ENEC CCC command
+ *
+ * Sends an ENEC CCC command to enable some or all events coming from a
+ * specific slave, or all devices if @addr is %I3C_BROADCAST_ADDR.
+ *
+ * This function must be called with the bus lock held in write mode.
+ *
+ * Input Parameters:
+ * master - Master used to send frames on the bus.
+ * addr - A valid I3C slave address or %I3C_BROADCAST_ADDR
+ * evts - Events to enable
+ *
+ * Returned Value:
+ * 0 in case of success, a positive I3C error code if the error is
+ * one of the official Mx error codes, and a negative error code otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_master_enec_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr, unsigned char evts)
+{
+ return i3c_master_enec_disec_locked(master, addr, true, evts);
+}
+
+/****************************************************************************
+ * Name: i3c_master_entdaa_locked()
+ *
+ * Description:
+ * Start a DAA (Dynamic Address Assignment) procedure
+ *
+ * This function must be called with the bus lock held in write mode.
+ *
+ * Note that this function only sends the ENTDAA CCC command, all the logic
+ * behind dynamic address assignment has to be handled in the I3C master
+ * driver.
+ *
+ * Input Parameters:
+ * master - Master used to send frames on the bus.
+ *
+ * Returned Value:
+ * 0 in case of success, a positive I3C error code if the error is one of
+ * the official Mx error codes, and a negative error code otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_master_entdaa_locked(FAR struct i3c_master_controller *master)
+{
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ i3c_ccc_cmd_dest_init(&dest, I3C_BROADCAST_ADDR, 0);
+ i3c_ccc_cmd_init(&cmd, false, I3C_CCC_ENTDAA, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_master_disec_locked()
+ *
+ * Description:
+ * Send a DISEC CCC command
+ *
+ * Send a DISEC CCC command to disable some or all events coming from a
+ * specific slave, or all devices if @addr is %I3C_BROADCAST_ADDR.
+ *
+ * This function must be called with the bus lock held in write mode.
+ *
+ * Input Parameters:
+ * master - Master used to send frames on the bus.
+ * addr - A valid I3C slave address or %I3C_BROADCAST_ADDR
+ * evts - Events to disable
+ *
+ * Returned Value:
+ * 0 in case of success, a positive I3C error code if the error is one of
+ * the official Mx error codes, and a negative error code otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_master_disec_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr, unsigned char evts)
+{
+ return i3c_master_enec_disec_locked(master, addr, false, evts);
+}
+
+/****************************************************************************
+ * Name: i3c_master_set_info()
+ *
+ * Description:
+ * Set master device information
+ *
+ * Set master device info. This should be called from
+ * &i3c_master_controller_ops->bus_init().
+ *
+ * Not all &i3c_device_info fields are meaningful for a master device.
+ * Here is a list of fields that should be properly filled:
+ *
+ * - &i3c_device_info->dyn_addr
+ * - &i3c_device_info->bcr
+ * - &i3c_device_info->dcr
+ * - &i3c_device_info->pid
+ * - &i3c_device_info->hdr_cap if %I3C_BCR_HDR_CAP bit is set in
+ * &i3c_device_info->bcr
+ *
+ * This function must be called with the bus lock held in maintenance mode.
+ *
+ * Input Parameters:
+ * master - Master used to send frames on the bus
+ * info - I3C device information
+ *
+ * Returned Value:
+ * 0 if @info contains valid information (not every piece of
+ * information can be checked, but we can at least make sure info->dyn_addr
+ * and @info->bcr are correct), -EINVAL otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_master_set_info(FAR struct i3c_master_controller *master,
+ FAR const struct i3c_device_info *info)
+{
+ FAR struct i3c_dev_desc *i3cdev;
+ int ret;
+
+ if (!i3c_bus_dev_addr_is_avail(&master->bus, info->dyn_addr))
+ {
+ return -EINVAL;
+ }
+
+ if (I3C_BCR_DEVICE_ROLE(info->bcr) == I3C_BCR_I3C_MASTER &&
+ master->secondary)
+ {
+ return -EINVAL;
+ }
+
+ if (master->this)
+ {
+ return -EINVAL;
+ }
+
+ i3cdev = i3c_master_alloc_i3c_dev(master, info);
+ if (i3cdev == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ master->this = i3cdev;
+ master->bus.cur_master = master->this;
+
+ ret = i3c_master_attach_i3c_dev(master, i3cdev);
+ if (ret)
+ {
+ goto err_free_dev;
+ }
+
+ return 0;
+
+err_free_dev:
+ kmm_free(i3cdev);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_master_add_i3c_dev_locked()
+ *
+ * Description:
+ * Add an I3C slave to the bus
+ *
+ * This function is instantiating an I3C device object and adding it to
+ * the I3C device list. All device information are automatically retrieved
+ * using standard CCC commands.
+ *
+ * The I3C device object is returned in case the master wants to attach
+ * private data to it using i3c_dev_set_master_data().
+ *
+ * This function must be called with the bus lock held in write mode.
+ *
+ * Input Parameters:
+ * master - Master used to send frames on the bus
+ * addr - I3C slave dynamic address assigned to the device
+ *
+ * Returned Value:
+ * A 0 in case of success, an negative error code otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_master_add_i3c_dev_locked(FAR struct i3c_master_controller *master,
+ unsigned char addr)
+{
+ struct i3c_device_info info =
+ {
+ .dyn_addr = addr
+ };
+
+ FAR struct i3c_dev_desc *newdev;
+ FAR struct i3c_dev_desc *olddev;
+ unsigned char old_dyn_addr = addr;
+ unsigned char expected_dyn_addr;
+ struct i3c_ibi_setup ibireq;
+ bool enable_ibi = false;
+ int ret;
+
+ memset(&ibireq, 0, sizeof(struct i3c_ibi_setup));
+ if (master == NULL)
+ {
+ return -EINVAL;
+ }
+
+ newdev = i3c_master_alloc_i3c_dev(master, &info);
+ if (newdev == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ ret = i3c_master_attach_i3c_dev(master, newdev);
+ if (ret)
+ {
+ goto err_free_dev;
+ }
+
+ ret = i3c_master_retrieve_dev_info(newdev);
+ if (ret)
+ {
+ goto err_detach_dev;
+ }
+
+ olddev = i3c_master_search_i3c_dev_duplicate(newdev);
+ if (olddev)
+ {
+ newdev->dev = olddev->dev;
+ if (newdev->dev)
+ {
+ newdev->dev->desc = newdev;
+ }
+
+ /* We need to restore the IBI state too, so let's save the
+ * IBI information and try to restore them after olddev has
+ * been detached+released and its IBI has been stopped and
+ * the associated resources have been freed.
+ */
+
+ nxmutex_lock(&olddev->ibi_lock);
+ if (olddev->ibi)
+ {
+ ibireq.handler = olddev->ibi->handler;
+ ibireq.max_payload_len = olddev->ibi->max_payload_len;
+ ibireq.num_slots = olddev->ibi->num_slots;
+
+ if (olddev->ibi->enabled)
+ {
+ enable_ibi = true;
+ i3c_dev_disable_ibi_locked(olddev);
+ }
+
+ i3c_dev_free_ibi_locked(olddev);
+ }
+
+ nxmutex_unlock(&olddev->ibi_lock);
+
+ old_dyn_addr = olddev->info.dyn_addr;
+
+ i3c_master_detach_i3c_dev(olddev);
+ kmm_free(olddev);
+ }
+
+ ret = i3c_master_reattach_i3c_dev(newdev, old_dyn_addr);
+ if (ret)
+ {
+ goto err_detach_dev;
+ }
+
+ /* Depending on our previous state, the expected dynamic address might
+ * differ:
+ * - if the device already had a dynamic address assigned, let's try to
+ * re-apply this one
+ * - if the device did not have a dynamic address and the firmware
+ * requested a specific address, pick this one
+ * - in any other case, keep the address automatically assigned by the
+ * master
+ */
+
+ if (old_dyn_addr && old_dyn_addr != newdev->info.dyn_addr)
+ {
+ expected_dyn_addr = old_dyn_addr;
+ }
+ else
+ {
+ expected_dyn_addr = newdev->info.dyn_addr;
+ }
+
+ if (newdev->info.dyn_addr != expected_dyn_addr)
+ {
+ /* Try to apply the expected dynamic address. If it fails, keep
+ * the address assigned by the master.
+ */
+
+ ret = i3c_master_setnewda_locked(master,
+ newdev->info.dyn_addr,
+ expected_dyn_addr);
+ if (!ret)
+ {
+ old_dyn_addr = newdev->info.dyn_addr;
+ newdev->info.dyn_addr = expected_dyn_addr;
+ i3c_master_reattach_i3c_dev(newdev, old_dyn_addr);
+ }
+ else
+ {
+ i3cerr("Failed to assign reserved/old address%d%"PRIx64"\n",
+ master->bus.id, newdev->info.pid);
+ }
+ }
+
+ /* Now is time to try to restore the IBI setup. If we're lucky,
+ * everything works as before, otherwise, all we can do is complain.
+ * FIXME: maybe we should add callback to inform the driver that it
+ * should request the IBI again instead of trying to hide that from
+ * him.
+ */
+
+ if (ibireq.handler)
+ {
+ nxmutex_lock(&newdev->ibi_lock);
+ ret = i3c_dev_request_ibi_locked(newdev, &ibireq);
+ if (ret)
+ {
+ i3cerr("Failed to request IBI on device %d-%"PRIx64"\n",
+ master->bus.id, newdev->info.pid);
+ }
+ else if (enable_ibi)
+ {
+ ret = i3c_dev_enable_ibi_locked(newdev);
+ if (ret)
+ {
+ i3cerr("Failed to re-enable IBI on device %d-%"PRIx64"\n",
+ master->bus.id, newdev->info.pid);
+ }
+ }
+
+ nxmutex_unlock(&newdev->ibi_lock);
+ }
+
+ return 0;
+
+err_detach_dev:
+ if (newdev->dev && newdev->dev->desc)
+ {
+ newdev->dev->desc = NULL;
+ }
+
+ i3c_master_detach_i3c_dev(newdev);
+
+err_free_dev:
+ kmm_free(newdev);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_master_queue_ibi()
+ *
+ * Description:
+ * Queue an IBI
+ *
+ * Queue an IBI to the controller workqueue. The IBI handler attached
+ * to the dev will be called from a workqueue context.
+ *
+ * Input Parameters:
+ * dev - The device this IBI is coming from
+ * slot - The IBI slot used to store the payload
+ *
+ ****************************************************************************/
+
+void i3c_master_queue_ibi(FAR struct i3c_dev_desc *dev,
+ FAR struct i3c_ibi_slot *slot)
+{
+ atomic_fetch_add(&dev->ibi->pending_ibis, 1);
+ work_queue(HPWORK, &slot->work, i3c_master_handle_ibi, slot, 0);
+}
+
+/****************************************************************************
+ * Name: i3c_generic_ibi_free_pool()
+ *
+ * Description:
+ * Free a generic IBI pool
+ *
+ * Free all IBI slots allated by a generic IBI pool.
+ *
+ * Input Parameters:
+ * pool - The IBI pool to free
+ *
+ ****************************************************************************/
+
+void i3c_generic_ibi_free_pool(FAR struct i3c_generic_ibi_pool *pool)
+{
+ FAR struct i3c_generic_ibi_slot *slot;
+ unsigned int nslots = 0;
+
+ while (!list_is_empty(&pool->free_slots))
+ {
+ slot = list_first_entry(&pool->free_slots,
+ struct i3c_generic_ibi_slot, node);
+ list_delete(&slot->node);
+ nslots++;
+ }
+
+ if (nslots != pool->num_slots)
+ {
+ i3cwarn("freed slots num is not equal to allocated slots num\n");
+ }
+
+ /* If the number of freed slots is not equal to the number of allocated
+ * slots we have a leak somewhere.
+ */
+
+ kmm_free(pool->payload_buf);
+ kmm_free(pool->slots);
+ kmm_free(pool);
+}
+
+/****************************************************************************
+ * Name: i3c_generic_ibi_alloc_pool()
+ *
+ * Description:
+ * Create a generic IBI pool
+ *
+ * Create a generic IBI pool based on the information provided in @req.
+ *
+ * Input Parameters:
+ * dev - The device this pool will be used for
+ * req - IBI setup request describing what the device driver expects
+ *
+ * Returned Value:
+ * A valid IBI pool in case of success, an ERR_PTR() otherwise.
+ ****************************************************************************/
+
+FAR struct i3c_generic_ibi_pool *
+i3c_generic_ibi_alloc_pool(FAR struct i3c_dev_desc *dev,
+ FAR const struct i3c_ibi_setup *req)
+{
+ FAR struct i3c_generic_ibi_pool *pool;
+ FAR struct i3c_generic_ibi_slot *slot;
+ unsigned int i;
+
+ struct i3c_generic_ibi_pool *ret;
+
+ pool = kmm_zalloc(sizeof(*pool));
+ if (pool == NULL)
+ {
+ return NULL;
+ }
+
+ list_initialize(&pool->free_slots);
+ list_initialize(&pool->pending);
+
+ pool->slots = kmm_calloc(req->num_slots, sizeof(*slot));
+ if (pool->slots == NULL)
+ {
+ ret = NULL;
+ goto err_free_pool;
+ }
+
+ if (req->max_payload_len)
+ {
+ pool->payload_buf = kmm_calloc(req->num_slots,
+ req->max_payload_len);
+ if (pool->payload_buf == NULL)
+ {
+ ret = NULL;
+ goto err_free_pool;
+ }
+ }
+
+ for (i = 0; i < req->num_slots; i++)
+ {
+ slot = &pool->slots[i];
+ i3c_master_init_ibi_slot(dev, &slot->base);
+
+ if (req->max_payload_len)
+ {
+ slot->base.data = pool->payload_buf +
+ (i * req->max_payload_len);
+ }
+
+ list_add_tail(&pool->free_slots, &slot->node);
+ pool->num_slots++;
+ }
+
+ return pool;
+
+err_free_pool:
+ i3c_generic_ibi_free_pool(pool);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_generic_ibi_get_free_slot()
Review Comment:
```suggestion
* Name: i3c_generic_ibi_get_free_slot
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] drivers/i3c: support i3c driver frameworks [nuttx]
Posted by "Donny9 (via GitHub)" <gi...@apache.org>.
Donny9 commented on PR #13507:
URL: https://github.com/apache/nuttx/pull/13507#issuecomment-2355733175
> please add the SPDX identifier
>
> * SPDX-License-Identifier: Apache-2.0
>
> to the license
Done~
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] drivers/i3c: support i3c driver frameworks [nuttx]
Posted by "Donny9 (via GitHub)" <gi...@apache.org>.
Donny9 commented on code in PR #13507:
URL: https://github.com/apache/nuttx/pull/13507#discussion_r1770710808
##########
drivers/i3c/device.c:
##########
@@ -0,0 +1,346 @@
+/****************************************************************************
+ * drivers/i3c/device.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "internals.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_device_do_priv_xfers()
+ *
+ * Description:
+ * Do I3C SDR private transfers directed to a specific device.
+ *
+ * Initiate one or several private SDR transfers with @dev.
+ *
+ * This function can sleep and thus cannot be called in atomic context.
+ *
+ * Input Parameters:
+ * dev - Device with which the transfers should be done
+ * xfers - Array of transfers
+ * nxfers - Number of transfers
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_do_priv_xfers(FAR const struct i3c_device *dev,
+ FAR struct i3c_priv_xfer *xfers,
+ int nxfers)
+{
+ int ret;
+ int i;
+
+ if (nxfers < 1)
+ {
+ return 0;
+ }
+
+ for (i = 0; i < nxfers; i++)
+ {
+ if (!xfers[i].len || !xfers[i].data.in)
+ {
+ return -EINVAL;
+ }
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers);
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_get_info()
+ * Get I3C device information, Retrieve I3C dev info.
+ *
+ * Input Parameters:
+ * dev - Device we want information on.
+ * info - The information object to fill in.
+ *
+ ****************************************************************************/
+
+void i3c_device_get_info(FAR const struct i3c_device *dev,
+ FAR struct i3c_device_info *info)
+{
+ if (info == NULL)
+ {
+ return;
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ *info = dev->desc->info;
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+}
+
+/****************************************************************************
+ * Name: i3c_device_disable_ibi()
+ *
+ * Description:
+ * Disable IBIs coming from a specific device.
+ *
+ * This function disable IBIs coming from a specific device and wait for
+ * all pending IBIs to be processed.
+ *
+ * Input Parameters:
+ * dev - Device on which IBIs should be disabled
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_disable_ibi(FAR const struct i3c_device *dev)
+{
+ int ret = -ENOENT;
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ ret = i3c_dev_disable_ibi_locked(dev->desc);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_enable_ibi()
Review Comment:
Done~
##########
drivers/i3c/device.c:
##########
@@ -0,0 +1,346 @@
+/****************************************************************************
+ * drivers/i3c/device.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "internals.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_device_do_priv_xfers()
+ *
+ * Description:
+ * Do I3C SDR private transfers directed to a specific device.
+ *
+ * Initiate one or several private SDR transfers with @dev.
+ *
+ * This function can sleep and thus cannot be called in atomic context.
+ *
+ * Input Parameters:
+ * dev - Device with which the transfers should be done
+ * xfers - Array of transfers
+ * nxfers - Number of transfers
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_do_priv_xfers(FAR const struct i3c_device *dev,
+ FAR struct i3c_priv_xfer *xfers,
+ int nxfers)
+{
+ int ret;
+ int i;
+
+ if (nxfers < 1)
+ {
+ return 0;
+ }
+
+ for (i = 0; i < nxfers; i++)
+ {
+ if (!xfers[i].len || !xfers[i].data.in)
+ {
+ return -EINVAL;
+ }
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers);
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_get_info()
+ * Get I3C device information, Retrieve I3C dev info.
+ *
+ * Input Parameters:
+ * dev - Device we want information on.
+ * info - The information object to fill in.
+ *
+ ****************************************************************************/
+
+void i3c_device_get_info(FAR const struct i3c_device *dev,
+ FAR struct i3c_device_info *info)
+{
+ if (info == NULL)
+ {
+ return;
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ *info = dev->desc->info;
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+}
+
+/****************************************************************************
+ * Name: i3c_device_disable_ibi()
Review Comment:
Done~
##########
drivers/i3c/device.c:
##########
@@ -0,0 +1,346 @@
+/****************************************************************************
+ * drivers/i3c/device.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "internals.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_device_do_priv_xfers()
+ *
+ * Description:
+ * Do I3C SDR private transfers directed to a specific device.
+ *
+ * Initiate one or several private SDR transfers with @dev.
+ *
+ * This function can sleep and thus cannot be called in atomic context.
+ *
+ * Input Parameters:
+ * dev - Device with which the transfers should be done
+ * xfers - Array of transfers
+ * nxfers - Number of transfers
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_do_priv_xfers(FAR const struct i3c_device *dev,
+ FAR struct i3c_priv_xfer *xfers,
+ int nxfers)
+{
+ int ret;
+ int i;
+
+ if (nxfers < 1)
+ {
+ return 0;
+ }
+
+ for (i = 0; i < nxfers; i++)
+ {
+ if (!xfers[i].len || !xfers[i].data.in)
+ {
+ return -EINVAL;
+ }
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers);
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_get_info()
Review Comment:
Done~
##########
drivers/i3c/device.c:
##########
@@ -0,0 +1,346 @@
+/****************************************************************************
+ * drivers/i3c/device.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "internals.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_device_do_priv_xfers()
Review Comment:
Done~
##########
drivers/i3c/device.c:
##########
@@ -0,0 +1,346 @@
+/****************************************************************************
+ * drivers/i3c/device.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "internals.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i3c_device_do_priv_xfers()
+ *
+ * Description:
+ * Do I3C SDR private transfers directed to a specific device.
+ *
+ * Initiate one or several private SDR transfers with @dev.
+ *
+ * This function can sleep and thus cannot be called in atomic context.
+ *
+ * Input Parameters:
+ * dev - Device with which the transfers should be done
+ * xfers - Array of transfers
+ * nxfers - Number of transfers
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_do_priv_xfers(FAR const struct i3c_device *dev,
+ FAR struct i3c_priv_xfer *xfers,
+ int nxfers)
+{
+ int ret;
+ int i;
+
+ if (nxfers < 1)
+ {
+ return 0;
+ }
+
+ for (i = 0; i < nxfers; i++)
+ {
+ if (!xfers[i].len || !xfers[i].data.in)
+ {
+ return -EINVAL;
+ }
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers);
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_get_info()
+ * Get I3C device information, Retrieve I3C dev info.
+ *
+ * Input Parameters:
+ * dev - Device we want information on.
+ * info - The information object to fill in.
+ *
+ ****************************************************************************/
+
+void i3c_device_get_info(FAR const struct i3c_device *dev,
+ FAR struct i3c_device_info *info)
+{
+ if (info == NULL)
+ {
+ return;
+ }
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ *info = dev->desc->info;
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+}
+
+/****************************************************************************
+ * Name: i3c_device_disable_ibi()
+ *
+ * Description:
+ * Disable IBIs coming from a specific device.
+ *
+ * This function disable IBIs coming from a specific device and wait for
+ * all pending IBIs to be processed.
+ *
+ * Input Parameters:
+ * dev - Device on which IBIs should be disabled
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_disable_ibi(FAR const struct i3c_device *dev)
+{
+ int ret = -ENOENT;
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ ret = i3c_dev_disable_ibi_locked(dev->desc);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_enable_ibi()
+ *
+ * Description:
+ * Enable IBIs coming from a specific device.
+ *
+ * This function enable IBIs coming from a specific device and wait for
+ * all pending IBIs to be processed. This should be called on a device
+ * where i3c_device_request_ibi() has succeeded.
+ *
+ * Note that IBIs from this device might be received before this function
+ * returns to its caller.
+ *
+ * Input Parameters:
+ * dev - Device on which IBIs should be enabled
+ *
+ * Returned Value:
+ * 0 in case of success, a negative error core otherwise.
+ *
+ ****************************************************************************/
+
+int i3c_device_enable_ibi(FAR const struct i3c_device *dev)
+{
+ int ret = -ENOENT;
+
+ i3c_bus_normaluse_lock(dev->bus);
+ if (dev->desc)
+ {
+ nxmutex_lock(&dev->desc->ibi_lock);
+ ret = i3c_dev_enable_ibi_locked(dev->desc);
+ nxmutex_unlock(&dev->desc->ibi_lock);
+ }
+
+ i3c_bus_normaluse_unlock(dev->bus);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: i3c_device_request_ibi()
Review Comment:
Done~
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org