@@ -21,3 +21,11 @@ config DW_I3C_MASTER
This driver can also be built as a module. If so, the module
will be called dw-i3c-master.
+
+config SVC_I3C_MASTER
+ tristate "Silvaco I3C Dual-Role Master driver"
+ depends on I3C
+ depends on HAS_IOMEM
+ depends on !(ALPHA || PARISC)
+ help
+ Support for Silvaco I3C Dual-Role Master Controller.
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CDNS_I3C_MASTER) += i3c-master-cdns.o
obj-$(CONFIG_DW_I3C_MASTER) += dw-i3c-master.o
+obj-$(CONFIG_SVC_I3C_MASTER) += svc-i3c-master.o
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,2863 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Silvaco, Inc.
+ *
+ * Author: Conor Culhane <conor.culhane@silvaco.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/i3c/master.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/iopoll.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define I3C_MCONFIG_OFFSET 0x000
+#define I3C_MCTRL_OFFSET 0x084
+#define I3C_MSTATUS_OFFSET 0x088
+#define I3C_IBIRULES_OFFSET 0x08C
+#define I3C_MINTSET_OFFSET 0x090
+#define I3C_MINTCLR_OFFSET 0x094
+#define I3C_MINTMASKED_OFFSET 0x098
+#define I3C_MERRWARN_OFFSET 0x09C
+#define I3C_MDMACTRL_OFFSET 0x0A0
+#define I3C_MDATACTRL_OFFSET 0x0AC
+#define I3C_MWDATAB_OFFSET 0x0B0
+#define I3C_MWDATABE_OFFSET 0x0B4
+#define I3C_MWDATAH_OFFSET 0x0B8
+#define I3C_MWDATAHE_OFFSET 0x0BC
+#define I3C_MRDATAB_OFFSET 0x0C0
+#define I3C_MRDATAH_OFFSET 0x0C8
+#define I3C_MWMSG_SDR_OFFSET 0x0D0
+#define I3C_MRMSG_SDR_OFFSET 0x0D4
+#define I3C_MWMSG_DDR_OFFSET 0x0D8
+#define I3C_MRMSG_DDR_OFFSET 0x0DC
+#define I3C_MDYNADDR_OFFSET 0x0E4
+#define I3C_CONFIG_OFFSET 0x004
+#define I3C_STATUS_OFFSET 0x008
+#define I3C_CTRL_OFFSET 0x00C
+#define I3C_INTSET_OFFSET 0x010
+#define I3C_INTCLR_OFFSET 0x014
+#define I3C_INTMASKED_OFFSET 0x018
+#define I3C_ERRWARN_OFFSET 0x01C
+#define I3C_DMACTRL_OFFSET 0x020
+#define I3C_DATACTRL_OFFSET 0x02C
+#define I3C_WDATAB_OFFSET 0x030
+#define I3C_WDATABE_OFFSET 0x034
+#define I3C_WDATAH_OFFSET 0x038
+#define I3C_WDATAHE_OFFSET 0x03C
+#define I3C_RDATAB_OFFSET 0x040
+#define I3C_RDATAH_OFFSET 0x048
+#define I3C_CAPABILITIES_OFFSET 0x060
+#define I3C_DYNADDR_OFFSET 0x064
+#define I3C_MAXLIMITS_OFFSET 0x068
+#define I3C_PARTNO_OFFSET 0x06C
+#define I3C_IDEXT_OFFSET 0x070
+#define I3C_VENDORID_OFFSET 0x074
+#define I3C_TCCLOCK_OFFSET 0x078
+#define I3C_ID_OFFSET 0xFFC
+#define I3C_MCONFIG_MSTENA_MASK 0x00000003
+#define I3C_MCONFIG_MSTENA_SHFT 0
+#define I3C_MCONFIG_MSTENA_DISABLE 0
+#define I3C_MCONFIG_MSTENA_ENABLE 1
+#define I3C_MCONFIG_MSTENA_CAPABLE 2
+#define I3C_MCONFIG_DISTO_MASK 0x00000008
+#define I3C_MCONFIG_DISTO_SHFT 3
+#define I3C_MCONFIG_HKEEP_MASK 0x00000030
+#define I3C_MCONFIG_HKEEP_SHFT 4
+#define I3C_MCONFIG_HKEEP_NONE 0
+#define I3C_MCONFIG_HKEEP_ON_CHIP 1
+#define I3C_MCONFIG_HKEEP_EXT_SDA 2
+#define I3C_MCONFIG_HKEEP_EXT_SDA_SCL 3
+#define I3C_MCONFIG_ODSTOP_MASK 0x00000040
+#define I3C_MCONFIG_ODSTOP_SHFT 6
+#define I3C_MCONFIG_PPBAUD_MASK 0x00000F00
+#define I3C_MCONFIG_PPBAUD_SHFT 8
+#define I3C_MCONFIG_PPLOW_MASK 0x0000F000
+#define I3C_MCONFIG_PPLOW_SHFT 12
+#define I3C_MCONFIG_ODBAUD_MASK 0x00FF0000
+#define I3C_MCONFIG_ODBAUD_SHFT 16
+#define I3C_MCONFIG_ODHPP_MASK 0x01000000
+#define I3C_MCONFIG_ODHPP_SHFT 24
+#define I3C_MCONFIG_SKEW_MASK 0x0E000000
+#define I3C_MCONFIG_SKEW_SHFT 25
+#define I3C_MCONFIG_I2CBAUD_MASK 0xF0000000
+#define I3C_MCONFIG_I2CBAUD_SHFT 28
+#define I3C_MCTRL_REQUEST_MASK 0x00000007
+#define I3C_MCTRL_REQUEST_SHFT 0
+#define I3C_MCTRL_REQUEST_NONE 0
+#define I3C_MCTRL_REQUEST_START_ADDR 1
+#define I3C_MCTRL_REQUEST_STOP 2
+#define I3C_MCTRL_REQUEST_IBI_ACKNACK 3
+#define I3C_MCTRL_REQUEST_PROC_DAA 4
+#define I3C_MCTRL_REQUEST_RESERVED 5
+#define I3C_MCTRL_REQUEST_FORCE_EXIT 6
+#define I3C_MCTRL_REQUEST_AUTO_IBI 7
+#define I3C_MCTRL_TYPE_MASK 0x00000030
+#define I3C_MCTRL_TYPE_SHFT 4
+#define I3C_MCTRL_TYPE_I3C (0)
+#define I3C_MCTRL_TYPE_I2C (1 << I3C_MCTRL_TYPE_SHFT)
+#define I3C_MCTRL_TYPE_HDR_DDR (2 << I3C_MCTRL_TYPE_SHFT)
+#define I3C_MCTRL_IBIRESP_MASK 0x000000C0
+#define I3C_MCTRL_IBIRESP_SHFT 6
+#define I3C_MCTRL_IBIRESP_ACK (0)
+#define I3C_MCTRL_IBIRESP_ACK_NO_BYTE (0)
+#define I3C_MCTRL_IBIRESP_NACK (1 << I3C_MCTRL_IBIRESP_SHFT)
+#define I3C_MCTRL_IBIRESP_ACK_WITH_BYTE (2 << I3C_MCTRL_IBIRESP_SHFT)
+#define I3C_MCTRL_IBIRESP_MANUAL (3 << I3C_MCTRL_IBIRESP_SHFT)
+#define I3C_MCTRL_DIR_MASK 0x00000100
+#define I3C_MCTRL_DIR_SHFT 8
+#define I3C_MCTRL_DIR_WRITE (0)
+#define I3C_MCTRL_DIR_READ (1 << I3C_MCTRL_DIR_SHFT)
+#define I3C_MCTRL_ADDR_MASK 0x0000FE00
+#define I3C_MCTRL_ADDR_SHFT 9
+#define I3C_MCTRL_RDTERM_MASK 0x00FF0000
+#define I3C_MCTRL_RDTERM_SHFT 16
+#define I3C_MSTATUS_STATE_MASK 0x00000007
+#define I3C_MSTATUS_STATE_SHFT 0
+#define I3C_MSTATUS_STATE_IDLE 0
+#define I3C_MSTATUS_STATE_SLVREQ 1
+#define I3C_MSTATUS_STATE_MSGSDR 2
+#define I3C_MSTATUS_STATE_NORMACT 3
+#define I3C_MSTATUS_STATE_DDR 4
+#define I3C_MSTATUS_STATE_DAA 5
+#define I3C_MSTATUS_STATE_IBIACK 6
+#define I3C_MSTATUS_STATE_IBIRCV 7
+#define I3C_MSTATUS_BETWEEN_MASK 0x00000010
+#define I3C_MSTATUS_BETWEEN_SHFT 4
+#define I3C_MSTATUS_NACKED_MASK 0x00000020
+#define I3C_MSTATUS_NACKED_SHFT 5
+#define I3C_MSTATUS_IBITYPE_MASK 0x000000C0
+#define I3C_MSTATUS_IBITYPE_SHFT 6
+#define I3C_MSTATUS_IBITYPE_NONE 0
+#define I3C_MSTATUS_IBITYPE_NORMAL 1
+#define I3C_MSTATUS_IBITYPE_MASTER_REQ 2
+#define I3C_MSTATUS_IBITYPE_HOT_JOIN 3
+#define I3C_MSTATUS_SLVSTART_MASK 0x00000100
+#define I3C_MSTATUS_SLVSTART_SHFT 8
+#define I3C_MSTATUS_MCTRLDONE_MASK 0x00000200
+#define I3C_MSTATUS_MCTRLDONE_SHFT 9
+#define I3C_MSTATUS_COMPLETE_MASK 0x00000400
+#define I3C_MSTATUS_COMPLETE_SHFT 10
+#define I3C_MSTATUS_RXPEND_MASK 0x00000800
+#define I3C_MSTATUS_RXPEND_SHFT 11
+#define I3C_MSTATUS_TXNOTFULL_MASK 0x00001000
+#define I3C_MSTATUS_TXNOTFULL_SHFT 12
+#define I3C_MSTATUS_IBIWON_MASK 0x00002000
+#define I3C_MSTATUS_IBIWON_SHFT 13
+#define I3C_MSTATUS_ERRWARN_MASK 0x00008000
+#define I3C_MSTATUS_ERRWARN_SHFT 15
+#define I3C_MSTATUS_NOWMASTER_MASK 0x00080000
+#define I3C_MSTATUS_NOWMASTER_SHFT 19
+#define I3C_MSTATUS_IBIADDR_MASK 0x7F000000
+#define I3C_MSTATUS_IBIADDR_SHFT 24
+#define I3C_MINT_SLVSTART_MASK 0x00000100
+#define I3C_MINT_MCTRLDONE_MASK 0x00000200
+#define I3C_MINT_COMPLETE_MASK 0x00000400
+#define I3C_MINT_RXPEND_MASK 0x00000800
+#define I3C_MINT_TXNOTFULL_MASK 0x00001000
+#define I3C_MINT_IBIWON_MASK 0x00002000
+#define I3C_MINT_ERRWARN_MASK 0x00008000
+#define I3C_MINT_NOWMASTER_MASK 0x00080000
+#define I3C_MERRWARN_NACK 0x00000004
+#define I3C_MERRWARN_WRABT 0x00000008
+#define I3C_MERRWARN_TERM 0x00000010
+#define I3C_MERRWARN_HPAR 0x00000200
+#define I3C_MERRWARN_HCRC 0x00000400
+#define I3C_MERRWARN_OREAD 0x00010000
+#define I3C_MERRWARN_OWRITE 0x00020000
+#define I3C_MERRWARN_MSGERR 0x00040000
+#define I3C_MERRWARN_INVREQ 0x00080000
+#define I3C_MERRWARN_TIMEOUT 0x00100000
+#define I3C_MDATACTRL_FLUSHTB_MASK 0x00000001
+#define I3C_MDATACTRL_FLUSHTB I3C_MDATACTRL_FLUSHTB_MASK
+#define I3C_MDATACTRL_FLUSHRB_MASK 0x00000002
+#define I3C_MDATACTRL_FLUSHRB I3C_MDATACTRL_FLUSHRB_MASK
+#define I3C_MDATACTRL_UNLOCK_MASK 0x00000008
+#define I3C_MDATACTRL_UNLOCK_TRIG I3C_MDATACTRL_UNLOCK_MASK
+#define I3C_MDATACTRL_TXTRIG_MASK 0x00000030
+#define I3C_MDATACTRL_TXTRIG_SHFT 4
+#define I3C_MDATACTRL_TXTRIG_FIFO_EMPTY (0)
+#define I3C_MDATACTRL_TXTRIG_FIFO_QTR_FULL (1 << I3C_MDATACTRL_TXTRIG_SHFT)
+#define I3C_MDATACTRL_TXTRIG_FIFO_HALF_FULL (2 << I3C_MDATACTRL_TXTRIG_SHFT)
+#define I3C_MDATACTRL_TXTRIG_FIFO_NOT_FULL (3 << I3C_MDATACTRL_TXTRIG_SHFT)
+#define I3C_MDATACTRL_RXTRIG_MASK 0x000000C0
+#define I3C_MDATACTRL_RXTRIG_SHFT 6
+#define I3C_MDATACTRL_RXTRIG_FIFO_NOT_EMPTY (0)
+#define I3C_MDATACTRL_RXTRIG_FIFO_QTR_FULL (1 << I3C_MDATACTRL_RXTRIG_SHFT)
+#define I3C_MDATACTRL_RXTRIG_FIFO_HALF_FULL (2 << I3C_MDATACTRL_RXTRIG_SHFT)
+#define I3C_MDATACTRL_RXTRIG_FIFO_3QTR_FULL (3 << I3C_MDATACTRL_RXTRIG_SHFT)
+#define I3C_MDATACTRL_TXCOUNT_MASK 0x001F0000
+#define I3C_MDATACTRL_TXCOUNT_SHFT 16
+#define I3C_MDATACTRL_RXCOUNT_MASK 0x1F000000
+#define I3C_MDATACTRL_RXCOUNT_SHFT 24
+#define I3C_MDATACTRL_TXFULL_MASK 0x40000000
+#define I3C_MDATACTRL_RXEMPTY_MASK 0x80000000
+#define I3C_MWMSG_SDR_DIR_MASK 0x00000001
+#define I3C_MWMSG_SDR_DIR_READ 1
+#define I3C_MWMSG_SDR_DIR_WRITE 0
+#define I3C_MWMSG_SDR_ADDR_MASK 0x000000FE
+#define I3C_MWMSG_SDR_ADDR_SHFT 1
+#define I3C_MWMSG_SDR_END_MASK 0x00000100
+#define I3C_MWMSG_SDR_END_SHFT 8
+#define I3C_MWMSG_SDR_END_STOP (1 << I3C_MWMSG_SDR_END_SHFT)
+#define I3C_MWMSG_SDR_MODE_MASK 0x00000400
+#define I3C_MWMSG_SDR_MODE_SHFT 10
+#define I3C_MWMSG_SDR_MODE_I2C (1 << I3C_MWMSG_SDR_MODE_SHFT)
+#define I3C_MWMSG_SDR_MODE_I3C (0 << I3C_MWMSG_SDR_MODE_SHFT)
+#define I3C_MWMSG_SDR_LEN_MASK 0x0000F800
+#define I3C_MWMSG_SDR_LEN_SHFT 11
+#define I3C_MWMSG_SDR_DATA_MASK 0x0000FFFF
+#define I3C_MRMSG_SDR_DATA_MASK 0x0000FFFF
+#define I3C_MRMSG_SDR_CLEN_MASK 0x003F0000
+#define I3C_MRMSG_SDR_CLEN_SHFT 16
+#define I3C_MWMSG_DDR_CTRL_LEN_MASK 0x000003FF
+#define I3C_MWMSG_DDR_CTRL_END_MASK 0x00004000
+#define I3C_MWMSG_DDR_CTRL_END_SHFT 14
+#define I3C_MWMSG_DDR_CTRL_END_EXIT_HDR (1 << I3C_MWMSG_DDR_CTRL_END_SHFT)
+#define I3C_MWMSG_DDR_ADDRCMD_HDR_MODE_CMD_MASK 0x0000007F
+#define I3C_MWMSG_DDR_ADDRCMD_DIR_MASK 0x00000080
+#define I3C_MWMSG_DDR_ADDRCMD_DIR_SHFT 7
+#define I3C_MWMSG_DDR_ADDRCMD_DIR_READ (1 << I3C_MWMSG_DDR_ADDRCMD_DIR_SHFT)
+#define I3C_MWMSG_DDR_ADDRCMD_DIR_WRITE (0 << I3C_MWMSG_DDR_ADDRCMD_DIR_SHFT)
+#define I3C_MWMSG_DDR_ADDRCMD_ADDR_MASK 0x0000FE00
+#define I3C_MWMSG_DDR_ADDRCMD_ADDR_SHFT 9
+#define I3C_MWMSG_DDR_DATA_MASK 0x0000FFFF
+#define I3C_MRMSG_DDR_DATA_MASK 0x0000FFFF
+#define I3C_MRMSG_DDR_CLEN_MASK 0x03FF0000
+#define I3C_MRMSG_DDR_CLEN_SHFT 16
+#define I3C_CONFIG_SLVENA_MASK 0x00000001
+#define I3C_CONFIG_SLVENA_SHFT 0
+#define I3C_CONFIG_NACK_MASK 0x00000002
+#define I3C_CONFIG_NACK_SHFT 1
+#define I3C_CONFIG_MATCHSS_MASK 0x00000004
+#define I3C_CONFIG_MATCHSS_SHFT 2
+#define I3C_CONFIG_S0IGNORE_MASK 0x00000008
+#define I3C_CONFIG_S0IGNORE_SHFT 3
+#define I3C_CONFIG_DDROK_MASK 0x00000010
+#define I3C_CONFIG_DDROK_SHFT 4
+#define I3C_CONFIG_IRAND_MASK 0x00000100
+#define I3C_CONFIG_IRAND_SHFT 8
+#define I3C_CONFIG_BAMATCH_MASK 0x00FF0000
+#define I3C_CONFIG_BAMATCH_SHFT 16
+#define I3C_CONFIG_SADDR_MASK 0xFE000000
+#define I3C_CONFIG_SADDR_SHFT 25
+#define I3C_INT_START_MASK 0x00000100
+#define I3C_INT_MATCHED_MASK 0x00000200
+#define I3C_INT_STOP_MASK 0x00000400
+#define I3C_INT_RXPEND_MASK 0x00000800
+#define I3C_INT_TXNOTFULL_MASK 0x00001000
+#define I3C_INT_DACHG_MASK 0x00002000
+#define I3C_INT_CCC_MASK 0x00004000
+#define I3C_INT_ERRWARN_MASK 0x00008000
+#define I3C_INT_DDRMATCH_MASK 0x00010000
+#define I3C_INT_CHANDLED_MASK 0x00020000
+#define I3C_INT_EVENT_MASK 0x00040000
+
+#define I3C_HW_FIFO_DEPTH 16
+
+#define I3C_DRV_VER_MAJOR 0
+#define I3C_DRV_VER_MINOR 1
+
+typedef __u32 uint32;
+typedef __s32 int32;
+typedef __u16 uint16;
+typedef __s16 int16;
+typedef __u8 uint8;
+typedef __s8 int8;
+
+typedef void (*fpEventHandler_t)(void *vpDriver, uint32 event);
+
+typedef enum {
+ SVC_DRV_CTRL_DISABLE = 0,
+ SVC_DRV_CTRL_ENABLE = 1
+} svc_drv_ctrl_enable_t;
+
+#define SVC_DRV_ERR_NONE (0)
+#define SVC_DRV_ERR_INVALID_PARAM (-EINVAL)
+#define SVC_DRV_ERR_NOT_SUPPORTED (-EPERM)
+#define SVC_DRV_ERR_NOT_IMPLEMENTED (-EPERM)
+#define SVC_DRV_ERR_I3C_TOO_MANY_DEV (-EPERM)
+#define SVC_DRV_ERR_I3C_BUSY (-EBUSY)
+#define SVC_DRV_ERR_I3C_XFER_ERR (-EIO)
+
+#define PUT_UINT32(_addr, _val) writel(_val, _addr)
+#define PUT_UINT16(_addr, _val) writew(_val, _addr)
+#define PUT_UINT8(_addr, _val) writeb(_val, _addr)
+
+#define GET_UINT32(_addr, _pVal) *_pVal = readl(_addr)
+#define GET_UINT16(_addr, _pVal) *_pVal = readw(_addr)
+#define GET_UINT8(_addr, _pVal) *_pVal = readb(_addr)
+
+#define svc_printf printk
+
+#define I3C_MAX_DEVICES 16
+
+#define I3C_PROV_ID_SIZE 8
+
+#define I3C_PPBAUD_MAX 15
+#define I3C_PPLOW_MAX 15
+#define I3C_ODBAUD_MAX 255
+#define I3C_SKEW_MAX 7
+#define I3C_I2CBAUD_MAX 15
+
+#ifndef I3C_BROADCAST_ADDR
+#define I3C_BROADCAST_ADDR 0x7E /*!< I3C Broadcast Address */
+#endif
+
+#define I3C_INVALID_ADDR 0xFF
+
+#define IBI_DATA_MAX_SIZE 4
+
+typedef enum {
+ I3C_CTRL_GET_VERSION,
+ I3C_CTRL_CFG_MASTER_ENABLE,
+ I3C_CTRL_CFG_TIMEOUT_ERR_ENABLE,
+ I3C_CTRL_CFG_HIGH_KEEPER,
+ I3C_CTRL_CFG_STOP_SPEED,
+ I3C_CTRL_CFG_PPBAUD,
+ I3C_CTRL_CFG_PPLOW,
+ I3C_CTRL_CFG_ODBAUD,
+ I3C_CTRL_CFG_ODHPP,
+ I3C_CTRL_CFG_SKEW,
+ I3C_CTRL_CFG_I2CBAUD,
+ I3C_CTRL_ENABLE,
+ I3C_CTRL_DISABLE,
+ I3C_CTRL_ENTER_DAA,
+ I3C_CTRL_ABORT_DAA,
+ I3C_CTRL_START_IBI,
+ I3C_CTRL_NACK_IBI,
+ I3C_CTRL_GET_DEV_AVAIL_FLAGS,
+
+ I3C_CTRL_GET_NUM_BUS_DEVICES,
+ I3C_CTRL_GET_SLV_ATTRIB,
+ I3C_CTRL_GET_IBI_DEV_ADDR,
+ I3C_CTRL_GET_IBI_TYPE,
+ I3C_CTRL_GET_IBI_DATA,
+ I3C_CTRL_ISSUE_STOP,
+
+} svc_i3c_ctrl_t;
+
+typedef enum {
+ I3C_EVENT_NONE = 0,
+ I3C_EVENT_DAA_COMPLETE,
+ I3C_EVENT_RW_COMPLETE,
+ I3C_EVENT_RW_NACK,
+ I3C_EVENT_RW_ERR,
+ I3C_EVENT_RW_TIMEOUT,
+ I3C_EVENT_IBI_REQUEST,
+ I3C_EVENT_IBI_COMPLETE,
+
+} svc_i3c_event_t;
+
+typedef enum {
+ I3C_MASTER_DISABLE,
+ I3C_MASTER_ENABLE,
+ I3C_MASTER_CAPABLE
+
+} svc_i3c_master_enable_t;
+
+typedef enum {
+ I3C_HIGH_KEEPER_NONE,
+ I3C_HIGH_KEEPER_ON_CHIP,
+ I3C_HIGH_KEEPER_EXTERN_SDA,
+ I3C_HIGH_KEEPER_EXTERN_SDA_SCL
+
+} svc_i3c_high_keeper_t;
+
+typedef enum {
+ I3C_STOP_SPEED_PUSH_PULL,
+ I3C_STOP_SPEED_OPEN_DRAIN
+
+} svc_i3c_stop_speed_t;
+
+typedef enum {
+ I3C_ODHPP_SCL_HIGH_EQ_SCL_LOW = 0,
+ I3C_ODHPP_SCL_HIGH_EQ_ONE_PPBAUD = 1
+
+} svc_i3c_odhpp_t;
+
+typedef enum {
+ I3C_ADDRESS_TYPE_UNASSIGNED,
+ I3C_ADDRESS_TYPE_STATIC,
+ I3C_ADDRESS_TYPE_DYNAMIC
+
+} svc_i3c_addr_type_t;
+
+typedef enum {
+ I3C_XFER_TYPE_I2C_READ = 0,
+ I3C_XFER_TYPE_I2C_WRITE = 1,
+ I3C_XFER_TYPE_I3C_READ = 2,
+ I3C_XFER_TYPE_I3C_WRITE = 3,
+ I3C_XFER_TYPE_I3C_READ_DDR = 4,
+ I3C_XFER_TYPE_I3C_WRITE_DDR = 5,
+
+ I3C_XFER_TYPE_I3C_BCCC_WRITE = 6,
+ I3C_XFER_TYPE_I3C_DCCC_READ = 7,
+ I3C_XFER_TYPE_I3C_DCCC_WRITE = 8,
+
+ I3C_XFER_TYPE_MAX = 9
+
+} svc_i3c_xfer_type_t;
+
+typedef enum {
+ I3C_STATE_IDLE = 0,
+ I3C_STATE_XFER_ACTIVE,
+ I3C_STATE_DAA,
+ I3C_STATE_AUTO_IBI,
+ I3C_STATE_IBI_REQ,
+ I3C_STATE_IBI_RESP
+
+} svc_i3c_state_t;
+
+typedef enum {
+ I3C_CCC_B_ENEC = 0x00,
+ I3C_CCC_B_DISEC = 0x01,
+ I3C_CCC_B_ENTAS0 = 0x02,
+ I3C_CCC_B_ENTAS1 = 0x03,
+ I3C_CCC_B_ENTAS2 = 0x04,
+ I3C_CCC_B_ENTAS3 = 0x05,
+ I3C_CCC_B_RSTDAA = 0x06,
+ I3C_CCC_B_ENTDAA = 0x07,
+ I3C_CCC_B_DEFSLVS = 0x08,
+ I3C_CCC_B_SETMWL = 0x09,
+ I3C_CCC_B_SETMRL = 0x0a,
+ I3C_CCC_B_ENTTM = 0x0b,
+ I3C_CCC_B_ENTHDR0 = 0x20,
+ I3C_CCC_B_ENTHDR1 = 0x21,
+ I3C_CCC_B_ENTHDR2 = 0x22,
+ I3C_CCC_B_ENTHDR3 = 0x23,
+ I3C_CCC_B_ENTHDR4 = 0x24,
+ I3C_CCC_B_ENTHDR5 = 0x25,
+ I3C_CCC_B_ENTHDR6 = 0x26,
+ I3C_CCC_B_ENTHDR7 = 0x27,
+ I3C_CCC_B_SETXTIME = 0x28,
+ I3C_CCC_D_ENEC = 0x80,
+ I3C_CCC_D_DISEC = 0x81,
+ I3C_CCC_D_ENTAS0 = 0x82,
+ I3C_CCC_D_ENTAS1 = 0x83,
+ I3C_CCC_D_ENTAS2 = 0x84,
+ I3C_CCC_D_ENTAS3 = 0x85,
+ I3C_CCC_D_RSTDAA = 0x86,
+ I3C_CCC_D_SETDASA = 0x87,
+ I3C_CCC_D_SETNEWDA = 0x88,
+ I3C_CCC_D_SETMWL = 0x89,
+ I3C_CCC_D_SETMRL = 0x8a,
+ I3C_CCC_D_GETMWL = 0x8b,
+ I3C_CCC_D_GETMRL = 0x8c,
+ I3C_CCC_D_GETPID = 0x8d,
+ I3C_CCC_D_GETBCR = 0x8e,
+ I3C_CCC_D_GETDCR = 0x8f,
+ I3C_CCC_D_GETSTATUS = 0x90,
+ I3C_CCC_D_GETACCMST = 0x91,
+ I3C_CCC_D_SETBRGTGT = 0x93,
+ I3C_CCC_D_GETMXDS = 0x94,
+ I3C_CCC_D_GETHDRCAP = 0x95,
+ I3C_CCC_D_SETXTIME = 0x98,
+ I3C_CCC_D_GETXTIME = 0x99
+
+} svc_i3c_ccc_t;
+
+typedef enum {
+ I3C_IBI_TYPE_NONE = 0,
+ I3C_IBI_TYPE_NORMAL = 1,
+ I3C_IBI_TYPE_MASTER_REQ = 2,
+ I3C_IBI_TYPE_HOT_JOIN_REQ = 3,
+
+} svc_i3c_ibi_type_t;
+
+struct __attribute__((__packed__)) svc_i3c_vendor_slv_id_s {
+ uint16 part;
+ uint8 instance;
+ uint16 extra;
+};
+
+typedef struct __attribute__((__packed__))
+svc_i3c_vendor_slv_id_s svc_i3c_vendor_slv_id_t;
+
+typedef struct svc_i3c_slv_id_s {
+ uint16 manufId;
+ uint8 randIdFlag;
+
+ union {
+ uint32 randId;
+ struct __attribute__((__packed__))
+ svc_i3c_vendor_slv_id_s vendId;
+ };
+
+} svc_i3c_slv_id_t;
+
+#define I3C_BCR_SPEED_LIMIT_MASK 0x01
+#define I3C_BCR_IBI_REQ_CAPABLE_MASK 0x02
+#define I3C_BCR_IBI_PAYLOAD_MASK 0x04
+#define I3C_BCR_OFFLINE_CAPABLE_MASK 0x08
+#define I3C_BCR_BRIDGE_DEVICE_MASK 0x10
+#define I3C_BCR_HDR_CAPABLE_MASK 0x20
+#define I3C_BCR_DEVICE_ROLE_MASK 0xC0
+#define I3C_BCR_DEVICE_ROLE_MASTER 0x40
+
+typedef struct svc_i3c_slv_bcr_s {
+ uint32 speedLimit : 1;
+ uint32 ibiReqCapable : 1;
+ uint32 ibiPayload : 1;
+ uint32 offlineCapable : 1;
+ uint32 bridgeDevice : 1;
+ uint32 hdrCapable : 1;
+ uint32 deviceRoleMaster : 1;
+
+} svc_i3c_slv_bcr_t;
+
+typedef struct svc_i3c_slv_attrib_s {
+ uint8 devIdx;
+ uint8 addr;
+ svc_i3c_addr_type_t addrType;
+ svc_i3c_slv_id_t devId;
+ svc_i3c_slv_bcr_t bcr;
+ uint8 dcr;
+
+} svc_i3c_slv_attrib_t;
+
+typedef struct svc_i3c_slv_s {
+ uint8 addr;
+ svc_i3c_addr_type_t addrType;
+
+ uint8 provId[I3C_PROV_ID_SIZE];
+
+} svc_i3c_slv_dev_t;
+
+typedef struct svc_i3c_xfer_s {
+ uint8 devId;
+ svc_i3c_xfer_type_t type;
+ uint8 ccc;
+ uint16 hdrDdrCmd;
+ uint8 *pData;
+ uint8 count;
+
+ uint8 remaining;
+ uint8 stop;
+ uint8 allowIbi;
+
+ struct completion comp;
+
+} svc_i3c_xfer_t;
+
+typedef struct svc_i3c_ibi_s {
+ uint8 ibiData[IBI_DATA_MAX_SIZE];
+ uint8 ibiIdx;
+ uint8 ibiDevAddr;
+ uint8 ibiType;
+
+} svc_i3c_ibi_t;
+
+typedef struct svc_i3c_dev_s {
+ svc_i3c_master_enable_t masterEna;
+ svc_drv_ctrl_enable_t timeoutErrEna;
+ svc_i3c_high_keeper_t highKeeper;
+ svc_i3c_stop_speed_t stopSpeed;
+ uint8 ppBaud;
+ uint8 ppLow;
+ uint8 odBaud;
+ svc_i3c_odhpp_t odHpp;
+ uint8 skew;
+ uint8 i2cBaud;
+
+ svc_i3c_slv_dev_t devices[I3C_MAX_DEVICES];
+ uint32 devAvailableFlags;
+
+ uint8 numDevices;
+
+ int daaDevIdx;
+ uint8 *pDaaData;
+
+ svc_i3c_state_t state;
+ svc_i3c_xfer_t *pXfer;
+
+ uint8 ibiData[IBI_DATA_MAX_SIZE];
+ uint8 ibiIdx;
+ uint8 ibiDevAddr;
+ uint8 ibiType;
+
+} svc_i3c_dev_t;
+
+struct svc_i3c_master {
+ struct i3c_master_controller base;
+ spinlock_t access_lock;
+
+ struct {
+ unsigned int num_ibi_devices;
+ struct i3c_dev_desc **dev_desc;
+ spinlock_t lock;
+ } ibi;
+
+ void __iomem *baseAddr;
+
+ struct clk *pclk;
+ struct clk *fclk_i3c;
+ struct clk *clk_slow_12_5;
+
+ uint32 i3c_pp_clk_div;
+ uint32 i3c_od_clk_div;
+ uint32 i2c_clk_div;
+
+ fpEventHandler_t eventHandler;
+
+ svc_i3c_dev_t *pDev;
+};
+
+void svcI3cMasterIsr(void *arg);
+
+void svcI3cInterruptEnable(void *vpDriver, uint32 mask);
+uint32 svcI3cInterruptDisable(void *vpDriver);
+
+int32 svcI3cInitialize(void *vpDriver, svc_i3c_dev_t *pDev, uint32 baseAddress,
+ uint32 irqBitmask);
+
+int32 svcI3cRegisterEventHandler(void *vpDriver, fpEventHandler_t handler);
+
+int svcI3cAddStaticDevice(void *vpDriver, uint8 addr);
+
+int svcI3cSetDeviceAddress(void *vpDriver, uint8 devIdx, uint8 addr);
+
+uint8 svcI3cGetDeviceIndexFromAddress(void *vpDriver, uint8 addr);
+
+int32 svcI3cRead(void *vpDriver, void *pData);
+
+int32 svcI3cWrite(void *vpDriver, void *pData);
+
+int32 svcI3cAbort(void *vpDriver, svc_i3c_xfer_t *pXfer);
+
+int32 svcI3cControlSet(void *vpDriver, uint32 control, uint32 arg);
+
+int32 svcI3cControlGet(void *vpDriver, uint32 control, void *arg);
+
+static void i3cDisable(void *pDriver)
+{
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)pDriver;
+ PUT_UINT32(pDrv->baseAddr + I3C_MINTCLR_OFFSET, 0xFFFFFFFF);
+ PUT_UINT32(pDrv->baseAddr + I3C_CONFIG_OFFSET, 0);
+ PUT_UINT32(pDrv->baseAddr + I3C_MCONFIG_OFFSET, 0);
+}
+
+static void i3cEnable(void *pDriver)
+{
+ svc_i3c_dev_t *pDev;
+ uint32 reg = 0;
+ uint32 mstatus;
+ uint32 mask;
+
+ if (pDriver) {
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)pDriver;
+
+ if (pDrv->pDev) {
+ pDev = pDrv->pDev;
+
+ switch (pDev->masterEna) {
+ case I3C_MASTER_ENABLE:
+ PUT_UINT32(pDrv->baseAddr + I3C_CONFIG_OFFSET,
+ I3C_CONFIG_DDROK_MASK);
+
+ reg = I3C_MCONFIG_MSTENA_ENABLE;
+
+ if (pDev->timeoutErrEna ==
+ SVC_DRV_CTRL_DISABLE) {
+ reg |= I3C_MCONFIG_DISTO_MASK;
+ }
+
+ switch (pDev->highKeeper) {
+ case I3C_HIGH_KEEPER_ON_CHIP:
+ reg |= (I3C_MCONFIG_HKEEP_ON_CHIP
+ << I3C_MCONFIG_HKEEP_SHFT);
+ break;
+ case I3C_HIGH_KEEPER_EXTERN_SDA:
+ reg |= (I3C_MCONFIG_HKEEP_EXT_SDA
+ << I3C_MCONFIG_HKEEP_SHFT);
+ break;
+ case I3C_HIGH_KEEPER_EXTERN_SDA_SCL:
+ reg |= (I3C_MCONFIG_HKEEP_EXT_SDA_SCL
+ << I3C_MCONFIG_HKEEP_SHFT);
+ break;
+ default:
+ break;
+ }
+
+ if (pDev->stopSpeed ==
+ I3C_STOP_SPEED_OPEN_DRAIN) {
+ reg |= I3C_MCONFIG_ODSTOP_MASK;
+ }
+
+ reg |= ((pDev->ppBaud
+ << I3C_MCONFIG_PPBAUD_SHFT) |
+ (pDev->ppLow
+ << I3C_MCONFIG_PPLOW_SHFT) |
+ (pDev->odBaud
+ << I3C_MCONFIG_ODBAUD_SHFT) |
+ (pDev->odHpp
+ << I3C_MCONFIG_ODHPP_SHFT) |
+ (pDev->skew << I3C_MCONFIG_SKEW_SHFT) |
+ (pDev->i2cBaud
+ << I3C_MCONFIG_I2CBAUD_SHFT));
+
+ PUT_UINT32(pDrv->baseAddr + I3C_MCONFIG_OFFSET,
+ reg);
+
+ GET_UINT32(pDrv->baseAddr + I3C_MSTATUS_OFFSET,
+ &mstatus);
+
+ if (mstatus & I3C_MSTATUS_ERRWARN_MASK) {
+ uint32 errWarn;
+
+ GET_UINT32(pDrv->baseAddr +
+ I3C_MERRWARN_OFFSET,
+ &errWarn);
+
+ PUT_UINT32(pDrv->baseAddr +
+ I3C_MERRWARN_OFFSET,
+ errWarn);
+ }
+
+ reg = I3C_MDATACTRL_UNLOCK_TRIG |
+ I3C_MDATACTRL_RXTRIG_FIFO_NOT_EMPTY |
+ I3C_MDATACTRL_TXTRIG_FIFO_NOT_FULL |
+ I3C_MDATACTRL_FLUSHRB |
+ I3C_MDATACTRL_FLUSHTB;
+ PUT_UINT32(pDrv->baseAddr +
+ I3C_MDATACTRL_OFFSET,
+ reg);
+
+ mask = I3C_MINT_RXPEND_MASK |
+ I3C_MINT_SLVSTART_MASK |
+ I3C_MINT_MCTRLDONE_MASK |
+ I3C_MINT_COMPLETE_MASK |
+ I3C_MINT_IBIWON_MASK |
+ I3C_MINT_ERRWARN_MASK;
+ svcI3cInterruptEnable((void *)pDrv, mask);
+
+ break;
+
+ case I3C_MASTER_CAPABLE:
+
+ break;
+ case I3C_MASTER_DISABLE:
+
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+static int i3cGetNextAvailableDevIdx(svc_i3c_dev_t *pDev)
+{
+ int i;
+
+ for (i = 0; i < I3C_MAX_DEVICES; i++) {
+ if (pDev->devAvailableFlags & (0x00000001 << i)) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static int32 i3cEnterDynamicAddressAssignment(void *vpDriver)
+{
+ int32 status = SVC_DRV_ERR_I3C_BUSY;
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+ svc_i3c_dev_t *pDev = (svc_i3c_dev_t *)(pDrv->pDev);
+ int devIdx;
+
+ if (pDev->state == I3C_STATE_IDLE) {
+ devIdx = i3cGetNextAvailableDevIdx(pDev);
+ if (devIdx >= 0) {
+ if (pDev->devices[devIdx].addr != I3C_INVALID_ADDR) {
+ pDev->state = I3C_STATE_DAA;
+
+ pDev->daaDevIdx = devIdx;
+
+ pDev->pDaaData = pDev->devices[devIdx].provId;
+
+ PUT_UINT32(pDrv->baseAddr + I3C_MCTRL_OFFSET,
+ I3C_MCTRL_REQUEST_PROC_DAA |
+ I3C_MCTRL_IBIRESP_MANUAL);
+ status = SVC_DRV_ERR_NONE;
+ } else {
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ }
+ } else {
+ status = SVC_DRV_ERR_I3C_TOO_MANY_DEV;
+ }
+ }
+
+ return status;
+}
+
+static int32 i3cExitDynamicAddressAssignment(void *vpDriver)
+{
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+ svc_i3c_dev_t *pDev = (svc_i3c_dev_t *)(pDrv->pDev);
+
+ pDev->state = I3C_STATE_IDLE;
+
+ pDev->daaDevIdx = -1;
+ pDev->pDaaData = NULL;
+
+ if (pDrv->eventHandler) {
+ pDrv->eventHandler((void *)pDrv, I3C_EVENT_DAA_COMPLETE);
+ }
+
+ return SVC_DRV_ERR_NONE;
+}
+
+static int32 i3cGetDeviceAttributes(void *vpDriver,
+ svc_i3c_slv_attrib_t *pAttrib)
+{
+ int32 status = SVC_DRV_ERR_NONE;
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+ svc_i3c_dev_t *pDev = (svc_i3c_dev_t *)(pDrv->pDev);
+ svc_i3c_slv_dev_t *pSlvDev;
+ uint16 tmpId;
+
+ if (pAttrib->devIdx < I3C_MAX_DEVICES) {
+ pSlvDev = &pDev->devices[pAttrib->devIdx];
+
+ pAttrib->addr = pSlvDev->addr;
+ pAttrib->addrType = pSlvDev->addrType;
+
+ tmpId = ((uint16)pSlvDev->provId[0] << 8) |
+ (uint16)pSlvDev->provId[1];
+ pAttrib->devId.randIdFlag = tmpId & 0x0001;
+ pAttrib->devId.manufId = tmpId >> 1;
+ if (tmpId & 0x0001) {
+ pAttrib->devId.randId =
+ ((uint32)pSlvDev->provId[2] << 24) |
+ ((uint32)pSlvDev->provId[3] << 16) |
+ ((uint32)pSlvDev->provId[4] << 8) |
+ (uint32)pSlvDev->provId[5];
+ } else {
+ pAttrib->devId.vendId.part =
+ ((uint16)pSlvDev->provId[2] << 8) |
+ (uint16)pSlvDev->provId[3];
+ pAttrib->devId.vendId.instance =
+ pSlvDev->provId[4] >> 4;
+ pAttrib->devId.vendId.extra =
+ (((uint16)pSlvDev->provId[4] << 8) & 0x0f00) |
+ (uint16)pSlvDev->provId[5];
+ }
+
+ pAttrib->bcr.speedLimit =
+ (pSlvDev->provId[6] & I3C_BCR_SPEED_LIMIT_MASK) ? 1 : 0;
+ pAttrib->bcr.ibiReqCapable =
+ (pSlvDev->provId[6] & I3C_BCR_IBI_REQ_CAPABLE_MASK) ?
+ 1 :
+ 0;
+ pAttrib->bcr.ibiPayload =
+ (pSlvDev->provId[6] & I3C_BCR_IBI_PAYLOAD_MASK) ? 1 : 0;
+ pAttrib->bcr.offlineCapable =
+ (pSlvDev->provId[6] & I3C_BCR_OFFLINE_CAPABLE_MASK) ?
+ 1 :
+ 0;
+ pAttrib->bcr.bridgeDevice =
+ (pSlvDev->provId[6] & I3C_BCR_BRIDGE_DEVICE_MASK) ? 1 :
+ 0;
+ pAttrib->bcr.hdrCapable =
+ (pSlvDev->provId[6] & I3C_BCR_HDR_CAPABLE_MASK) ? 1 : 0;
+ pAttrib->bcr.deviceRoleMaster =
+ (pSlvDev->provId[6] & I3C_BCR_DEVICE_ROLE_MASTER) ? 1 :
+ 0;
+
+ pAttrib->dcr = pSlvDev->provId[7];
+ } else {
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ }
+
+ return status;
+}
+
+static int32 i3cWriteCtrlMode(void *vpDriver, void *pData)
+{
+ int32 status = SVC_DRV_ERR_I3C_XFER_ERR;
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+ volatile uint32 reg;
+ volatile uint32 mstatusReg = 0;
+ uint8 addr;
+ uint8 bufIdx;
+
+ svc_i3c_dev_t *pDev;
+ svc_i3c_xfer_t *pXfer;
+
+ if (pDrv && pData) {
+ pDev = pDrv->pDev;
+
+ if (pDev->state == I3C_STATE_IDLE) {
+ pDev->state = I3C_STATE_XFER_ACTIVE;
+
+ pXfer = (svc_i3c_xfer_t *)pData;
+
+ reg = I3C_MDATACTRL_FLUSHTB;
+ PUT_UINT32(pDrv->baseAddr + I3C_MDATACTRL_OFFSET, reg);
+
+ pDev->pXfer = pXfer;
+
+ bufIdx = 0;
+ pXfer->remaining = pXfer->count;
+
+ if (pXfer->type == I3C_XFER_TYPE_I3C_WRITE_DDR) {
+ if (pXfer->count > 0) {
+ PUT_UINT32(pDrv->baseAddr +
+ I3C_MWDATAB_OFFSET,
+ (uint32)(pXfer->hdrDdrCmd &
+ 0x000000FF));
+ } else {
+ PUT_UINT32(pDrv->baseAddr +
+ I3C_MWDATABE_OFFSET,
+ (uint32)(pXfer->hdrDdrCmd &
+ 0x000000FF));
+ }
+ }
+
+ while (pXfer->remaining > 0) {
+ GET_UINT32(pDrv->baseAddr + I3C_MSTATUS_OFFSET,
+ &mstatusReg);
+ if (mstatusReg & I3C_MSTATUS_TXNOTFULL_MASK) {
+ if (pXfer->remaining > 1) {
+ PUT_UINT32(
+ pDrv->baseAddr +
+ I3C_MWDATAB_OFFSET,
+ (uint32)(
+ pXfer->pData
+ [bufIdx++] &
+ 0x000000FF));
+ } else {
+ PUT_UINT32(
+ pDrv->baseAddr +
+ I3C_MWDATABE_OFFSET,
+ (uint32)(
+ pXfer->pData
+ [bufIdx++] &
+ 0x000000FF));
+ }
+ pXfer->remaining--;
+ } else {
+ PUT_UINT32(pDrv->baseAddr +
+ I3C_MINTSET_OFFSET,
+ I3C_MINT_TXNOTFULL_MASK);
+
+ break;
+ }
+ }
+
+ if (pXfer->type == I3C_XFER_TYPE_I3C_BCCC_WRITE) {
+ addr = I3C_BROADCAST_ADDR;
+ } else {
+ addr = pDev->devices[pXfer->devId].addr & 0x7F;
+ }
+
+ if (pXfer->allowIbi) {
+ reg = I3C_MCTRL_IBIRESP_MANUAL;
+ } else {
+ reg = I3C_MCTRL_IBIRESP_NACK;
+ }
+
+ reg |= I3C_MCTRL_REQUEST_START_ADDR |
+ I3C_MCTRL_DIR_WRITE |
+ (addr << I3C_MCTRL_ADDR_SHFT);
+
+ switch (pXfer->type) {
+ case I3C_XFER_TYPE_I2C_WRITE:
+ reg |= I3C_MCTRL_TYPE_I2C;
+ break;
+ case I3C_XFER_TYPE_I3C_WRITE:
+ case I3C_XFER_TYPE_I3C_DCCC_WRITE:
+ case I3C_XFER_TYPE_I3C_BCCC_WRITE:
+ reg |= I3C_MCTRL_TYPE_I3C;
+ break;
+ case I3C_XFER_TYPE_I3C_WRITE_DDR:
+ reg |= I3C_MCTRL_TYPE_HDR_DDR;
+ break;
+ default:
+ reg = 0;
+ break;
+ }
+
+ if (reg) {
+ PUT_UINT32(pDrv->baseAddr + I3C_MCTRL_OFFSET,
+ reg);
+ status = SVC_DRV_ERR_NONE;
+ }
+ } else {
+ status = SVC_DRV_ERR_I3C_BUSY;
+ }
+ }
+
+ return status;
+}
+
+static int32 i3cReadCtrlMode(void *vpDriver, void *pData)
+{
+ int32 status = SVC_DRV_ERR_I3C_XFER_ERR;
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+ volatile uint32 reg = 0;
+ uint8 bufIdx, rdCount;
+
+ svc_i3c_dev_t *pDev;
+ svc_i3c_xfer_t *pXfer;
+
+ if (pDrv && pData) {
+ pDev = pDrv->pDev;
+
+ if (pDev->state == I3C_STATE_IDLE) {
+ pDev->state = I3C_STATE_XFER_ACTIVE;
+
+ pXfer = (svc_i3c_xfer_t *)pData;
+
+ reg = I3C_MDATACTRL_FLUSHRB;
+ PUT_UINT32(pDrv->baseAddr + I3C_MDATACTRL_OFFSET, reg);
+
+ pDev->pXfer = pXfer;
+
+ bufIdx = 0;
+
+ pXfer->remaining = pXfer->count;
+
+ if (pXfer->type == I3C_XFER_TYPE_I3C_READ_DDR) {
+ rdCount = 2 + pXfer->count / 2;
+ } else {
+ rdCount = pXfer->count;
+ }
+
+ if (pXfer->allowIbi) {
+ reg = I3C_MCTRL_IBIRESP_MANUAL;
+ } else {
+ reg = I3C_MCTRL_IBIRESP_NACK;
+ }
+
+ reg |= (rdCount << I3C_MCTRL_RDTERM_SHFT) |
+ I3C_MCTRL_REQUEST_START_ADDR |
+ I3C_MCTRL_DIR_READ |
+ (pDev->devices[pXfer->devId].addr
+ << I3C_MCTRL_ADDR_SHFT);
+
+ switch (pXfer->type) {
+ case I3C_XFER_TYPE_I2C_READ:
+ reg |= I3C_MCTRL_TYPE_I2C;
+ break;
+ case I3C_XFER_TYPE_I3C_READ:
+ case I3C_XFER_TYPE_I3C_DCCC_READ:
+ reg |= I3C_MCTRL_TYPE_I3C;
+ break;
+ case I3C_XFER_TYPE_I3C_READ_DDR:
+
+ PUT_UINT32(pDrv->baseAddr + I3C_MWDATAB_OFFSET,
+ (uint32)(pXfer->hdrDdrCmd &
+ 0x000000FF));
+ reg |= I3C_MCTRL_TYPE_HDR_DDR;
+ break;
+ default:
+
+ reg = 0;
+ break;
+ }
+
+ if (reg) {
+ PUT_UINT32(pDrv->baseAddr + I3C_MCTRL_OFFSET,
+ reg);
+ status = SVC_DRV_ERR_NONE;
+ }
+ } else {
+ status = SVC_DRV_ERR_I3C_BUSY;
+ }
+ }
+
+ return status;
+}
+
+static void i3cStop(void *vpDriver)
+{
+ PUT_UINT32(((struct svc_i3c_master *)vpDriver)->baseAddr +
+ I3C_MCTRL_OFFSET,
+ (I3C_MCTRL_REQUEST_STOP << I3C_MCTRL_REQUEST_SHFT));
+}
+
+static void i3cExitHdr(void *vpDriver)
+{
+ PUT_UINT32(((struct svc_i3c_master *)vpDriver)->baseAddr +
+ I3C_MCTRL_OFFSET,
+ (I3C_MCTRL_REQUEST_FORCE_EXIT << I3C_MCTRL_REQUEST_SHFT));
+}
+
+static int32 i3cIbiStart(void *vpDriver)
+{
+ int32 status = SVC_DRV_ERR_NONE;
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+ svc_i3c_dev_t *pDev = (svc_i3c_dev_t *)(pDrv->pDev);
+ volatile uint32 reg = 0;
+
+ if (pDev->state == I3C_STATE_IDLE) {
+ pDev->state = I3C_STATE_IBI_REQ;
+
+ memset((void *)pDev->ibiData, 0x00, IBI_DATA_MAX_SIZE);
+ pDev->ibiIdx = 0;
+ pDev->ibiDevAddr = 0;
+ pDev->ibiType = 0;
+
+ reg = I3C_MCTRL_REQUEST_START_ADDR | I3C_MCTRL_IBIRESP_MANUAL |
+ (I3C_BROADCAST_ADDR << I3C_MCTRL_ADDR_SHFT);
+ PUT_UINT32(pDrv->baseAddr + I3C_MCTRL_OFFSET, reg);
+ } else {
+ status = SVC_DRV_ERR_I3C_BUSY;
+ }
+ return status;
+}
+
+static int32 i3cIbiNack(void *vpDriver)
+{
+ int32 status = SVC_DRV_ERR_NONE;
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+ svc_i3c_dev_t *pDev = (svc_i3c_dev_t *)(pDrv->pDev);
+ volatile uint32 reg = 0;
+
+ if (pDev->state == I3C_STATE_IDLE) {
+ reg = I3C_MCTRL_IBIRESP_NACK | I3C_MCTRL_REQUEST_IBI_ACKNACK;
+ PUT_UINT32(pDrv->baseAddr + I3C_MCTRL_OFFSET, reg);
+ } else {
+ status = SVC_DRV_ERR_I3C_BUSY;
+ }
+ return status;
+}
+
+void svcI3cMasterIsr(void *arg)
+{
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)arg;
+ svc_i3c_dev_t *pDev;
+ volatile uint32 intMask, mstatus, state, reg;
+ volatile uint32 dataCtrl, rxData;
+ uint8 bufIdx;
+ uint8 twoBytes;
+
+ if (pDrv) {
+ GET_UINT32(pDrv->baseAddr + I3C_MINTMASKED_OFFSET, &intMask);
+
+ pDev = (svc_i3c_dev_t *)pDrv->pDev;
+
+ if (intMask & I3C_MINT_RXPEND_MASK) {
+ GET_UINT32(pDrv->baseAddr + I3C_MSTATUS_OFFSET,
+ &mstatus);
+
+ state = mstatus & I3C_MSTATUS_STATE_MASK;
+
+ GET_UINT32(pDrv->baseAddr + I3C_MDATACTRL_OFFSET,
+ &dataCtrl);
+
+ do {
+ if (((dataCtrl & I3C_MDATACTRL_RXCOUNT_MASK) >>
+ I3C_MDATACTRL_RXCOUNT_SHFT) > 1) {
+ twoBytes = 1;
+ } else {
+ twoBytes = 0;
+ }
+
+ switch (state) {
+ case I3C_MSTATUS_STATE_MSGSDR:
+
+ GET_UINT32(pDrv->baseAddr +
+ I3C_MRMSG_SDR_OFFSET,
+ &rxData);
+ break;
+ case I3C_MSTATUS_STATE_DDR:
+
+ GET_UINT32(pDrv->baseAddr +
+ I3C_MRMSG_DDR_OFFSET,
+ &rxData);
+ break;
+ case I3C_MSTATUS_STATE_NORMACT:
+ case I3C_MSTATUS_STATE_DAA:
+ default:
+ if (twoBytes) {
+ GET_UINT32(
+ pDrv->baseAddr +
+ I3C_MRDATAH_OFFSET,
+ &rxData);
+ } else {
+ GET_UINT32(
+ pDrv->baseAddr +
+ I3C_MRDATAB_OFFSET,
+ &rxData);
+ }
+ break;
+ }
+
+ switch (pDev->state) {
+ case I3C_STATE_DAA:
+ if (state == I3C_MSTATUS_STATE_DAA) {
+ if (pDev->pDaaData) {
+ *pDev->pDaaData = (uint8)(
+ rxData &
+ 0x000000ff);
+ pDev->pDaaData++;
+
+ if (twoBytes) {
+ *pDev->pDaaData = (uint8)(
+ (rxData &
+ 0x0000ff00) >>
+ 8);
+ pDev->pDaaData++;
+ }
+ } else {
+ }
+ } else {
+ }
+ break;
+
+ case I3C_STATE_XFER_ACTIVE:
+
+ if (pDev->pXfer != NULL) {
+ bufIdx = pDev->pXfer->count -
+ pDev->pXfer->remaining;
+
+ if (pDev->pXfer->pData) {
+ if ((pDev->pXfer
+ ->remaining >
+ 1) &&
+ (twoBytes == 1)) {
+ pDev->pXfer->pData
+ [bufIdx++] =
+ (uint8)(rxData &
+ 0x000000ff);
+ pDev->pXfer->pData
+ [bufIdx++] = (uint8)(
+ (rxData &
+ 0x0000ff00) >>
+ 8);
+ pDev->pXfer
+ ->remaining -=
+ 2;
+ } else if (pDev->pXfer
+ ->remaining >
+ 0) {
+ pDev->pXfer->pData
+ [bufIdx++] =
+ (uint8)(rxData &
+ 0x000000ff);
+ pDev->pXfer
+ ->remaining--;
+ }
+ }
+ }
+ break;
+ case I3C_STATE_AUTO_IBI:
+ case I3C_STATE_IBI_RESP:
+ if (pDev->ibiData) {
+ pDev->ibiData[pDev->ibiIdx] =
+ (uint8)rxData;
+ pDev->ibiIdx++;
+ }
+ break;
+ case I3C_STATE_IDLE:
+ default:
+ break;
+ }
+
+ GET_UINT32(pDrv->baseAddr +
+ I3C_MDATACTRL_OFFSET,
+ &dataCtrl);
+
+ } while (!(dataCtrl & I3C_MDATACTRL_RXEMPTY_MASK));
+ }
+
+ if (intMask & I3C_MINT_MCTRLDONE_MASK) {
+ PUT_UINT32(pDrv->baseAddr + I3C_MSTATUS_OFFSET,
+ I3C_MINT_MCTRLDONE_MASK);
+
+ GET_UINT32(pDrv->baseAddr + I3C_MSTATUS_OFFSET,
+ &mstatus);
+
+ if (((mstatus & I3C_MSTATUS_STATE_MASK) ==
+ I3C_MSTATUS_STATE_DAA) &&
+ (mstatus & I3C_MSTATUS_BETWEEN_MASK)) {
+ if (pDev->numDevices < I3C_MAX_DEVICES) {
+ int devIdx;
+ int8 devAddr;
+
+ if (pDev->daaDevIdx >= 0) {
+ devAddr =
+ pDev->devices
+ [pDev->daaDevIdx]
+ .addr;
+
+ if (devAddr !=
+ I3C_INVALID_ADDR) {
+ pDev->devices
+ [pDev->daaDevIdx]
+ .addrType =
+ I3C_ADDRESS_TYPE_DYNAMIC;
+
+ pDev->devAvailableFlags &=
+ ~(1
+ << pDev->daaDevIdx);
+
+ PUT_UINT32(
+ pDrv->baseAddr +
+ I3C_MWDATAB_OFFSET,
+ devAddr);
+
+ devIdx =
+ i3cGetNextAvailableDevIdx(
+ pDev);
+ if (devIdx > 0) {
+ pDev->daaDevIdx =
+ devIdx;
+
+ pDev->pDaaData =
+ pDev->devices[devIdx]
+ .provId;
+ } else {
+ pDev->pDaaData =
+ NULL;
+ }
+
+ PUT_UINT32(
+ pDrv->baseAddr +
+ I3C_MCTRL_OFFSET,
+ I3C_MCTRL_REQUEST_PROC_DAA |
+ I3C_MCTRL_IBIRESP_MANUAL);
+
+ pDev->numDevices++;
+ } else {
+ }
+ }
+ }
+ }
+ }
+
+ if (intMask & I3C_MINT_COMPLETE_MASK) {
+ PUT_UINT32(pDrv->baseAddr + I3C_MSTATUS_OFFSET,
+ I3C_MINT_COMPLETE_MASK);
+
+ switch (pDev->state) {
+ case I3C_STATE_XFER_ACTIVE:
+
+ if (!(mstatus & I3C_MSTATUS_IBIWON_MASK)) {
+ if (pDev->pXfer != NULL) {
+ if (pDev->pXfer->stop) {
+ if (pDev->pXfer->type ==
+ I3C_XFER_TYPE_I3C_READ_DDR ||
+ pDev->pXfer->type ==
+ I3C_XFER_TYPE_I3C_WRITE_DDR) {
+ i3cExitHdr(
+ pDrv);
+ } else {
+ i3cStop(pDrv);
+ }
+ }
+
+ if (pDev->pXfer->type ==
+ I3C_XFER_TYPE_I3C_WRITE ||
+ pDev->pXfer->type ==
+ I3C_XFER_TYPE_I2C_WRITE ||
+ pDev->pXfer->type ==
+ I3C_XFER_TYPE_I3C_WRITE_DDR) {
+ PUT_UINT32(
+ pDrv->baseAddr +
+ I3C_MINTCLR_OFFSET,
+ I3C_MINT_TXNOTFULL_MASK);
+
+ pDrv->eventHandler(
+ (void *)pDrv,
+ I3C_EVENT_RW_COMPLETE);
+ } else {
+ pDrv->eventHandler(
+ (void *)pDrv,
+ I3C_EVENT_RW_COMPLETE);
+ }
+
+ pDev->pXfer = NULL;
+ pDev->state = I3C_STATE_IDLE;
+ }
+ }
+ break;
+ case I3C_STATE_DAA:
+ i3cExitDynamicAddressAssignment(pDrv);
+ break;
+ case I3C_STATE_AUTO_IBI:
+ case I3C_STATE_IBI_RESP:
+
+ pDrv->eventHandler((void *)pDrv,
+ I3C_EVENT_IBI_COMPLETE);
+
+ i3cStop(pDrv);
+
+ pDev->state = I3C_STATE_IDLE;
+ break;
+ case I3C_STATE_IDLE:
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (intMask & I3C_MINT_TXNOTFULL_MASK) {
+ PUT_UINT32(pDrv->baseAddr + I3C_MSTATUS_OFFSET,
+ I3C_MINT_TXNOTFULL_MASK);
+
+ if ((pDev->state == I3C_STATE_XFER_ACTIVE) &&
+ (pDev->pXfer != NULL)) {
+ bufIdx = pDev->pXfer->count -
+ pDev->pXfer->remaining;
+
+ if (pDev->pXfer->pData) {
+ while (pDev->pXfer->remaining > 0) {
+ if (pDev->pXfer->remaining >
+ 1) {
+ PUT_UINT32(
+ pDrv->baseAddr +
+ I3C_MWDATAB_OFFSET,
+ (uint32)(
+ pDev->pXfer
+ ->pData[bufIdx]) &
+ 0x000000ff);
+ } else {
+ PUT_UINT32(
+ pDrv->baseAddr +
+ I3C_MWDATABE_OFFSET,
+ (uint32)(
+ pDev->pXfer
+ ->pData[bufIdx]) &
+ 0x000000ff);
+ }
+
+ bufIdx++;
+ pDev->pXfer->remaining--;
+
+ GET_UINT32(
+ pDrv->baseAddr +
+ I3C_MDATACTRL_OFFSET,
+ ®);
+ if (reg &
+ I3C_MDATACTRL_TXFULL_MASK) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (intMask & I3C_MINT_SLVSTART_MASK) {
+ PUT_UINT32(pDrv->baseAddr + I3C_MSTATUS_OFFSET,
+ I3C_MINT_SLVSTART_MASK);
+
+ pDrv->eventHandler((void *)pDrv, I3C_EVENT_IBI_REQUEST);
+ }
+
+ if (intMask & I3C_MINT_IBIWON_MASK) {
+ uint8 addr =
+ (uint8)((mstatus & I3C_MSTATUS_IBIADDR_MASK) >>
+ I3C_MSTATUS_IBIADDR_SHFT);
+ uint8 ibiType =
+ (uint8)((mstatus & I3C_MSTATUS_IBITYPE_MASK) >>
+ I3C_MSTATUS_IBITYPE_SHFT);
+ uint8 i;
+
+ PUT_UINT32(pDrv->baseAddr + I3C_MSTATUS_OFFSET,
+ I3C_MINT_IBIWON_MASK);
+
+ if (pDev->state == I3C_STATE_XFER_ACTIVE) {
+ pDrv->eventHandler((void *)pDrv,
+ I3C_EVENT_RW_ERR);
+
+ pDev->pXfer = NULL;
+
+ pDev->state = I3C_STATE_IBI_REQ;
+ }
+
+ if (pDev->state == I3C_STATE_IBI_REQ) {
+ pDev->state = I3C_STATE_IBI_RESP;
+
+ if (ibiType == I3C_MSTATUS_IBITYPE_HOT_JOIN) {
+ pDev->ibiIdx = 0;
+ memset((void *)pDev->ibiData, 0x00,
+ IBI_DATA_MAX_SIZE);
+ pDev->ibiDevAddr = addr;
+ pDev->ibiType = ibiType;
+
+ reg = (I3C_MCTRL_IBIRESP_ACK_NO_BYTE |
+ I3C_MCTRL_REQUEST_IBI_ACKNACK);
+ } else {
+ reg = (I3C_MCTRL_IBIRESP_NACK |
+ I3C_MCTRL_REQUEST_IBI_ACKNACK);
+
+ for (i = 0; i < I3C_MAX_DEVICES; i++) {
+ if (addr ==
+ pDev->devices[i].addr) {
+ svc_i3c_slv_attrib_t
+ slvAttrib;
+
+ pDev->ibiIdx = 0;
+ memset((void *)pDev
+ ->ibiData,
+ 0x00,
+ IBI_DATA_MAX_SIZE);
+ pDev->ibiDevAddr = addr;
+ pDev->ibiType = ibiType;
+
+ slvAttrib.devIdx = i;
+ if (i3cGetDeviceAttributes(
+ pDrv,
+ &slvAttrib) ==
+ SVC_DRV_ERR_NONE) {
+ if (slvAttrib
+ .bcr
+ .ibiPayload) {
+ reg = (I3C_MCTRL_IBIRESP_ACK_WITH_BYTE |
+ I3C_MCTRL_REQUEST_IBI_ACKNACK);
+ } else {
+ reg = (I3C_MCTRL_IBIRESP_ACK_NO_BYTE |
+ I3C_MCTRL_REQUEST_IBI_ACKNACK);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ PUT_UINT32(pDrv->baseAddr + I3C_MCTRL_OFFSET,
+ reg);
+ }
+ }
+
+ if (intMask & I3C_MINT_ERRWARN_MASK) {
+ GET_UINT32(pDrv->baseAddr + I3C_MERRWARN_OFFSET, ®);
+
+ PUT_UINT32(pDrv->baseAddr + I3C_MERRWARN_OFFSET, reg);
+
+ if ((pDev->state == I3C_STATE_XFER_ACTIVE) &&
+ (pDev->pXfer != NULL)) {
+ if (reg & I3C_MERRWARN_NACK ||
+ reg & I3C_MERRWARN_WRABT) {
+ pDrv->eventHandler((void *)pDrv,
+ I3C_EVENT_RW_NACK);
+ } else if (reg & I3C_MERRWARN_TIMEOUT) {
+ pDrv->eventHandler(
+ (void *)pDrv,
+ I3C_EVENT_RW_TIMEOUT);
+ } else {
+ pDrv->eventHandler((void *)pDrv,
+ I3C_EVENT_RW_ERR);
+ }
+ }
+ }
+
+ if (intMask & I3C_MINT_NOWMASTER_MASK) {
+ PUT_UINT32(pDrv->baseAddr + I3C_MSTATUS_OFFSET,
+ I3C_MINT_NOWMASTER_MASK);
+ }
+ }
+}
+
+void svcI3cInterruptEnable(void *vpDriver, uint32 mask)
+{
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+ PUT_UINT32(pDrv->baseAddr + I3C_MINTSET_OFFSET, mask);
+}
+
+uint32 svcI3cInterruptDisable(void *vpDriver)
+{
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+ uint32 mask;
+
+ GET_UINT32(pDrv->baseAddr + I3C_MINTSET_OFFSET, &mask);
+
+ PUT_UINT32(pDrv->baseAddr + I3C_MINTCLR_OFFSET, mask);
+
+ return mask;
+}
+
+int32 svcI3cInitialize(void *vpDriver, svc_i3c_dev_t *pDev, uint32 baseAddress,
+ uint32 irqBitmask)
+{
+ int i;
+
+ if (vpDriver) {
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+
+ i3cDisable(vpDriver);
+
+ pDev->masterEna = I3C_MASTER_ENABLE;
+ pDev->timeoutErrEna = SVC_DRV_CTRL_ENABLE;
+ pDev->highKeeper = I3C_HIGH_KEEPER_NONE;
+ pDev->stopSpeed = I3C_STOP_SPEED_PUSH_PULL;
+ pDev->ppBaud = 0;
+ pDev->ppLow = 0;
+ pDev->odBaud = 0;
+ pDev->odHpp = I3C_ODHPP_SCL_HIGH_EQ_SCL_LOW;
+ pDev->skew = 0;
+ pDev->i2cBaud = 0;
+
+ for (i = 0; i < I3C_MAX_DEVICES; i++) {
+ pDev->devices[i].addr = I3C_INVALID_ADDR;
+ pDev->devices[i].addrType = I3C_ADDRESS_TYPE_UNASSIGNED;
+ memset((void *)pDev->devices[i].provId, 0x00,
+ sizeof(pDev->devices[i].provId));
+ }
+ pDev->numDevices = 0;
+ pDev->devAvailableFlags = 0xFFFFFFFF;
+
+ pDev->pXfer = NULL;
+
+ pDev->state = I3C_STATE_IDLE;
+
+ pDev->daaDevIdx = -1;
+ pDev->pDaaData = NULL;
+
+ memset((void *)pDev->ibiData, 0x00, IBI_DATA_MAX_SIZE);
+ pDev->ibiIdx = 0;
+ pDev->ibiDevAddr = 0;
+ pDev->ibiType = 0;
+
+ return SVC_DRV_ERR_NONE;
+ }
+
+ return SVC_DRV_ERR_INVALID_PARAM;
+}
+
+int32 svcI3cRegisterEventHandler(void *vpDriver, fpEventHandler_t handler)
+{
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+ if (pDrv) {
+ pDrv->eventHandler = handler;
+
+ return SVC_DRV_ERR_NONE;
+ }
+ return SVC_DRV_ERR_INVALID_PARAM;
+}
+
+int svcI3cAddStaticDevice(void *vpDriver, uint8 addr)
+{
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+ svc_i3c_dev_t *pDev = (svc_i3c_dev_t *)(pDrv->pDev);
+ int devIdx;
+ uint32 irqEna;
+
+ if (addr > 0) {
+ irqEna = svcI3cInterruptDisable(vpDriver);
+
+ devIdx = i3cGetNextAvailableDevIdx(pDev);
+ if (devIdx != I3C_INVALID_ADDR) {
+ pDev->devices[devIdx].addr = addr;
+ pDev->devices[devIdx].addrType =
+ I3C_ADDRESS_TYPE_STATIC;
+ pDev->devAvailableFlags &= ~(0x00000001 << devIdx);
+ pDev->numDevices++;
+ } else {
+ devIdx = SVC_DRV_ERR_I3C_TOO_MANY_DEV;
+ }
+
+ svcI3cInterruptEnable(vpDriver, irqEna);
+ } else {
+ devIdx = SVC_DRV_ERR_INVALID_PARAM;
+ }
+
+ return devIdx;
+}
+
+int svcI3cSetDeviceAddress(void *vpDriver, uint8 devIdx, uint8 addr)
+{
+ int status = SVC_DRV_ERR_NONE;
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+ svc_i3c_dev_t *pDev = (svc_i3c_dev_t *)(pDrv->pDev);
+
+ if (devIdx < I3C_MAX_DEVICES) {
+ if (pDev->devAvailableFlags & (0x1 << devIdx)) {
+ pDev->devices[devIdx].addr = addr;
+ } else {
+ status = SVC_DRV_ERR_I3C_BUSY;
+ }
+ } else {
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ }
+
+ return status;
+}
+
+uint8 svcI3cGetDeviceIndexFromAddress(void *vpDriver, uint8 devAddr)
+{
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+ svc_i3c_dev_t *pDev = (svc_i3c_dev_t *)(pDrv->pDev);
+ uint8 i;
+
+ for (i = 0; i < I3C_MAX_DEVICES; i++) {
+ if (pDev->devices[i].addr == devAddr) {
+ return i;
+ }
+ }
+
+ return I3C_INVALID_ADDR;
+}
+
+int32 svcI3cRead(void *vpDriver, void *pData)
+{
+ int32 status;
+
+ status = i3cReadCtrlMode(vpDriver, pData);
+
+ return status;
+}
+
+int32 svcI3cWrite(void *vpDriver, void *pData)
+{
+ int32 status;
+
+ status = i3cWriteCtrlMode(vpDriver, pData);
+
+ return status;
+}
+
+int32 svcI3cAbort(void *vpDriver, svc_i3c_xfer_t *pXfer)
+{
+ int32 status = SVC_DRV_ERR_I3C_XFER_ERR;
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+ svc_i3c_dev_t *pDev;
+ uint32 intEna;
+
+ if (pDrv) {
+ pDev = pDrv->pDev;
+
+ printk(" %s: aborting transfer\n", __FUNCTION__);
+ printk(" %s: pDev->state = %d\n", __FUNCTION__, pDev->state);
+ printk(" %s: pDev->pXfer = %px\n", __FUNCTION__,
+ pDev->pXfer);
+ printk(" %s: pXfer = %px\n", __FUNCTION__, pXfer);
+
+ intEna = svcI3cInterruptDisable(vpDriver);
+ if ((pDev->state == I3C_STATE_XFER_ACTIVE) &&
+ (pXfer == pDev->pXfer)) {
+ pDev->pXfer = 0;
+ pDev->state = I3C_STATE_IDLE;
+ status = SVC_DRV_ERR_NONE;
+ }
+ svcI3cInterruptEnable(vpDriver, intEna);
+ }
+
+ return status;
+}
+
+int32 svcI3cControlSet(void *vpDriver, uint32 control, uint32 arg)
+{
+ int32 status = SVC_DRV_ERR_NONE;
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+ svc_i3c_dev_t *pDev;
+
+ if (pDrv) {
+ pDev = pDrv->pDev;
+
+ switch (control) {
+ case I3C_CTRL_CFG_MASTER_ENABLE:
+ if (arg <= I3C_MASTER_CAPABLE) {
+ if (arg == I3C_MASTER_ENABLE) {
+ pDev->masterEna =
+ (svc_i3c_master_enable_t)arg;
+ } else {
+ status = SVC_DRV_ERR_NOT_SUPPORTED;
+ }
+ } else {
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ }
+ break;
+
+ case I3C_CTRL_CFG_TIMEOUT_ERR_ENABLE:
+ if (arg == SVC_DRV_CTRL_ENABLE ||
+ arg == SVC_DRV_CTRL_DISABLE) {
+ pDev->timeoutErrEna =
+ (svc_drv_ctrl_enable_t)arg;
+ } else {
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ }
+ break;
+
+ case I3C_CTRL_CFG_HIGH_KEEPER:
+ if (arg <= I3C_HIGH_KEEPER_EXTERN_SDA_SCL) {
+ pDev->highKeeper = (svc_i3c_high_keeper_t)arg;
+ } else {
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ }
+ break;
+
+ case I3C_CTRL_CFG_STOP_SPEED:
+ if (arg == I3C_STOP_SPEED_PUSH_PULL ||
+ arg == I3C_STOP_SPEED_OPEN_DRAIN) {
+ pDev->stopSpeed = (svc_i3c_stop_speed_t)arg;
+ } else {
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ }
+ break;
+
+ case I3C_CTRL_CFG_PPBAUD:
+ if (arg <= I3C_PPBAUD_MAX) {
+ pDev->ppBaud = (uint8)arg;
+ } else {
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ }
+ break;
+
+ case I3C_CTRL_CFG_PPLOW:
+ if (arg <= I3C_PPLOW_MAX) {
+ pDev->ppLow = (uint8)arg;
+ } else {
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ }
+ break;
+
+ case I3C_CTRL_CFG_ODBAUD:
+ if (arg <= I3C_ODBAUD_MAX) {
+ pDev->odBaud = (uint8)arg;
+ } else {
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ }
+ break;
+
+ case I3C_CTRL_CFG_ODHPP:
+ if (arg == I3C_ODHPP_SCL_HIGH_EQ_SCL_LOW ||
+ arg == I3C_ODHPP_SCL_HIGH_EQ_ONE_PPBAUD) {
+ pDev->odHpp = (svc_i3c_odhpp_t)arg;
+ } else {
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ }
+ break;
+
+ case I3C_CTRL_CFG_SKEW:
+ if (arg <= I3C_SKEW_MAX) {
+ pDev->skew = (uint8)arg;
+ } else {
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ }
+ break;
+
+ case I3C_CTRL_CFG_I2CBAUD:
+ if (arg <= I3C_I2CBAUD_MAX) {
+ pDev->i2cBaud = (uint8)arg;
+ } else {
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ }
+ break;
+
+ case I3C_CTRL_ENABLE:
+ i3cEnable(vpDriver);
+ break;
+
+ case I3C_CTRL_DISABLE:
+ i3cDisable(vpDriver);
+ break;
+ case I3C_CTRL_ENTER_DAA:
+ status = i3cEnterDynamicAddressAssignment(vpDriver);
+ break;
+ case I3C_CTRL_ABORT_DAA:
+ status = i3cExitDynamicAddressAssignment(vpDriver);
+ break;
+ case I3C_CTRL_START_IBI:
+ status = i3cIbiStart(vpDriver);
+ break;
+ case I3C_CTRL_NACK_IBI:
+ status = i3cIbiNack(vpDriver);
+ break;
+ case I3C_CTRL_ISSUE_STOP:
+ i3cStop(vpDriver);
+ break;
+ default:
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ break;
+ }
+ } else {
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ }
+
+ return status;
+}
+
+int32 svcI3cControlGet(void *vpDriver, uint32 control, void *arg)
+{
+ int32 status = SVC_DRV_ERR_NONE;
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+
+ uint32 i;
+ svc_i3c_dev_t *pDev;
+ uint32 *pRet32;
+ uint8 *pRet8;
+
+ if (pDrv && arg) {
+ pDev = pDrv->pDev;
+
+ pRet32 = (uint32 *)arg;
+ pRet8 = (uint8 *)arg;
+
+ switch (control) {
+ case I3C_CTRL_GET_VERSION:
+ *pRet32 = (I3C_DRV_VER_MAJOR << 8 | I3C_DRV_VER_MINOR);
+ break;
+ case I3C_CTRL_CFG_MASTER_ENABLE:
+ *pRet32 = pDev->masterEna;
+ break;
+ case I3C_CTRL_CFG_TIMEOUT_ERR_ENABLE:
+ *pRet32 = pDev->timeoutErrEna;
+ break;
+ case I3C_CTRL_CFG_HIGH_KEEPER:
+ *pRet32 = pDev->highKeeper;
+ break;
+ case I3C_CTRL_CFG_STOP_SPEED:
+ *pRet32 = pDev->stopSpeed;
+ break;
+ case I3C_CTRL_CFG_PPBAUD:
+ *pRet32 = pDev->ppBaud;
+ break;
+ case I3C_CTRL_CFG_PPLOW:
+ *pRet32 = pDev->ppLow;
+ break;
+ case I3C_CTRL_CFG_ODBAUD:
+ *pRet32 = pDev->odBaud;
+ break;
+ case I3C_CTRL_CFG_ODHPP:
+ *pRet32 = pDev->odHpp;
+ break;
+ case I3C_CTRL_CFG_SKEW:
+ *pRet32 = pDev->skew;
+ break;
+ case I3C_CTRL_CFG_I2CBAUD:
+ *pRet32 = pDev->i2cBaud;
+ break;
+ case I3C_CTRL_GET_DEV_AVAIL_FLAGS:
+ *pRet32 = pDev->devAvailableFlags;
+ break;
+ case I3C_CTRL_GET_NUM_BUS_DEVICES:
+ *pRet32 = pDev->numDevices;
+ break;
+ case I3C_CTRL_GET_SLV_ATTRIB:
+ status = i3cGetDeviceAttributes(
+ vpDriver, (svc_i3c_slv_attrib_t *)arg);
+ break;
+ case I3C_CTRL_GET_IBI_DEV_ADDR:
+ *pRet32 = pDev->ibiDevAddr;
+ break;
+ case I3C_CTRL_GET_IBI_TYPE:
+
+ switch (pDev->ibiType) {
+ case I3C_MSTATUS_IBITYPE_NORMAL:
+ *pRet32 = I3C_IBI_TYPE_NORMAL;
+ break;
+ case I3C_MSTATUS_IBITYPE_MASTER_REQ:
+ *pRet32 = I3C_IBI_TYPE_MASTER_REQ;
+ break;
+ case I3C_MSTATUS_IBITYPE_HOT_JOIN:
+ *pRet32 = I3C_IBI_TYPE_HOT_JOIN_REQ;
+ break;
+ case I3C_MSTATUS_IBITYPE_NONE:
+ default:
+ *pRet32 = I3C_IBI_TYPE_NONE;
+ break;
+ }
+ break;
+ case I3C_CTRL_GET_IBI_DATA:
+ for (i = 0; i < IBI_DATA_MAX_SIZE; i++) {
+ pRet8[i] = pDev->ibiData[i];
+ }
+ break;
+ default:
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ break;
+ }
+ } else {
+ status = SVC_DRV_ERR_INVALID_PARAM;
+ }
+
+ return status;
+}
+
+#define XFER_TIMEOUT (msecs_to_jiffies(1000))
+#define CCC_TIMEOUT (msecs_to_jiffies(1000))
+#define DAA_TIMEOUT (msecs_to_jiffies(4000))
+
+#define I3C_PP_CLK_DIV_DEFAULT 3
+#define I3C_OD_CLK_DIV_DEFAULT 3
+#define I2C_CLK_DIV_DEFAULT 4
+
+static u32 eventCnt;
+static u32 daaCompleteCnt;
+static u32 rwCompletCnt;
+static u32 rwNackCnt;
+static u32 rwErrorCnt;
+static u32 ibiReqCnt;
+static u32 ibiCompleteCnt;
+
+static DECLARE_COMPLETION(daaComplete);
+
+static int svc_i3c_master_ibi_request_handler(struct svc_i3c_master *master);
+static int svc_i3c_master_ibi_complete_handler(struct svc_i3c_master *master);
+
+struct svc_i3c_i2c_dev_data {
+ u8 index;
+
+ s16 ibi_dev_idx;
+ struct i3c_generic_ibi_pool *ibi_pool;
+};
+
+static inline struct svc_i3c_master *
+to_svc_i3c_master(struct i3c_master_controller *master)
+{
+ return container_of(master, struct svc_i3c_master, base);
+}
+
+static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
+{
+ svcI3cMasterIsr(dev_id);
+
+ return IRQ_HANDLED;
+}
+
+static void i3cEventHandler(void *vpDriver, uint32 event)
+{
+ struct svc_i3c_master *pDrv = (struct svc_i3c_master *)vpDriver;
+
+ eventCnt++;
+
+ switch (event) {
+ case I3C_EVENT_DAA_COMPLETE:
+ daaCompleteCnt++;
+ complete(&daaComplete);
+ break;
+ case I3C_EVENT_RW_COMPLETE:
+ complete(&(pDrv->pDev->pXfer->comp));
+ rwCompletCnt++;
+ break;
+ case I3C_EVENT_RW_NACK:
+ rwNackCnt++;
+ break;
+ case I3C_EVENT_RW_TIMEOUT:
+ case I3C_EVENT_RW_ERR:
+ rwErrorCnt++;
+ break;
+ case I3C_EVENT_IBI_REQUEST:
+ ibiReqCnt++;
+ svc_i3c_master_ibi_request_handler(pDrv);
+ break;
+ case I3C_EVENT_IBI_COMPLETE:
+ ibiCompleteCnt++;
+ svc_i3c_master_ibi_complete_handler(pDrv);
+ break;
+ default:
+ break;
+ }
+}
+
+static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
+{
+ struct svc_i3c_master *master = to_svc_i3c_master(m);
+ struct i3c_device_info info = {};
+ int ret;
+
+ ret = i3c_master_get_free_addr(m, 0);
+ if (ret < 0)
+ return ret;
+
+ memset(&info, 0, sizeof(info));
+ info.dyn_addr = ret;
+
+ ret = i3c_master_set_info(&master->base, &info);
+ if (ret)
+ return ret;
+
+ svcI3cRegisterEventHandler((void *)master, i3cEventHandler);
+ svcI3cControlSet((void *)master, I3C_CTRL_CFG_MASTER_ENABLE,
+ I3C_MASTER_ENABLE);
+
+ svcI3cControlSet((void *)master, I3C_CTRL_CFG_PPLOW, 0);
+
+ if (svcI3cControlSet((void *)master, I3C_CTRL_CFG_PPBAUD,
+ (master->i3c_pp_clk_div - 1)) !=
+ SVC_DRV_ERR_NONE) {
+ svcI3cControlSet((void *)master, I3C_CTRL_CFG_PPBAUD,
+ I3C_PP_CLK_DIV_DEFAULT);
+ }
+
+ if (svcI3cControlSet((void *)master, I3C_CTRL_CFG_ODBAUD,
+ (master->i3c_od_clk_div - 1)) !=
+ SVC_DRV_ERR_NONE) {
+ svcI3cControlSet((void *)master, I3C_CTRL_CFG_ODBAUD,
+ I3C_OD_CLK_DIV_DEFAULT);
+ }
+
+ svcI3cControlSet((void *)master, I3C_CTRL_CFG_ODHPP,
+ I3C_ODHPP_SCL_HIGH_EQ_SCL_LOW);
+ svcI3cControlSet((void *)master, I3C_CTRL_CFG_SKEW, 0);
+
+ if (svcI3cControlSet((void *)master, I3C_CTRL_CFG_I2CBAUD,
+ (master->i3c_od_clk_div - 1)) !=
+ SVC_DRV_ERR_NONE) {
+ svcI3cControlSet((void *)master, I3C_CTRL_CFG_I2CBAUD,
+ I2C_CLK_DIV_DEFAULT);
+ }
+
+ svcI3cControlSet((void *)master, I3C_CTRL_ENABLE, 0);
+
+ return 0;
+}
+
+static int svc_i3c_master_attach_i3c_dev(struct i3c_dev_desc *dev)
+{
+ struct svc_i3c_i2c_dev_data *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->ibi_dev_idx = -1;
+ i3c_dev_set_master_data(dev, data);
+
+ return 0;
+}
+
+static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
+{
+ struct svc_i3c_master *master = to_svc_i3c_master(m);
+ int ret, i;
+ int status = 0;
+ uint8 tempAddr = 0;
+ uint32 origDevAvailFlags, newDevAvailFlags, changedFlags;
+ svc_i3c_slv_attrib_t slvAttrib;
+
+ spin_lock(&master->access_lock);
+
+ svcI3cControlGet((void *)master, I3C_CTRL_GET_DEV_AVAIL_FLAGS,
+ &origDevAvailFlags);
+
+ for (i = 0; i < I3C_MAX_DEVICES; i++) {
+ if (origDevAvailFlags & (0x1 << i)) {
+ ret = i3c_master_get_free_addr(m, tempAddr + 1);
+ if (ret > 0) {
+ tempAddr = (uint8)ret;
+ svcI3cSetDeviceAddress((void *)master, i,
+ tempAddr);
+ }
+ }
+ }
+
+ reinit_completion(&daaComplete);
+
+ ret = svcI3cControlSet((void *)master, I3C_CTRL_ENTER_DAA, 0);
+ if (ret == SVC_DRV_ERR_NONE) {
+ if (!wait_for_completion_timeout(&daaComplete, DAA_TIMEOUT)) {
+ svcI3cControlSet((void *)master, I3C_CTRL_ABORT_DAA, 0);
+
+ status = -ETIME;
+ } else {
+ }
+ } else {
+ status = ret;
+ }
+
+ spin_unlock(&master->access_lock);
+
+ svcI3cControlGet((void *)master, I3C_CTRL_GET_DEV_AVAIL_FLAGS,
+ &newDevAvailFlags);
+
+ changedFlags = origDevAvailFlags - newDevAvailFlags;
+
+ for (i = 0; i < I3C_MAX_DEVICES; i++) {
+ if (changedFlags & (0x1 << i)) {
+ slvAttrib.devIdx = i;
+ svcI3cControlGet((void *)master,
+ I3C_CTRL_GET_SLV_ATTRIB, &slvAttrib);
+
+ ret = i3c_master_add_i3c_dev_locked(m, slvAttrib.addr);
+ } else if (newDevAvailFlags & (0x1 << i)) {
+ svcI3cSetDeviceAddress((void *)master, i,
+ I3C_INVALID_ADDR);
+ }
+ }
+
+ i3c_master_enec_locked(m, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_HJ | I3C_CCC_EVENT_MR);
+
+ return status;
+}
+
+static bool
+svc_i3c_master_supports_ccc_cmd(struct i3c_master_controller *master,
+ const struct i3c_ccc_cmd *cmd)
+{
+ if (cmd->ndests > 1)
+ return false;
+
+ return true;
+}
+
+static int svc_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
+ struct i3c_ccc_cmd *cmd)
+{
+ int status = -1;
+ struct svc_i3c_master *master = to_svc_i3c_master(m);
+ svc_i3c_xfer_t *pI3cXfer;
+ uint8 *pXferBuf;
+ uint8 *pData;
+ int i;
+ int ret;
+
+ pI3cXfer =
+ (svc_i3c_xfer_t *)kzalloc(sizeof(svc_i3c_xfer_t), GFP_KERNEL);
+ if (pI3cXfer == NULL) {
+ return -ENOMEM;
+ }
+
+ pI3cXfer->type = I3C_XFER_TYPE_I3C_BCCC_WRITE;
+
+ if (cmd->rnw) {
+ pI3cXfer->count = 1;
+ pI3cXfer->pData = &cmd->id;
+ pI3cXfer->stop = 0;
+ pI3cXfer->allowIbi = 1;
+
+ init_completion(&pI3cXfer->comp);
+
+ spin_lock(&master->access_lock);
+
+ ret = svcI3cWrite((void *)master, (void *)pI3cXfer);
+ if (ret == SVC_DRV_ERR_NONE) {
+ if (!wait_for_completion_timeout(&pI3cXfer->comp,
+ XFER_TIMEOUT)) {
+ svcI3cAbort((void *)master, pI3cXfer);
+
+ status = -ETIMEDOUT;
+ } else {
+ status = 0;
+ }
+ } else {
+ svcI3cAbort((void *)master, pI3cXfer);
+
+ status = ret;
+ }
+
+ if (status == 0) {
+ status = -1;
+
+ ret = svcI3cGetDeviceIndexFromAddress(
+ (void *)master, cmd->dests[0].addr);
+ if (ret >= 0) {
+ pI3cXfer->devId = (uint8)ret;
+ pI3cXfer->type = I3C_XFER_TYPE_I3C_READ;
+ pI3cXfer->count = cmd->dests[0].payload.len;
+ pI3cXfer->pData = cmd->dests[0].payload.data;
+ pI3cXfer->stop = 1;
+ pI3cXfer->allowIbi = 0;
+
+ reinit_completion(&pI3cXfer->comp);
+
+ ret = svcI3cRead((void *)master,
+ (void *)pI3cXfer);
+ if (ret == SVC_DRV_ERR_NONE) {
+ if (!wait_for_completion_timeout(
+ &pI3cXfer->comp,
+ XFER_TIMEOUT)) {
+ svcI3cAbort((void *)master,
+ pI3cXfer);
+
+ status = -ETIMEDOUT;
+ } else {
+ status = 0;
+ }
+ } else {
+ svcI3cAbort((void *)master, pI3cXfer);
+
+ status = ret;
+ }
+ }
+ }
+
+ spin_unlock(&master->access_lock);
+
+ } else {
+ if (cmd->dests[0].addr == I3C_BROADCAST_ADDR) {
+ pI3cXfer->count = cmd->dests[0].payload.len + 1;
+
+ pXferBuf = kzalloc(pI3cXfer->count, GFP_KERNEL);
+ if (pXferBuf) {
+ pXferBuf[0] = cmd->id;
+ pData = (uint8 *)cmd->dests[0].payload.data;
+ for (i = 1; i < pI3cXfer->count; i++) {
+ pXferBuf[i] = pData[i - 1];
+ }
+
+ pI3cXfer->pData = pXferBuf;
+ pI3cXfer->stop = 1;
+ pI3cXfer->allowIbi = 1;
+
+ init_completion(&pI3cXfer->comp);
+
+ spin_lock(&master->access_lock);
+
+ ret = svcI3cWrite((void *)master,
+ (void *)pI3cXfer);
+ if (ret == SVC_DRV_ERR_NONE) {
+ if (!wait_for_completion_timeout(
+ &pI3cXfer->comp,
+ XFER_TIMEOUT)) {
+ svcI3cAbort((void *)master,
+ pI3cXfer);
+
+ status = -ETIMEDOUT;
+ } else {
+ status = 0;
+ }
+ } else {
+ svcI3cAbort((void *)master, pI3cXfer);
+
+ status = ret;
+ }
+
+ spin_unlock(&master->access_lock);
+
+ kzfree(pXferBuf);
+ } else {
+ status = -ENOMEM;
+ }
+ } else {
+ pI3cXfer->count = 1;
+ pI3cXfer->pData = &cmd->id;
+ pI3cXfer->stop = 0;
+ pI3cXfer->allowIbi = 1;
+
+ init_completion(&pI3cXfer->comp);
+
+ spin_lock(&master->access_lock);
+
+ ret = svcI3cWrite((void *)master, (void *)pI3cXfer);
+ if (ret == SVC_DRV_ERR_NONE) {
+ if (!wait_for_completion_timeout(
+ &pI3cXfer->comp, XFER_TIMEOUT)) {
+ svcI3cAbort((void *)master, pI3cXfer);
+
+ status = -ETIMEDOUT;
+ } else {
+ status = 0;
+ }
+ } else {
+ svcI3cAbort((void *)master, pI3cXfer);
+
+ status = ret;
+ }
+
+ if (status == 0) {
+ status = -1;
+
+ ret = svcI3cGetDeviceIndexFromAddress(
+ (void *)master, cmd->dests[0].addr);
+ if (ret >= 0) {
+ pI3cXfer->devId = (uint8)ret;
+ pI3cXfer->type =
+ I3C_XFER_TYPE_I3C_WRITE;
+ pI3cXfer->count =
+ cmd->dests[0].payload.len;
+ pI3cXfer->pData =
+ cmd->dests[0].payload.data;
+ pI3cXfer->stop = 1;
+ pI3cXfer->allowIbi = 0;
+
+ reinit_completion(&pI3cXfer->comp);
+
+ ret = svcI3cWrite((void *)master,
+ (void *)pI3cXfer);
+ if (ret == SVC_DRV_ERR_NONE) {
+ if (!wait_for_completion_timeout(
+ &pI3cXfer->comp,
+ XFER_TIMEOUT)) {
+ svcI3cAbort(
+ (void *)master,
+ pI3cXfer);
+
+ status = -ETIMEDOUT;
+ } else {
+ status = 0;
+ }
+ } else {
+ svcI3cAbort((void *)master,
+ pI3cXfer);
+
+ status = ret;
+ }
+ }
+ }
+
+ spin_unlock(&master->access_lock);
+ }
+ }
+
+ kzfree(pI3cXfer);
+
+ return status;
+}
+
+static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
+ struct i3c_priv_xfer *xfers, int nxfers)
+{
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct svc_i3c_master *master = to_svc_i3c_master(m);
+ svc_i3c_xfer_t *pI3cXfer;
+ int i;
+ int ret;
+
+ pI3cXfer =
+ (svc_i3c_xfer_t *)kzalloc(sizeof(svc_i3c_xfer_t), GFP_KERNEL);
+ if (pI3cXfer == NULL) {
+ return -ENOMEM;
+ }
+
+ spin_lock(&master->access_lock);
+
+ for (i = 0; i < nxfers; i++) {
+ if (dev->info.dyn_addr) {
+ ret = svcI3cGetDeviceIndexFromAddress(
+ (void *)master, dev->info.dyn_addr);
+ } else {
+ ret = svcI3cGetDeviceIndexFromAddress(
+ (void *)master, dev->info.static_addr);
+ }
+
+ if (ret < 0) {
+ kzfree(pI3cXfer);
+ spin_unlock(&master->access_lock);
+ return -ENODEV;
+ }
+
+ pI3cXfer->devId = (uint8)ret;
+
+ pI3cXfer->count = xfers[i].len;
+
+ if (i < nxfers - 1) {
+ pI3cXfer->stop = 0;
+ } else {
+ pI3cXfer->stop = 1;
+ }
+
+ pI3cXfer->allowIbi = 1;
+
+ init_completion(&pI3cXfer->comp);
+
+ if (xfers[i].rnw) {
+ pI3cXfer->pData = xfers[i].data.in;
+ pI3cXfer->type = I3C_XFER_TYPE_I3C_READ;
+ ret = svcI3cRead((void *)master, (void *)pI3cXfer);
+ } else {
+ pI3cXfer->pData = (uint8 *)(xfers[i].data.out);
+ pI3cXfer->type = I3C_XFER_TYPE_I3C_WRITE;
+ ret = svcI3cWrite((void *)master, (void *)pI3cXfer);
+ }
+
+ if (ret == SVC_DRV_ERR_NONE) {
+ if (!wait_for_completion_timeout(&pI3cXfer->comp,
+ XFER_TIMEOUT)) {
+ svcI3cAbort((void *)master, pI3cXfer);
+
+ spin_unlock(&master->access_lock);
+
+ kzfree(pI3cXfer);
+ return -ETIMEDOUT;
+ }
+ } else {
+ svcI3cAbort((void *)master, pI3cXfer);
+
+ spin_unlock(&master->access_lock);
+
+ kzfree(pI3cXfer);
+ return ret;
+ }
+ }
+
+ spin_unlock(&master->access_lock);
+
+ kzfree(pI3cXfer);
+
+ return 0;
+}
+
+static int svc_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev)
+{
+ struct i3c_master_controller *m = i2c_dev_get_master(dev);
+ struct svc_i3c_master *master = to_svc_i3c_master(m);
+ struct svc_i3c_i2c_dev_data *data;
+ int32 ret;
+
+ ret = svcI3cAddStaticDevice((void *)master, dev->boardinfo->base.addr);
+ if (ret < 0) {
+ return ret;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->index = ret;
+
+ i2c_dev_set_master_data(dev, data);
+
+ return 0;
+}
+
+static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
+ const struct i2c_msg *xfers, int nxfers)
+{
+ struct i3c_master_controller *m = i2c_dev_get_master(dev);
+ struct svc_i3c_master *master = to_svc_i3c_master(m);
+ struct svc_i3c_i2c_dev_data *data = i2c_dev_get_master_data(dev);
+ svc_i3c_xfer_t *pI3cXfer;
+ int i;
+ int ret;
+
+ pI3cXfer =
+ (svc_i3c_xfer_t *)kzalloc(sizeof(svc_i3c_xfer_t), GFP_KERNEL);
+ if (pI3cXfer == NULL) {
+ return -ENOMEM;
+ }
+
+ spin_lock(&master->access_lock);
+
+ for (i = 0; i < nxfers; i++) {
+ pI3cXfer->devId = data->index;
+
+ pI3cXfer->count = xfers[i].len;
+ pI3cXfer->pData = xfers[i].buf;
+
+ if (i < nxfers - 1) {
+ pI3cXfer->stop = 0;
+ } else {
+ pI3cXfer->stop = 1;
+ }
+
+ pI3cXfer->allowIbi = 1;
+
+ init_completion(&pI3cXfer->comp);
+
+ if (xfers[i].flags & I2C_M_RD) {
+ pI3cXfer->type = I3C_XFER_TYPE_I2C_READ;
+ ret = svcI3cRead((void *)master, (void *)pI3cXfer);
+ } else {
+ pI3cXfer->type = I3C_XFER_TYPE_I2C_WRITE;
+ ret = svcI3cWrite((void *)master, (void *)pI3cXfer);
+ }
+
+ if (ret == SVC_DRV_ERR_NONE) {
+ if (!wait_for_completion_timeout(&pI3cXfer->comp,
+ XFER_TIMEOUT)) {
+ svcI3cAbort((void *)master, pI3cXfer);
+
+ spin_unlock(&master->access_lock);
+
+ kzfree(pI3cXfer);
+ return -ETIMEDOUT;
+ }
+ } else {
+ svcI3cAbort((void *)master, pI3cXfer);
+
+ spin_unlock(&master->access_lock);
+
+ kzfree(pI3cXfer);
+ return ret;
+ }
+ }
+
+ spin_unlock(&master->access_lock);
+ kzfree(pI3cXfer);
+ return 0;
+}
+
+static int svc_i3c_master_ibi_request_handler(struct svc_i3c_master *master)
+{
+ int status;
+
+ spin_lock(&master->access_lock);
+ status = svcI3cControlSet((void *)master, I3C_CTRL_START_IBI, 0);
+ if (status != SVC_DRV_ERR_NONE) {
+ svcI3cControlSet((void *)master, I3C_CTRL_NACK_IBI, 0);
+ }
+
+ spin_unlock(&master->access_lock);
+ return status;
+}
+
+static int svc_i3c_master_ibi_complete_handler(struct svc_i3c_master *master)
+{
+ struct svc_i3c_i2c_dev_data *data;
+ struct i3c_dev_desc *dev;
+ struct i3c_ibi_slot *slot;
+ unsigned int i;
+ int ret = -1;
+
+ for (i = 0; i < master->ibi.num_ibi_devices; i++) {
+ dev = master->ibi.dev_desc[i];
+
+ if (dev->info.dyn_addr == master->pDev->ibiDevAddr) {
+ data = i3c_dev_get_master_data(dev);
+
+ slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
+ if (slot) {
+ memcpy(slot->data, master->pDev->ibiData,
+ master->pDev->ibiIdx);
+ slot->len = master->pDev->ibiIdx;
+
+ i3c_master_queue_ibi(dev, slot);
+ ret = 0;
+ }
+
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int svc_i3c_master_request_ibi(struct i3c_dev_desc *dev,
+ const struct i3c_ibi_setup *req)
+{
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct svc_i3c_master *master = to_svc_i3c_master(m);
+ struct svc_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+ unsigned long flags;
+ unsigned int i;
+
+ data->ibi_pool = i3c_generic_ibi_alloc_pool(dev, req);
+ if (IS_ERR(data->ibi_pool)) {
+ return PTR_ERR(data->ibi_pool);
+ }
+
+ spin_lock_irqsave(&master->ibi.lock, flags);
+ for (i = 0; i < master->ibi.num_ibi_devices; i++) {
+ if (!master->ibi.dev_desc[i]) {
+ data->ibi_dev_idx = i;
+ master->ibi.dev_desc[i] = dev;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&master->ibi.lock, flags);
+
+ if (i < master->ibi.num_ibi_devices) {
+ return 0;
+ }
+
+ i3c_generic_ibi_free_pool(data->ibi_pool);
+ data->ibi_pool = NULL;
+
+ return -ENOSPC;
+}
+
+static void svc_i3c_master_free_ibi(struct i3c_dev_desc *dev)
+{
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ struct svc_i3c_master *master = to_svc_i3c_master(m);
+ struct svc_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&master->ibi.lock, flags);
+ master->ibi.dev_desc[data->ibi_dev_idx] = NULL;
+ data->ibi_dev_idx = -1;
+ spin_unlock_irqrestore(&master->ibi.lock, flags);
+
+ i3c_generic_ibi_free_pool(data->ibi_pool);
+}
+
+static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
+{
+ int ret;
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+
+ ret = i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
+ return ret;
+}
+
+static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
+{
+ int ret;
+ struct i3c_master_controller *m = i3c_dev_get_master(dev);
+ ret = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
+ return ret;
+}
+
+static void svc_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
+ struct i3c_ibi_slot *slot)
+{
+ struct svc_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+
+ i3c_generic_ibi_recycle_slot(data->ibi_pool, slot);
+}
+
+static const struct i3c_master_controller_ops svc_i3c_master_ops = {
+ .bus_init = svc_i3c_master_bus_init,
+ .attach_i3c_dev = svc_i3c_master_attach_i3c_dev,
+ .do_daa = svc_i3c_master_do_daa,
+ .supports_ccc_cmd = svc_i3c_master_supports_ccc_cmd,
+ .send_ccc_cmd = svc_i3c_master_send_ccc_cmd,
+ .priv_xfers = svc_i3c_master_priv_xfers,
+ .attach_i2c_dev = svc_i3c_master_attach_i2c_dev,
+ .i2c_xfers = svc_i3c_master_i2c_xfers,
+ .request_ibi = svc_i3c_master_request_ibi,
+ .free_ibi = svc_i3c_master_free_ibi,
+ .enable_ibi = svc_i3c_master_enable_ibi,
+ .disable_ibi = svc_i3c_master_disable_ibi,
+ .recycle_ibi_slot = svc_i3c_master_recycle_ibi_slot,
+};
+
+static int svc_i3c_master_probe(struct platform_device *pdev)
+{
+ struct svc_i3c_master *master;
+ struct resource *res;
+ int ret, irq;
+ int reg;
+ struct device_node *np = pdev->dev.of_node;
+ void *pTmp;
+
+ master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return -ENOMEM;
+
+ master->pDev =
+ devm_kzalloc(&pdev->dev, sizeof(svc_i3c_dev_t), GFP_KERNEL);
+ if (!(master->pDev))
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ master->baseAddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(master->baseAddr))
+ return PTR_ERR(master->baseAddr);
+
+ spin_lock_init(&master->access_lock);
+
+ svcI3cInitialize((void *)master, master->pDev, 0, 0);
+
+ master->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(master->pclk))
+ return PTR_ERR(master->pclk);
+
+ master->fclk_i3c = devm_clk_get(&pdev->dev, "fclk_i3c_ms");
+ if (IS_ERR(master->fclk_i3c))
+ return PTR_ERR(master->fclk_i3c);
+
+ master->clk_slow_12_5 = devm_clk_get(&pdev->dev, "clk_slow_12p5mhz");
+ if (IS_ERR(master->clk_slow_12_5))
+ return PTR_ERR(master->clk_slow_12_5);
+
+ ret = clk_prepare_enable(master->pclk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(master->fclk_i3c);
+ if (ret)
+ goto err_disable_pclk;
+
+ ret = clk_prepare_enable(master->clk_slow_12_5);
+ if (ret)
+ goto err_disable_fclk_i3c;
+
+ irq = platform_get_irq(pdev, 0);
+
+ ret = devm_request_irq(&pdev->dev, irq, svc_i3c_master_irq_handler, 0,
+ dev_name(&pdev->dev), master);
+ if (ret)
+ goto err_disable_clk_slow_12p5mhz;
+
+ platform_set_drvdata(pdev, master);
+
+ master->i3c_pp_clk_div = I3C_PP_CLK_DIV_DEFAULT;
+ master->i3c_od_clk_div = I3C_OD_CLK_DIV_DEFAULT;
+ master->i2c_clk_div = I2C_CLK_DIV_DEFAULT;
+
+ pTmp = of_get_property(np, "svc,i3c-pp-clk-div", NULL);
+ if (pTmp) {
+ if (*(uint32 *)pTmp) {
+ master->i3c_pp_clk_div = (uint32)be32_to_cpup(pTmp);
+ }
+ }
+
+ pTmp = of_get_property(np, "svc,i3c-od-clk-div", NULL);
+ if (pTmp) {
+ if (*(uint32 *)pTmp) {
+ master->i3c_od_clk_div = (uint32)be32_to_cpup(pTmp);
+ }
+ }
+
+ pTmp = of_get_property(np, "svc,i2c-clk-div", NULL);
+ if (pTmp) {
+ if (*(uint32 *)pTmp) {
+ master->i2c_clk_div = (uint32)be32_to_cpup(pTmp);
+ }
+ }
+
+ spin_lock_init(&master->ibi.lock);
+ master->ibi.num_ibi_devices = I3C_MAX_DEVICES;
+ master->ibi.dev_desc =
+ devm_kcalloc(&pdev->dev, master->ibi.num_ibi_devices,
+ sizeof(*master->ibi.dev_desc), GFP_KERNEL);
+ if (!master->ibi.dev_desc) {
+ ret = -ENOMEM;
+ goto err_disable_clk_slow_12p5mhz;
+ }
+
+ ret = i3c_master_register(&master->base, &pdev->dev,
+ &svc_i3c_master_ops, false);
+
+ if (ret == 0)
+ return 0;
+
+err_disable_clk_slow_12p5mhz:
+ clk_disable_unprepare(master->clk_slow_12_5);
+
+err_disable_fclk_i3c:
+ clk_disable_unprepare(master->fclk_i3c);
+
+err_disable_pclk:
+ clk_disable_unprepare(master->pclk);
+
+ return ret;
+}
+
+static int svc_i3c_master_remove(struct platform_device *pdev)
+{
+ struct svc_i3c_master *master = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = i3c_master_unregister(&master->base);
+ if (ret)
+ return ret;
+
+ clk_disable_unprepare(master->pclk);
+ clk_disable_unprepare(master->fclk_i3c);
+ clk_disable_unprepare(master->clk_slow_12_5);
+
+ return 0;
+}
+
+static const struct of_device_id svc_i3c_master_of_match_tbl[] = {
+ {
+ .compatible = "svc,i3c-master",
+ },
+ {},
+};
+
+static struct platform_driver svc_i3c_master = {
+ .probe = svc_i3c_master_probe,
+ .remove = svc_i3c_master_remove,
+ .driver =
+ {
+ .name = "svc-i3c-master",
+ .of_match_table = svc_i3c_master_of_match_tbl,
+ },
+};
+
+module_platform_driver(svc_i3c_master);
+
+MODULE_AUTHOR("Conor Culhane <conor.culhane@silvaco.com>");
+MODULE_DESCRIPTION("Silvaco I3C master driver");
+MODULE_LICENSE("GPL v2");
Signed-off-by: Conor Culhane <conor.culhane@silvaco.com> --- drivers/i3c/master/Kconfig | 8 + drivers/i3c/master/Makefile | 1 + drivers/i3c/master/svc-i3c-master.c | 2863 +++++++++++++++++++++++++++ 3 files changed, 2872 insertions(+) create mode 100644 drivers/i3c/master/svc-i3c-master.c