// SPDX-License-Identifier: GPL-2.0
/*
* Silvaco dual - role I3C master driver
*
* Copyright ( C ) 2020 Silvaco
* Author : Miquel RAYNAL < miquel . raynal @ bootlin . com >
* Based on a work from : Conor Culhane < conor . culhane @ silvaco . com >
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/errno.h>
#include <linux/i3c/master.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
/* Master Mode Registers */
#define SVC_I3C_MCONFIG 0 x000
#define SVC_I3C_MCONFIG_MASTER_EN BIT(0 )
#define SVC_I3C_MCONFIG_DISTO(x) FIELD_PREP(BIT(3 ), (x))
#define SVC_I3C_MCONFIG_HKEEP(x) FIELD_PREP(GENMASK(5 , 4 ), (x))
#define SVC_I3C_MCONFIG_ODSTOP(x) FIELD_PREP(BIT(6 ), (x))
#define SVC_I3C_MCONFIG_PPBAUD(x) FIELD_PREP(GENMASK(11 , 8 ), (x))
#define SVC_I3C_MCONFIG_PPLOW(x) FIELD_PREP(GENMASK(15 , 12 ), (x))
#define SVC_I3C_MCONFIG_ODBAUD(x) FIELD_PREP(GENMASK(23 , 16 ), (x))
#define SVC_I3C_MCONFIG_ODHPP(x) FIELD_PREP(BIT(24 ), (x))
#define SVC_I3C_MCONFIG_SKEW(x) FIELD_PREP(GENMASK(27 , 25 ), (x))
#define SVC_I3C_MCONFIG_SKEW_MASK GENMASK(27 , 25 )
#define SVC_I3C_MCONFIG_I2CBAUD(x) FIELD_PREP(GENMASK(31 , 28 ), (x))
#define SVC_I3C_MCTRL 0 x084
#define SVC_I3C_MCTRL_REQUEST_MASK GENMASK(2 , 0 )
#define SVC_I3C_MCTRL_REQUEST_NONE 0
#define SVC_I3C_MCTRL_REQUEST_START_ADDR 1
#define SVC_I3C_MCTRL_REQUEST_STOP 2
#define SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK 3
#define SVC_I3C_MCTRL_REQUEST_PROC_DAA 4
#define SVC_I3C_MCTRL_REQUEST_AUTO_IBI 7
#define SVC_I3C_MCTRL_TYPE_I3C 0
#define SVC_I3C_MCTRL_TYPE_I2C BIT(4 )
#define SVC_I3C_MCTRL_IBIRESP_AUTO 0
#define SVC_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE 0
#define SVC_I3C_MCTRL_IBIRESP_ACK_WITH_BYTE BIT(7 )
#define SVC_I3C_MCTRL_IBIRESP_NACK BIT(6 )
#define SVC_I3C_MCTRL_IBIRESP_MANUAL GENMASK(7 , 6 )
#define SVC_I3C_MCTRL_DIR(x) FIELD_PREP(BIT(8 ), (x))
#define SVC_I3C_MCTRL_DIR_WRITE 0
#define SVC_I3C_MCTRL_DIR_READ 1
#define SVC_I3C_MCTRL_ADDR(x) FIELD_PREP(GENMASK(15 , 9 ), (x))
#define SVC_I3C_MCTRL_RDTERM(x) FIELD_PREP(GENMASK(23 , 16 ), (x))
#define SVC_I3C_MSTATUS 0 x088
#define SVC_I3C_MSTATUS_STATE(x) FIELD_GET(GENMASK(2 , 0 ), (x))
#define SVC_I3C_MSTATUS_STATE_DAA(x) (SVC_I3C_MSTATUS_STATE(x) == 5 )
#define SVC_I3C_MSTATUS_STATE_SLVREQ(x) (SVC_I3C_MSTATUS_STATE(x) == 1 )
#define SVC_I3C_MSTATUS_STATE_IDLE(x) (SVC_I3C_MSTATUS_STATE(x) == 0 )
#define SVC_I3C_MSTATUS_BETWEEN(x) FIELD_GET(BIT(4 ), (x))
#define SVC_I3C_MSTATUS_NACKED(x) FIELD_GET(BIT(5 ), (x))
#define SVC_I3C_MSTATUS_IBITYPE(x) FIELD_GET(GENMASK(7 , 6 ), (x))
#define SVC_I3C_MSTATUS_IBITYPE_IBI 1
#define SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST 2
#define SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN 3
#define SVC_I3C_MINT_SLVSTART BIT(8 )
#define SVC_I3C_MINT_MCTRLDONE BIT(9 )
#define SVC_I3C_MINT_COMPLETE BIT(10 )
#define SVC_I3C_MINT_RXPEND BIT(11 )
#define SVC_I3C_MINT_TXNOTFULL BIT(12 )
#define SVC_I3C_MINT_IBIWON BIT(13 )
#define SVC_I3C_MINT_ERRWARN BIT(15 )
#define SVC_I3C_MSTATUS_SLVSTART(x) FIELD_GET(SVC_I3C_MINT_SLVSTART, (x))
#define SVC_I3C_MSTATUS_MCTRLDONE(x) FIELD_GET(SVC_I3C_MINT_MCTRLDONE, (x))
#define SVC_I3C_MSTATUS_COMPLETE(x) FIELD_GET(SVC_I3C_MINT_COMPLETE, (x))
#define SVC_I3C_MSTATUS_RXPEND(x) FIELD_GET(SVC_I3C_MINT_RXPEND, (x))
#define SVC_I3C_MSTATUS_TXNOTFULL(x) FIELD_GET(SVC_I3C_MINT_TXNOTFULL, (x))
#define SVC_I3C_MSTATUS_IBIWON(x) FIELD_GET(SVC_I3C_MINT_IBIWON, (x))
#define SVC_I3C_MSTATUS_ERRWARN(x) FIELD_GET(SVC_I3C_MINT_ERRWARN, (x))
#define SVC_I3C_MSTATUS_IBIADDR(x) FIELD_GET(GENMASK(30 , 24 ), (x))
#define SVC_I3C_IBIRULES 0 x08C
#define SVC_I3C_IBIRULES_ADDR(slot, addr) FIELD_PREP(GENMASK(29 , 0 ), \
((addr) & 0 x3F) << ((slot) * 6 ))
#define SVC_I3C_IBIRULES_ADDRS 5
#define SVC_I3C_IBIRULES_MSB0 BIT(30 )
#define SVC_I3C_IBIRULES_NOBYTE BIT(31 )
#define SVC_I3C_IBIRULES_MANDBYTE 0
#define SVC_I3C_MINTSET 0 x090
#define SVC_I3C_MINTCLR 0 x094
#define SVC_I3C_MINTMASKED 0 x098
#define SVC_I3C_MERRWARN 0 x09C
#define SVC_I3C_MERRWARN_NACK BIT(2 )
#define SVC_I3C_MERRWARN_TIMEOUT BIT(20 )
#define SVC_I3C_MDMACTRL 0 x0A0
#define SVC_I3C_MDATACTRL 0 x0AC
#define SVC_I3C_MDATACTRL_FLUSHTB BIT(0 )
#define SVC_I3C_MDATACTRL_FLUSHRB BIT(1 )
#define SVC_I3C_MDATACTRL_UNLOCK_TRIG BIT(3 )
#define SVC_I3C_MDATACTRL_TXTRIG_FIFO_NOT_FULL GENMASK(5 , 4 )
#define SVC_I3C_MDATACTRL_RXTRIG_FIFO_NOT_EMPTY 0
#define SVC_I3C_MDATACTRL_RXCOUNT(x) FIELD_GET(GENMASK(28 , 24 ), (x))
#define SVC_I3C_MDATACTRL_TXCOUNT(x) FIELD_GET(GENMASK(20 , 16 ), (x))
#define SVC_I3C_MDATACTRL_TXFULL BIT(30 )
#define SVC_I3C_MDATACTRL_RXEMPTY BIT(31 )
#define SVC_I3C_MWDATAB 0 x0B0
#define SVC_I3C_MWDATAB_END BIT(8 )
#define SVC_I3C_MWDATABE 0 x0B4
#define SVC_I3C_MWDATAH 0 x0B8
#define SVC_I3C_MWDATAHE 0 x0BC
#define SVC_I3C_MRDATAB 0 x0C0
#define SVC_I3C_MRDATAH 0 x0C8
#define SVC_I3C_MWDATAB1 0 x0CC
#define SVC_I3C_MWMSG_SDR 0 x0D0
#define SVC_I3C_MRMSG_SDR 0 x0D4
#define SVC_I3C_MWMSG_DDR 0 x0D8
#define SVC_I3C_MRMSG_DDR 0 x0DC
#define SVC_I3C_MDYNADDR 0 x0E4
#define SVC_MDYNADDR_VALID BIT(0 )
#define SVC_MDYNADDR_ADDR(x) FIELD_PREP(GENMASK(7 , 1 ), (x))
#define SVC_I3C_MAX_DEVS 32
#define SVC_I3C_PM_TIMEOUT_MS 1000
/* This parameter depends on the implementation and may be tuned */
#define SVC_I3C_FIFO_SIZE 16
#define SVC_I3C_PPBAUD_MAX 15
#define SVC_I3C_QUICK_I2C_CLK 4170000
#define SVC_I3C_EVENT_IBI GENMASK(7 , 0 )
#define SVC_I3C_EVENT_HOTJOIN BIT(31 )
/*
* SVC_I3C_QUIRK_FIFO_EMPTY :
* I3C HW stalls the write transfer if the transmit FIFO becomes empty ,
* when new data is written to FIFO , I3C HW resumes the transfer but
* the first transmitted data bit may have the wrong value .
* Workaround :
* Fill the FIFO in advance to prevent FIFO from becoming empty .
*/
#define SVC_I3C_QUIRK_FIFO_EMPTY BIT(0 )
/*
* SVC_I3C_QUIRK_FLASE_SLVSTART :
* I3C HW may generate an invalid SlvStart event when emitting a STOP .
* If it is a true SlvStart , the MSTATUS state is SLVREQ .
*/
#define SVC_I3C_QUIRK_FALSE_SLVSTART BIT(1 )
/*
* SVC_I3C_QUIRK_DAA_CORRUPT :
* When MCONFIG . SKEW = 0 and MCONFIG . ODHPP = 0 , the ENTDAA transaction gets
* corrupted and results in a no repeated - start condition at the end of
* address assignment .
* Workaround :
* Set MCONFIG . SKEW to 1 before initiating the DAA process . After the DAA
* process is completed , return MCONFIG . SKEW to its previous value .
*/
#define SVC_I3C_QUIRK_DAA_CORRUPT BIT(2 )
struct svc_i3c_cmd {
u8 addr;
bool rnw;
u8 *in;
const void *out;
unsigned int len;
unsigned int actual_len;
struct i3c_priv_xfer *xfer;
bool continued;
};
struct svc_i3c_xfer {
struct list_head node;
struct completion comp;
int ret;
unsigned int type;
unsigned int ncmds;
struct svc_i3c_cmd cmds[] __counted_by(ncmds);
};
struct svc_i3c_regs_save {
u32 mconfig;
u32 mdynaddr;
};
struct svc_i3c_drvdata {
u32 quirks;
};
/**
* struct svc_i3c_master - Silvaco I3C Master structure
* @ base : I3C master controller
* @ dev : Corresponding device
* @ regs : Memory mapping
* @ saved_regs : Volatile values for PM operations
* @ free_slots : Bit array of available slots
* @ addrs : Array containing the dynamic addresses of each attached device
* @ descs : Array of descriptors , one per attached device
* @ hj_work : Hot - join work
* @ irq : Main interrupt
* @ num_clks : I3C clock number
* @ fclk : Fast clock ( bus )
* @ clks : I3C clock array
* @ xferqueue : Transfer queue structure
* @ xferqueue . list : List member
* @ xferqueue . cur : Current ongoing transfer
* @ xferqueue . lock : Queue lock
* @ ibi : IBI structure
* @ ibi . num_slots : Number of slots available in @ ibi . slots
* @ ibi . slots : Available IBI slots
* @ ibi . tbq_slot : To be queued IBI slot
* @ ibi . lock : IBI lock
* @ lock : Transfer lock , protect between IBI work thread and callbacks from master
* @ drvdata : Driver data
* @ enabled_events : Bit masks for enable events ( IBI , HotJoin ) .
* @ mctrl_config : Configuration value in SVC_I3C_MCTRL for setting speed back .
*/
struct svc_i3c_master {
struct i3c_master_controller base;
struct device *dev;
void __iomem *regs;
struct svc_i3c_regs_save saved_regs;
u32 free_slots;
u8 addrs[SVC_I3C_MAX_DEVS];
struct i3c_dev_desc *descs[SVC_I3C_MAX_DEVS];
struct work_struct hj_work;
int irq;
int num_clks;
struct clk *fclk;
struct clk_bulk_data *clks;
struct {
struct list_head list;
struct svc_i3c_xfer *cur;
/* Prevent races between transfers */
spinlock_t lock;
} xferqueue;
struct {
unsigned int num_slots;
struct i3c_dev_desc **slots;
struct i3c_ibi_slot *tbq_slot;
/* Prevent races within IBI handlers */
spinlock_t lock;
} ibi;
struct mutex lock;
const struct svc_i3c_drvdata *drvdata;
u32 enabled_events;
u32 mctrl_config;
};
/**
* struct svc_i3c_i2c_dev_data - Device specific data
* @ index : Index in the master tables corresponding to this device
* @ ibi : IBI slot index in the master structure
* @ ibi_pool : IBI pool associated to this device
*/
struct svc_i3c_i2c_dev_data {
u8 index;
int ibi;
struct i3c_generic_ibi_pool *ibi_pool;
};
static inline bool svc_has_quirk(struct svc_i3c_master *master, u32 quirk)
{
return (master->drvdata->quirks & quirk);
}
static inline bool svc_has_daa_corrupt(struct svc_i3c_master *master)
{
return ((master->drvdata->quirks & SVC_I3C_QUIRK_DAA_CORRUPT) &&
!(master->mctrl_config &
(SVC_I3C_MCONFIG_SKEW_MASK | SVC_I3C_MCONFIG_ODHPP(1 ))));
}
static inline bool is_events_enabled(struct svc_i3c_master *master, u32 mask)
{
return !!(master->enabled_events & mask);
}
static bool svc_i3c_master_error(struct svc_i3c_master *master)
{
u32 mstatus, merrwarn;
mstatus = readl(master->regs + SVC_I3C_MSTATUS);
if (SVC_I3C_MSTATUS_ERRWARN(mstatus)) {
merrwarn = readl(master->regs + SVC_I3C_MERRWARN);
writel(merrwarn, master->regs + SVC_I3C_MERRWARN);
/* Ignore timeout error */
if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT) {
dev_dbg(master->dev, "Warning condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n" ,
mstatus, merrwarn);
return false ;
}
dev_err(master->dev,
"Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n" ,
mstatus, merrwarn);
return true ;
}
return false ;
}
static void svc_i3c_master_enable_interrupts(struct svc_i3c_master *master, u32 mask)
{
writel(mask, master->regs + SVC_I3C_MINTSET);
}
static void svc_i3c_master_disable_interrupts(struct svc_i3c_master *master)
{
u32 mask = readl(master->regs + SVC_I3C_MINTSET);
writel(mask, master->regs + SVC_I3C_MINTCLR);
}
static void svc_i3c_master_clear_merrwarn(struct svc_i3c_master *master)
{
/* Clear pending warnings */
writel(readl(master->regs + SVC_I3C_MERRWARN),
master->regs + SVC_I3C_MERRWARN);
}
static void svc_i3c_master_flush_fifo(struct svc_i3c_master *master)
{
/* Flush FIFOs */
writel(SVC_I3C_MDATACTRL_FLUSHTB | SVC_I3C_MDATACTRL_FLUSHRB,
master->regs + SVC_I3C_MDATACTRL);
}
static void svc_i3c_master_reset_fifo_trigger(struct svc_i3c_master *master)
{
u32 reg;
/* Set RX and TX tigger levels, flush FIFOs */
reg = SVC_I3C_MDATACTRL_FLUSHTB |
SVC_I3C_MDATACTRL_FLUSHRB |
SVC_I3C_MDATACTRL_UNLOCK_TRIG |
SVC_I3C_MDATACTRL_TXTRIG_FIFO_NOT_FULL |
SVC_I3C_MDATACTRL_RXTRIG_FIFO_NOT_EMPTY;
writel(reg, master->regs + SVC_I3C_MDATACTRL);
}
static void svc_i3c_master_reset(struct svc_i3c_master *master)
{
svc_i3c_master_clear_merrwarn(master);
svc_i3c_master_reset_fifo_trigger(master);
svc_i3c_master_disable_interrupts(master);
}
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 void svc_i3c_master_hj_work(struct work_struct *work)
{
struct svc_i3c_master *master;
master = container_of(work, struct svc_i3c_master, hj_work);
i3c_master_do_daa(&master->base);
}
static struct i3c_dev_desc *
svc_i3c_master_dev_from_addr(struct svc_i3c_master *master,
unsigned int ibiaddr)
{
int i;
for (i = 0 ; i < SVC_I3C_MAX_DEVS; i++)
if (master->addrs[i] == ibiaddr)
break ;
if (i == SVC_I3C_MAX_DEVS)
return NULL;
return master->descs[i];
}
static void svc_i3c_master_emit_stop(struct svc_i3c_master *master)
{
writel(SVC_I3C_MCTRL_REQUEST_STOP, master->regs + SVC_I3C_MCTRL);
/*
* This delay is necessary after the emission of a stop , otherwise eg .
* repeating IBIs do not get detected . There is a note in the manual
* about it , stating that the stop condition might not be settled
* correctly if a start condition follows too rapidly .
*/
udelay(1 );
}
static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
struct i3c_dev_desc *dev)
{
struct svc_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
struct i3c_ibi_slot *slot;
unsigned int count;
u32 mdatactrl;
int ret, val;
u8 *buf;
slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
if (!slot)
return -ENOSPC;
slot->len = 0 ;
buf = slot->data;
ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
SVC_I3C_MSTATUS_COMPLETE(val), 0 , 1000 );
if (ret) {
dev_err(master->dev, "Timeout when polling for COMPLETE\n" );
i3c_generic_ibi_recycle_slot(data->ibi_pool, slot);
return ret;
}
while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS)) &&
slot->len < SVC_I3C_FIFO_SIZE) {
mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
count = SVC_I3C_MDATACTRL_RXCOUNT(mdatactrl);
readsb(master->regs + SVC_I3C_MRDATAB, buf, count);
slot->len += count;
buf += count;
}
master->ibi.tbq_slot = slot;
return 0 ;
}
static int svc_i3c_master_ack_ibi(struct svc_i3c_master *master,
bool mandatory_byte)
{
unsigned int ibi_ack_nack;
u32 reg;
ibi_ack_nack = SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK;
if (mandatory_byte)
ibi_ack_nack |= SVC_I3C_MCTRL_IBIRESP_ACK_WITH_BYTE;
else
ibi_ack_nack |= SVC_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE;
writel(ibi_ack_nack, master->regs + SVC_I3C_MCTRL);
return readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, reg,
SVC_I3C_MSTATUS_MCTRLDONE(reg), 1 , 1000 );
}
static int svc_i3c_master_nack_ibi(struct svc_i3c_master *master)
{
int ret;
u32 reg;
writel(SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK |
SVC_I3C_MCTRL_IBIRESP_NACK,
master->regs + SVC_I3C_MCTRL);
ret = readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, reg,
SVC_I3C_MSTATUS_MCTRLDONE(reg), 1 , 1000 );
return ret;
}
static int svc_i3c_master_handle_ibi_won(struct svc_i3c_master *master, u32 mstatus)
{
u32 ibitype;
int ret = 0 ;
ibitype = SVC_I3C_MSTATUS_IBITYPE(mstatus);
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
/* Hardware can't auto emit NACK for hot join and master request */
switch (ibitype) {
case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
ret = svc_i3c_master_nack_ibi(master);
}
return ret;
}
static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master)
{
struct svc_i3c_i2c_dev_data *data;
unsigned int ibitype, ibiaddr;
struct i3c_dev_desc *dev;
u32 status, val;
int ret;
/*
* According to I3C spec ver 1 . 1 , 09 - Jun - 2021 , section 5 . 1 . 2 . 5 :
*
* The I3C Controller shall hold SCL low while the Bus is in ACK / NACK Phase of I3C / I2C
* transfer . But maximum stall time is 100 us . The IRQs have to be disabled to prevent
* schedule during the whole I3C transaction , otherwise , the I3C bus timeout may happen if
* any irq or schedule happen during transaction .
*/
guard(spinlock)(&master->xferqueue.lock);
/*
* IBIWON may be set before SVC_I3C_MCTRL_REQUEST_AUTO_IBI , causing
* readl_relaxed_poll_timeout ( ) to return immediately . Consequently ,
* ibitype will be 0 since it was last updated only after the 8 th SCL
* cycle , leading to missed client IBI handlers .
*
* A typical scenario is when IBIWON occurs and bus arbitration is lost
* at svc_i3c_master_priv_xfers ( ) .
*
* Clear SVC_I3C_MINT_IBIWON before sending SVC_I3C_MCTRL_REQUEST_AUTO_IBI .
*/
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
/*
* Write REQUEST_START_ADDR request to emit broadcast address for arbitration ,
* instend of using AUTO_IBI .
*
* Using AutoIBI request may cause controller to remain in AutoIBI state when
* there is a glitch on SDA line ( high - > low - > high ) .
* 1 . SDA high - > low , raising an interrupt to execute IBI isr .
* 2 . SDA low - > high .
* 3 . IBI isr writes an AutoIBI request .
* 4 . The controller will not start AutoIBI process because SDA is not low .
* 5 . IBIWON polling times out .
* 6 . Controller reamins in AutoIBI state and doesn ' t accept EmitStop request .
*/
writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
SVC_I3C_MCTRL_TYPE_I3C |
SVC_I3C_MCTRL_IBIRESP_MANUAL |
SVC_I3C_MCTRL_DIR(SVC_I3C_MCTRL_DIR_WRITE) |
SVC_I3C_MCTRL_ADDR(I3C_BROADCAST_ADDR),
master->regs + SVC_I3C_MCTRL);
/* Wait for IBIWON, should take approximately 100us */
ret = readl_relaxed_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, val,
SVC_I3C_MSTATUS_IBIWON(val), 0 , 100 );
if (ret) {
dev_err(master->dev, "Timeout when polling for IBIWON\n" );
svc_i3c_master_emit_stop(master);
return ;
}
status = readl(master->regs + SVC_I3C_MSTATUS);
ibitype = SVC_I3C_MSTATUS_IBITYPE(status);
ibiaddr = SVC_I3C_MSTATUS_IBIADDR(status);
/* Handle the critical responses to IBI's */
switch (ibitype) {
case SVC_I3C_MSTATUS_IBITYPE_IBI:
dev = svc_i3c_master_dev_from_addr(master, ibiaddr);
if (!dev || !is_events_enabled(master, SVC_I3C_EVENT_IBI)) {
svc_i3c_master_nack_ibi(master);
} else {
if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
svc_i3c_master_ack_ibi(master, true );
else
svc_i3c_master_ack_ibi(master, false );
svc_i3c_master_handle_ibi(master, dev);
}
break ;
case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN))
svc_i3c_master_ack_ibi(master, false );
else
svc_i3c_master_nack_ibi(master);
break ;
case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
svc_i3c_master_nack_ibi(master);
break ;
default :
break ;
}
/*
* If an error happened , we probably got interrupted and the exchange
* timedout . In this case we just drop everything , emit a stop and wait
* for the slave to interrupt again .
*/
if (svc_i3c_master_error(master)) {
if (master->ibi.tbq_slot) {
data = i3c_dev_get_master_data(dev);
i3c_generic_ibi_recycle_slot(data->ibi_pool,
master->ibi.tbq_slot);
master->ibi.tbq_slot = NULL;
}
svc_i3c_master_emit_stop(master);
return ;
}
/* Handle the non critical tasks */
switch (ibitype) {
case SVC_I3C_MSTATUS_IBITYPE_IBI:
svc_i3c_master_emit_stop(master);
if (dev) {
i3c_master_queue_ibi(dev, master->ibi.tbq_slot);
master->ibi.tbq_slot = NULL;
}
break ;
case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
svc_i3c_master_emit_stop(master);
if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN))
queue_work(master->base.wq, &master->hj_work);
break ;
case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
svc_i3c_master_emit_stop(master);
break ;
default :
break ;
}
}
static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
{
struct svc_i3c_master *master = (struct svc_i3c_master *)dev_id;
u32 active = readl(master->regs + SVC_I3C_MSTATUS);
if (!SVC_I3C_MSTATUS_SLVSTART(active))
return IRQ_NONE;
/* Clear the interrupt status */
writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MSTATUS);
/* Ignore the false event */
if (svc_has_quirk(master, SVC_I3C_QUIRK_FALSE_SLVSTART) &&
!SVC_I3C_MSTATUS_STATE_SLVREQ(active))
return IRQ_HANDLED;
/*
* The SDA line remains low until the request is processed .
* Receive the request in the interrupt context to respond promptly
* and restore the bus to idle state .
*/
svc_i3c_master_ibi_isr(master);
return IRQ_HANDLED;
}
static int svc_i3c_master_set_speed(struct i3c_master_controller *m,
enum i3c_open_drain_speed speed)
{
struct svc_i3c_master *master = to_svc_i3c_master(m);
struct i3c_bus *bus = i3c_master_get_bus(&master->base);
u32 ppbaud, odbaud, odhpp, mconfig;
unsigned long fclk_rate;
int ret;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0 ) {
dev_err(master->dev, "<%s> Cannot get runtime PM.\n" , __func__);
return ret;
}
switch (speed) {
case I3C_OPEN_DRAIN_SLOW_SPEED:
fclk_rate = clk_get_rate(master->fclk);
if (!fclk_rate) {
ret = -EINVAL;
goto rpm_out;
}
/*
* Set 50 % duty - cycle I2C speed to I3C OPEN - DRAIN mode , so the first
* broadcast address is visible to all I2C / I3C devices on the I3C bus .
* I3C device working as a I2C device will turn off its 50 ns Spike
* Filter to change to I3C mode .
*/
mconfig = master->mctrl_config;
ppbaud = FIELD_GET(GENMASK(11 , 8 ), mconfig);
odhpp = 0 ;
odbaud = DIV_ROUND_UP(fclk_rate, bus->scl_rate.i2c * (2 + 2 * ppbaud)) - 1 ;
mconfig &= ~GENMASK(24 , 16 );
mconfig |= SVC_I3C_MCONFIG_ODBAUD(odbaud) | SVC_I3C_MCONFIG_ODHPP(odhpp);
writel(mconfig, master->regs + SVC_I3C_MCONFIG);
break ;
case I3C_OPEN_DRAIN_NORMAL_SPEED:
writel(master->mctrl_config, master->regs + SVC_I3C_MCONFIG);
break ;
}
rpm_out:
pm_runtime_put_autosuspend(master->dev);
return ret;
}
static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
{
struct svc_i3c_master *master = to_svc_i3c_master(m);
struct i3c_bus *bus = i3c_master_get_bus(m);
struct i3c_device_info info = {};
unsigned long fclk_rate, fclk_period_ns;
unsigned long i2c_period_ns, i2c_scl_rate, i3c_scl_rate;
unsigned int high_period_ns, od_low_period_ns;
u32 ppbaud, pplow, odhpp, odbaud, odstop, i2cbaud, reg;
int ret;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0 ) {
dev_err(master->dev,
"<%s> cannot resume i3c bus master, err: %d\n" ,
__func__, ret);
return ret;
}
/* Timings derivation */
fclk_rate = clk_get_rate(master->fclk);
if (!fclk_rate) {
ret = -EINVAL;
goto rpm_out;
}
fclk_period_ns = DIV_ROUND_UP(1000000000 , fclk_rate);
i2c_period_ns = DIV_ROUND_UP(1000000000 , bus->scl_rate.i2c);
i2c_scl_rate = bus->scl_rate.i2c;
i3c_scl_rate = bus->scl_rate.i3c;
/*
* Using I3C Push - Pull mode , target is 12 . 5 MHz / 80 ns period .
* Simplest configuration is using a 50 % duty - cycle of 40 ns .
*/
ppbaud = DIV_ROUND_UP(fclk_rate / 2 , i3c_scl_rate) - 1 ;
pplow = 0 ;
/*
* Using I3C Open - Drain mode , target is 4 . 17 MHz / 240 ns with a
* duty - cycle tuned so that high levels are filetered out by
* the 50 ns filter ( target being 40 ns ) .
*/
odhpp = 1 ;
high_period_ns = (ppbaud + 1 ) * fclk_period_ns;
odbaud = DIV_ROUND_UP(fclk_rate, SVC_I3C_QUICK_I2C_CLK * (1 + ppbaud)) - 2 ;
od_low_period_ns = (odbaud + 1 ) * high_period_ns;
switch (bus->mode) {
case I3C_BUS_MODE_PURE:
i2cbaud = 0 ;
odstop = 0 ;
break ;
case I3C_BUS_MODE_MIXED_FAST:
/*
* Using I2C Fm + mode , target is 1 MHz / 1000 ns , the difference
* between the high and low period does not really matter .
*/
i2cbaud = DIV_ROUND_UP(i2c_period_ns, od_low_period_ns) - 2 ;
odstop = 1 ;
break ;
case I3C_BUS_MODE_MIXED_LIMITED:
case I3C_BUS_MODE_MIXED_SLOW:
/* I3C PP + I3C OP + I2C OP both use i2c clk rate */
if (ppbaud > SVC_I3C_PPBAUD_MAX) {
ppbaud = SVC_I3C_PPBAUD_MAX;
pplow = DIV_ROUND_UP(fclk_rate, i3c_scl_rate) - (2 + 2 * ppbaud);
}
high_period_ns = (ppbaud + 1 ) * fclk_period_ns;
odhpp = 0 ;
odbaud = DIV_ROUND_UP(fclk_rate, i2c_scl_rate * (2 + 2 * ppbaud)) - 1 ;
od_low_period_ns = (odbaud + 1 ) * high_period_ns;
i2cbaud = DIV_ROUND_UP(i2c_period_ns, od_low_period_ns) - 2 ;
odstop = 1 ;
break ;
default :
goto rpm_out;
}
reg = SVC_I3C_MCONFIG_MASTER_EN |
SVC_I3C_MCONFIG_DISTO(0 ) |
SVC_I3C_MCONFIG_HKEEP(0 ) |
SVC_I3C_MCONFIG_ODSTOP(odstop) |
SVC_I3C_MCONFIG_PPBAUD(ppbaud) |
SVC_I3C_MCONFIG_PPLOW(pplow) |
SVC_I3C_MCONFIG_ODBAUD(odbaud) |
SVC_I3C_MCONFIG_ODHPP(odhpp) |
SVC_I3C_MCONFIG_SKEW(0 ) |
SVC_I3C_MCONFIG_I2CBAUD(i2cbaud);
writel(reg, master->regs + SVC_I3C_MCONFIG);
master->mctrl_config = reg;
/* Master core's registration */
ret = i3c_master_get_free_addr(m, 0 );
if (ret < 0 )
goto rpm_out;
info.dyn_addr = ret;
writel(SVC_MDYNADDR_VALID | SVC_MDYNADDR_ADDR(info.dyn_addr),
master->regs + SVC_I3C_MDYNADDR);
ret = i3c_master_set_info(&master->base, &info);
if (ret)
goto rpm_out;
rpm_out:
pm_runtime_put_autosuspend(master->dev);
return ret;
}
static void svc_i3c_master_bus_cleanup(struct i3c_master_controller *m)
{
struct svc_i3c_master *master = to_svc_i3c_master(m);
int ret;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0 ) {
dev_err(master->dev, "<%s> Cannot get runtime PM.\n" , __func__);
return ;
}
svc_i3c_master_disable_interrupts(master);
/* Disable master */
writel(0 , master->regs + SVC_I3C_MCONFIG);
pm_runtime_put_autosuspend(master->dev);
}
static int svc_i3c_master_reserve_slot(struct svc_i3c_master *master)
{
unsigned int slot;
if (!(master->free_slots & GENMASK(SVC_I3C_MAX_DEVS - 1 , 0 )))
return -ENOSPC;
slot = ffs(master->free_slots) - 1 ;
master->free_slots &= ~BIT(slot);
return slot;
}
static void svc_i3c_master_release_slot(struct svc_i3c_master *master,
unsigned int slot)
{
master->free_slots |= BIT(slot);
}
static int svc_i3c_master_attach_i3c_dev(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;
int slot;
slot = svc_i3c_master_reserve_slot(master);
if (slot < 0 )
return slot;
data = kzalloc(sizeof (*data), GFP_KERNEL);
if (!data) {
svc_i3c_master_release_slot(master, slot);
return -ENOMEM;
}
data->ibi = -1 ;
data->index = slot;
master->addrs[slot] = dev->info.dyn_addr ? dev->info.dyn_addr :
dev->info.static_addr;
master->descs[slot] = dev;
i3c_dev_set_master_data(dev, data);
return 0 ;
}
static int svc_i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
u8 old_dyn_addr)
{
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);
master->addrs[data->index] = dev->info.dyn_addr ? dev->info.dyn_addr :
dev->info.static_addr;
return 0 ;
}
static void svc_i3c_master_detach_i3c_dev(struct i3c_dev_desc *dev)
{
struct svc_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct svc_i3c_master *master = to_svc_i3c_master(m);
master->addrs[data->index] = 0 ;
svc_i3c_master_release_slot(master, data->index);
kfree(data);
}
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;
int slot;
slot = svc_i3c_master_reserve_slot(master);
if (slot < 0 )
return slot;
data = kzalloc(sizeof (*data), GFP_KERNEL);
if (!data) {
svc_i3c_master_release_slot(master, slot);
return -ENOMEM;
}
data->index = slot;
master->addrs[slot] = dev->addr;
i2c_dev_set_master_data(dev, data);
return 0 ;
}
static void svc_i3c_master_detach_i2c_dev(struct i2c_dev_desc *dev)
{
struct svc_i3c_i2c_dev_data *data = i2c_dev_get_master_data(dev);
struct i3c_master_controller *m = i2c_dev_get_master(dev);
struct svc_i3c_master *master = to_svc_i3c_master(m);
svc_i3c_master_release_slot(master, data->index);
kfree(data);
}
static int svc_i3c_master_readb(struct svc_i3c_master *master, u8 *dst,
unsigned int len)
{
int ret, i;
u32 reg;
for (i = 0 ; i < len; i++) {
ret = readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS,
reg,
SVC_I3C_MSTATUS_RXPEND(reg),
0 , 1000 );
if (ret)
return ret;
dst[i] = readl(master->regs + SVC_I3C_MRDATAB);
}
return 0 ;
}
static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
u8 *addrs, unsigned int *count)
{
u64 prov_id[SVC_I3C_MAX_DEVS] = {}, nacking_prov_id = 0 ;
unsigned int dev_nb = 0 , last_addr = 0 , dyn_addr = 0 ;
u32 reg;
int ret, i;
svc_i3c_master_flush_fifo(master);
while (true ) {
/* clean SVC_I3C_MINT_IBIWON w1c bits */
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
/* SVC_I3C_MCTRL_REQUEST_PROC_DAA have two mode, ENTER DAA or PROCESS DAA.
*
* ENTER DAA :
* 1 will issue START , 7 E , ENTDAA , and then emits 7 E / R to process first target .
* 2 Stops just before the new Dynamic Address ( DA ) is to be emitted .
*
* PROCESS DAA :
* 1 The DA is written using MWDATAB or ADDR bits 6 : 0 .
* 2 ProcessDAA is requested again to write the new address , and then starts the
* next ( START , 7 E , ENTDAA ) unless marked to STOP ; an MSTATUS indicating NACK
* means DA was not accepted ( e . g . parity error ) . If PROCESSDAA is NACKed on the
* 7 E / R , which means no more Slaves need a DA , then a COMPLETE will be signaled
* ( along with DONE ) , and a STOP issued automatically .
*/
writel(SVC_I3C_MCTRL_REQUEST_PROC_DAA |
SVC_I3C_MCTRL_TYPE_I3C |
SVC_I3C_MCTRL_IBIRESP_NACK |
SVC_I3C_MCTRL_DIR(SVC_I3C_MCTRL_DIR_WRITE),
master->regs + SVC_I3C_MCTRL);
/*
* Either one slave will send its ID , or the assignment process
* is done .
*/
ret = readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS,
reg,
SVC_I3C_MSTATUS_RXPEND(reg) |
SVC_I3C_MSTATUS_MCTRLDONE(reg),
1 , 1000 );
if (ret)
break ;
if (SVC_I3C_MSTATUS_RXPEND(reg)) {
u8 data[6 ];
/*
* One slave sends its ID to request for address assignment ,
* prefilling the dynamic address can reduce SCL clock stalls
* and also fix the SVC_I3C_QUIRK_FIFO_EMPTY quirk .
*
* Ideally , prefilling before the processDAA command is better .
* However , it requires an additional check to write the dyn_addr
* at the right time because the driver needs to write the processDAA
* command twice for one assignment .
* Prefilling here is safe and efficient because the FIFO starts
* filling within a few hundred nanoseconds , which is significantly
* faster compared to the 64 SCL clock cycles .
*/
ret = i3c_master_get_free_addr(&master->base, last_addr + 1 );
if (ret < 0 )
break ;
dyn_addr = ret;
writel(dyn_addr, master->regs + SVC_I3C_MWDATAB);
/*
* We only care about the 48 - bit provisioned ID yet to
* be sure a device does not nack an address twice .
* Otherwise , we would just need to flush the RX FIFO .
*/
ret = svc_i3c_master_readb(master, data, 6 );
if (ret)
break ;
for (i = 0 ; i < 6 ; i++)
prov_id[dev_nb] |= (u64)(data[i]) << (8 * (5 - i));
/* We do not care about the BCR and DCR yet */
ret = svc_i3c_master_readb(master, data, 2 );
if (ret)
break ;
} else if (SVC_I3C_MSTATUS_IBIWON(reg)) {
ret = svc_i3c_master_handle_ibi_won(master, reg);
if (ret)
break ;
continue ;
} else if (SVC_I3C_MSTATUS_MCTRLDONE(reg)) {
if (SVC_I3C_MSTATUS_STATE_IDLE(reg) &&
SVC_I3C_MSTATUS_COMPLETE(reg)) {
/*
* All devices received and acked they dynamic
* address , this is the natural end of the DAA
* procedure .
*
* Hardware will auto emit STOP at this case .
*/
*count = dev_nb;
return 0 ;
} else if (SVC_I3C_MSTATUS_NACKED(reg)) {
/* No I3C devices attached */
if (dev_nb == 0 ) {
/*
* Hardware can ' t treat first NACK for ENTAA as normal
* COMPLETE . So need manual emit STOP .
*/
ret = 0 ;
*count = 0 ;
break ;
}
/*
* A slave device nacked the address , this is
* allowed only once , DAA will be stopped and
* then resumed . The same device is supposed to
* answer again immediately and shall ack the
* address this time .
*/
if (prov_id[dev_nb] == nacking_prov_id) {
ret = -EIO;
break ;
}
dev_nb--;
nacking_prov_id = prov_id[dev_nb];
svc_i3c_master_emit_stop(master);
continue ;
} else {
break ;
}
}
/* Wait for the slave to be ready to receive its address */
ret = readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS,
reg,
SVC_I3C_MSTATUS_MCTRLDONE(reg) &&
SVC_I3C_MSTATUS_STATE_DAA(reg) &&
SVC_I3C_MSTATUS_BETWEEN(reg),
0 , 1000 );
if (ret)
break ;
addrs[dev_nb] = dyn_addr;
dev_dbg(master->dev, "DAA: device %d assigned to 0x%02x\n" ,
dev_nb, addrs[dev_nb]);
last_addr = addrs[dev_nb++];
}
/* Need manual issue STOP except for Complete condition */
svc_i3c_master_emit_stop(master);
svc_i3c_master_flush_fifo(master);
return ret;
}
static int svc_i3c_update_ibirules(struct svc_i3c_master *master)
{
struct i3c_dev_desc *dev;
u32 reg_mbyte = 0 , reg_nobyte = SVC_I3C_IBIRULES_NOBYTE;
unsigned int mbyte_addr_ok = 0 , mbyte_addr_ko = 0 , nobyte_addr_ok = 0 ,
nobyte_addr_ko = 0 ;
bool list_mbyte = false , list_nobyte = false ;
/* Create the IBIRULES register for both cases */
i3c_bus_for_each_i3cdev(&master->base.bus, dev) {
if (!(dev->info.bcr & I3C_BCR_IBI_REQ_CAP))
continue ;
if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD) {
reg_mbyte |= SVC_I3C_IBIRULES_ADDR(mbyte_addr_ok,
dev->info.dyn_addr);
/* IBI rules cannot be applied to devices with MSb=1 */
if (dev->info.dyn_addr & BIT(7 ))
mbyte_addr_ko++;
else
mbyte_addr_ok++;
} else {
reg_nobyte |= SVC_I3C_IBIRULES_ADDR(nobyte_addr_ok,
dev->info.dyn_addr);
/* IBI rules cannot be applied to devices with MSb=1 */
if (dev->info.dyn_addr & BIT(7 ))
nobyte_addr_ko++;
else
nobyte_addr_ok++;
}
}
/* Device list cannot be handled by hardware */
if (!mbyte_addr_ko && mbyte_addr_ok <= SVC_I3C_IBIRULES_ADDRS)
list_mbyte = true ;
if (!nobyte_addr_ko && nobyte_addr_ok <= SVC_I3C_IBIRULES_ADDRS)
list_nobyte = true ;
/* No list can be properly handled, return an error */
if (!list_mbyte && !list_nobyte)
return -ERANGE;
/* Pick the first list that can be handled by hardware, randomly */
if (list_mbyte)
writel(reg_mbyte, master->regs + SVC_I3C_IBIRULES);
else
writel(reg_nobyte, master->regs + SVC_I3C_IBIRULES);
return 0 ;
}
static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
{
struct svc_i3c_master *master = to_svc_i3c_master(m);
u8 addrs[SVC_I3C_MAX_DEVS];
unsigned long flags;
unsigned int dev_nb;
int ret, i;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0 ) {
dev_err(master->dev, "<%s> Cannot get runtime PM.\n" , __func__);
return ret;
}
spin_lock_irqsave(&master->xferqueue.lock, flags);
if (svc_has_daa_corrupt(master))
writel(master->mctrl_config | SVC_I3C_MCONFIG_SKEW(1 ),
master->regs + SVC_I3C_MCONFIG);
ret = svc_i3c_master_do_daa_locked(master, addrs, &dev_nb);
if (svc_has_daa_corrupt(master))
writel(master->mctrl_config, master->regs + SVC_I3C_MCONFIG);
spin_unlock_irqrestore(&master->xferqueue.lock, flags);
svc_i3c_master_clear_merrwarn(master);
if (ret)
goto rpm_out;
/*
* Register all devices who participated to the core
*
* If two devices ( A and B ) are detected in DAA and address 0 xa is assigned to
* device A and 0 xb to device B , a failure in i3c_master_add_i3c_dev_locked ( )
* for device A ( addr : 0 xa ) could prevent device B ( addr : 0 xb ) from being
* registered on the bus . The I3C stack might still consider 0 xb a free
* address . If a subsequent Hotjoin occurs , 0 xb might be assigned to Device A ,
* causing both devices A and B to use the same address 0 xb , violating the I3C
* specification .
*
* The return value for i3c_master_add_i3c_dev_locked ( ) should not be checked
* because subsequent steps will scan the entire I3C bus , independent of
* whether i3c_master_add_i3c_dev_locked ( ) returns success .
*
* If device A registration fails , there is still a chance to register device
* B . i3c_master_add_i3c_dev_locked ( ) can reset DAA if a failure occurs while
* retrieving device information .
*/
for (i = 0 ; i < dev_nb; i++)
i3c_master_add_i3c_dev_locked(m, addrs[i]);
/* Configure IBI auto-rules */
ret = svc_i3c_update_ibirules(master);
if (ret)
dev_err(master->dev, "Cannot handle such a list of devices" );
rpm_out:
pm_runtime_put_autosuspend(master->dev);
return ret;
}
static int svc_i3c_master_read(struct svc_i3c_master *master,
u8 *in, unsigned int len)
{
int offset = 0 , i;
u32 mdctrl, mstatus;
bool completed = false ;
unsigned int count;
unsigned long start = jiffies;
while (!completed) {
mstatus = readl(master->regs + SVC_I3C_MSTATUS);
if (SVC_I3C_MSTATUS_COMPLETE(mstatus) != 0 )
completed = true ;
if (time_after(jiffies, start + msecs_to_jiffies(1000 ))) {
dev_dbg(master->dev, "I3C read timeout\n" );
return -ETIMEDOUT;
}
mdctrl = readl(master->regs + SVC_I3C_MDATACTRL);
count = SVC_I3C_MDATACTRL_RXCOUNT(mdctrl);
if (offset + count > len) {
dev_err(master->dev, "I3C receive length too long!\n" );
return -EINVAL;
}
for (i = 0 ; i < count; i++)
in[offset + i] = readl(master->regs + SVC_I3C_MRDATAB);
offset += count;
}
return offset;
}
static int svc_i3c_master_write(struct svc_i3c_master *master,
const u8 *out, unsigned int len)
{
int offset = 0 , ret;
u32 mdctrl;
while (offset < len) {
ret = readl_poll_timeout(master->regs + SVC_I3C_MDATACTRL,
mdctrl,
!(mdctrl & SVC_I3C_MDATACTRL_TXFULL),
0 , 1000 );
if (ret)
return ret;
/*
* The last byte to be sent over the bus must either have the
* " end " bit set or be written in MWDATABE .
*/
if (likely(offset < (len - 1 )))
writel(out[offset++], master->regs + SVC_I3C_MWDATAB);
else
writel(out[offset++], master->regs + SVC_I3C_MWDATABE);
}
return 0 ;
}
static int svc_i3c_master_xfer(struct svc_i3c_master *master,
bool rnw, unsigned int xfer_type, u8 addr,
u8 *in, const u8 *out, unsigned int xfer_len,
unsigned int *actual_len, bool continued, bool repeat_start)
{
int retry = repeat_start ? 1 : 2 ;
u32 reg;
int ret;
/* clean SVC_I3C_MINT_IBIWON w1c bits */
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
while (retry--) {
writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
xfer_type |
SVC_I3C_MCTRL_IBIRESP_NACK |
SVC_I3C_MCTRL_DIR(rnw) |
SVC_I3C_MCTRL_ADDR(addr) |
SVC_I3C_MCTRL_RDTERM(*actual_len),
master->regs + SVC_I3C_MCTRL);
/*
* The entire transaction can consist of multiple write transfers .
* Prefilling before EmitStartAddr causes the data to be emitted
* immediately , becoming part of the previous transfer .
* The only way to work around this hardware issue is to let the
* FIFO start filling as soon as possible after EmitStartAddr .
*/
if (svc_has_quirk(master, SVC_I3C_QUIRK_FIFO_EMPTY) && !rnw && xfer_len) {
u32 space, end, len;
reg = readl(master->regs + SVC_I3C_MDATACTRL);
space = SVC_I3C_FIFO_SIZE - SVC_I3C_MDATACTRL_TXCOUNT(reg);
if (space) {
end = xfer_len > space ? 0 : SVC_I3C_MWDATAB_END;
len = min_t(u32, xfer_len, space);
writesb(master->regs + SVC_I3C_MWDATAB1, out, len - 1 );
/* Mark END bit if this is the last byte */
writel(out[len - 1 ] | end, master->regs + SVC_I3C_MWDATAB);
xfer_len -= len;
out += len;
}
}
ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
SVC_I3C_MSTATUS_MCTRLDONE(reg), 0 , 1000 );
if (ret)
goto emit_stop;
/*
* According to I3C spec ver 1 . 1 . 1 , 5 . 1 . 2 . 2 . 3 Consequence of Controller Starting a
* Frame with I3C Target Address .
*
* The I3C Controller normally should start a Frame , the Address may be arbitrated ,
* and so the Controller shall monitor to see whether an In - Band Interrupt request ,
* a Controller Role Request ( i . e . , Secondary Controller requests to become the
* Active Controller ) , or a Hot - Join Request has been made .
*
* If missed IBIWON check , the wrong data will be return . When IBIWON happen , issue
* repeat start . Address arbitrate only happen at START , never happen at REPEAT
* start .
*/
if (SVC_I3C_MSTATUS_IBIWON(reg)) {
ret = svc_i3c_master_handle_ibi_won(master, reg);
if (ret)
goto emit_stop;
continue ;
}
if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) {
/*
* According to I3C Spec 1 . 1 . 1 , 11 - Jun - 2021 , section : 5 . 1 . 2 . 2 . 3 .
* If the Controller chooses to start an I3C Message with an I3C Dynamic
* Address , then special provisions shall be made because that same I3C
* Target may be initiating an IBI or a Controller Role Request . So , one of
* three things may happen : ( skip 1 , 2 )
*
* 3 . The Addresses match and the RnW bits also match , and so neither
* Controller nor Target will ACK since both are expecting the other side to
* provide ACK . As a result , each side might think it had " won " arbitration ,
* but neither side would continue , as each would subsequently see that the
* other did not provide ACK .
* . . .
* For either value of RnW : Due to the NACK , the Controller shall defer the
* Private Write or Private Read , and should typically transmit the Target
* Address again after a Repeated START ( i . e . , the next one or any one prior
* to a STOP in the Frame ) . Since the Address Header following a Repeated
* START is not arbitrated , the Controller will always win ( see Section
* 5 . 1 . 2 . 2 . 4 ) .
*/
if (retry && addr != 0 x7e) {
writel(SVC_I3C_MERRWARN_NACK, master->regs + SVC_I3C_MERRWARN);
} else {
ret = -ENXIO;
*actual_len = 0 ;
goto emit_stop;
}
} else {
break ;
}
}
if (rnw)
ret = svc_i3c_master_read(master, in, xfer_len);
else
ret = svc_i3c_master_write(master, out, xfer_len);
if (ret < 0 )
goto emit_stop;
if (rnw)
*actual_len = ret;
ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
SVC_I3C_MSTATUS_COMPLETE(reg), 0 , 1000 );
if (ret)
goto emit_stop;
writel(SVC_I3C_MINT_COMPLETE, master->regs + SVC_I3C_MSTATUS);
if (!continued) {
svc_i3c_master_emit_stop(master);
/* Wait idle if stop is sent. */
readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
SVC_I3C_MSTATUS_STATE_IDLE(reg), 0 , 1000 );
}
return 0 ;
emit_stop:
svc_i3c_master_emit_stop(master);
svc_i3c_master_clear_merrwarn(master);
svc_i3c_master_flush_fifo(master);
return ret;
}
static struct svc_i3c_xfer *
svc_i3c_master_alloc_xfer(struct svc_i3c_master *master, unsigned int ncmds)
{
struct svc_i3c_xfer *xfer;
xfer = kzalloc(struct_size(xfer, cmds, ncmds), GFP_KERNEL);
if (!xfer)
return NULL;
INIT_LIST_HEAD(&xfer->node);
xfer->ncmds = ncmds;
xfer->ret = -ETIMEDOUT;
return xfer;
}
static void svc_i3c_master_free_xfer(struct svc_i3c_xfer *xfer)
{
kfree(xfer);
}
static void svc_i3c_master_dequeue_xfer_locked(struct svc_i3c_master *master,
struct svc_i3c_xfer *xfer)
{
if (master->xferqueue.cur == xfer)
master->xferqueue.cur = NULL;
else
list_del_init(&xfer->node);
}
static void svc_i3c_master_dequeue_xfer(struct svc_i3c_master *master,
struct svc_i3c_xfer *xfer)
{
unsigned long flags;
spin_lock_irqsave(&master->xferqueue.lock, flags);
svc_i3c_master_dequeue_xfer_locked(master, xfer);
spin_unlock_irqrestore(&master->xferqueue.lock, flags);
}
static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
{
struct svc_i3c_xfer *xfer = master->xferqueue.cur;
int ret, i;
if (!xfer)
return ;
svc_i3c_master_clear_merrwarn(master);
svc_i3c_master_flush_fifo(master);
for (i = 0 ; i < xfer->ncmds; i++) {
struct svc_i3c_cmd *cmd = &xfer->cmds[i];
ret = svc_i3c_master_xfer(master, cmd->rnw, xfer->type,
cmd->addr, cmd->in, cmd->out,
cmd->len, &cmd->actual_len,
cmd->continued, i > 0 );
/* cmd->xfer is NULL if I2C or CCC transfer */
if (cmd->xfer)
cmd->xfer->actual_len = cmd->actual_len;
if (ret)
break ;
}
xfer->ret = ret;
complete(&xfer->comp);
if (ret < 0 )
svc_i3c_master_dequeue_xfer_locked(master, xfer);
xfer = list_first_entry_or_null(&master->xferqueue.list,
struct svc_i3c_xfer,
node);
if (xfer)
list_del_init(&xfer->node);
master->xferqueue.cur = xfer;
svc_i3c_master_start_xfer_locked(master);
}
static void svc_i3c_master_enqueue_xfer(struct svc_i3c_master *master,
struct svc_i3c_xfer *xfer)
{
unsigned long flags;
int ret;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0 ) {
dev_err(master->dev, "<%s> Cannot get runtime PM.\n" , __func__);
return ;
}
init_completion(&xfer->comp);
spin_lock_irqsave(&master->xferqueue.lock, flags);
if (master->xferqueue.cur) {
list_add_tail(&xfer->node, &master->xferqueue.list);
} else {
master->xferqueue.cur = xfer;
svc_i3c_master_start_xfer_locked(master);
}
spin_unlock_irqrestore(&master->xferqueue.lock, flags);
pm_runtime_put_autosuspend(master->dev);
}
static bool
svc_i3c_master_supports_ccc_cmd(struct i3c_master_controller *master,
const struct i3c_ccc_cmd *cmd)
{
/* No software support for CCC commands targeting more than one slave */
return (cmd->ndests == 1 );
}
static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
struct i3c_ccc_cmd *ccc)
{
unsigned int xfer_len = ccc->dests[0 ].payload.len + 1 ;
struct svc_i3c_xfer *xfer;
struct svc_i3c_cmd *cmd;
u8 *buf;
int ret;
xfer = svc_i3c_master_alloc_xfer(master, 1 );
if (!xfer)
return -ENOMEM;
buf = kmalloc(xfer_len, GFP_KERNEL);
if (!buf) {
svc_i3c_master_free_xfer(xfer);
return -ENOMEM;
}
buf[0 ] = ccc->id;
memcpy(&buf[1 ], ccc->dests[0 ].payload.data, ccc->dests[0 ].payload.len);
xfer->type = SVC_I3C_MCTRL_TYPE_I3C;
cmd = &xfer->cmds[0 ];
cmd->addr = ccc->dests[0 ].addr;
cmd->rnw = ccc->rnw;
cmd->in = NULL;
cmd->out = buf;
cmd->len = xfer_len;
cmd->actual_len = 0 ;
cmd->continued = false ;
mutex_lock(&master->lock);
svc_i3c_master_enqueue_xfer(master, xfer);
if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000 )))
svc_i3c_master_dequeue_xfer(master, xfer);
mutex_unlock(&master->lock);
ret = xfer->ret;
kfree(buf);
svc_i3c_master_free_xfer(xfer);
return ret;
}
static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
struct i3c_ccc_cmd *ccc)
{
unsigned int xfer_len = ccc->dests[0 ].payload.len;
unsigned int actual_len = ccc->rnw ? xfer_len : 0 ;
struct svc_i3c_xfer *xfer;
struct svc_i3c_cmd *cmd;
int ret;
xfer = svc_i3c_master_alloc_xfer(master, 2 );
if (!xfer)
return -ENOMEM;
xfer->type = SVC_I3C_MCTRL_TYPE_I3C;
/* Broadcasted message */
cmd = &xfer->cmds[0 ];
cmd->addr = I3C_BROADCAST_ADDR;
cmd->rnw = 0 ;
cmd->in = NULL;
cmd->out = &ccc->id;
cmd->len = 1 ;
cmd->actual_len = 0 ;
cmd->continued = true ;
/* Directed message */
cmd = &xfer->cmds[1 ];
cmd->addr = ccc->dests[0 ].addr;
cmd->rnw = ccc->rnw;
cmd->in = ccc->rnw ? ccc->dests[0 ].payload.data : NULL;
cmd->out = ccc->rnw ? NULL : ccc->dests[0 ].payload.data;
cmd->len = xfer_len;
cmd->actual_len = actual_len;
cmd->continued = false ;
mutex_lock(&master->lock);
svc_i3c_master_enqueue_xfer(master, xfer);
if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000 )))
svc_i3c_master_dequeue_xfer(master, xfer);
mutex_unlock(&master->lock);
if (cmd->actual_len != xfer_len)
ccc->dests[0 ].payload.len = cmd->actual_len;
ret = xfer->ret;
svc_i3c_master_free_xfer(xfer);
return ret;
}
static int svc_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
struct i3c_ccc_cmd *cmd)
{
struct svc_i3c_master *master = to_svc_i3c_master(m);
bool broadcast = cmd->id < 0 x80;
int ret;
if (broadcast)
ret = svc_i3c_master_send_bdcast_ccc_cmd(master, cmd);
else
ret = svc_i3c_master_send_direct_ccc_cmd(master, cmd);
if (ret)
cmd->err = I3C_ERROR_M2;
return ret;
}
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);
struct svc_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
struct svc_i3c_xfer *xfer;
int ret, i;
xfer = svc_i3c_master_alloc_xfer(master, nxfers);
if (!xfer)
return -ENOMEM;
xfer->type = SVC_I3C_MCTRL_TYPE_I3C;
for (i = 0 ; i < nxfers; i++) {
struct svc_i3c_cmd *cmd = &xfer->cmds[i];
cmd->xfer = &xfers[i];
cmd->addr = master->addrs[data->index];
cmd->rnw = xfers[i].rnw;
cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL;
cmd->out = xfers[i].rnw ? NULL : xfers[i].data.out;
cmd->len = xfers[i].len;
cmd->actual_len = xfers[i].rnw ? xfers[i].len : 0 ;
cmd->continued = (i + 1 ) < nxfers;
}
mutex_lock(&master->lock);
svc_i3c_master_enqueue_xfer(master, xfer);
if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000 )))
svc_i3c_master_dequeue_xfer(master, xfer);
mutex_unlock(&master->lock);
ret = xfer->ret;
svc_i3c_master_free_xfer(xfer);
return ret;
}
static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
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);
struct svc_i3c_xfer *xfer;
int ret, i;
xfer = svc_i3c_master_alloc_xfer(master, nxfers);
if (!xfer)
return -ENOMEM;
xfer->type = SVC_I3C_MCTRL_TYPE_I2C;
for (i = 0 ; i < nxfers; i++) {
struct svc_i3c_cmd *cmd = &xfer->cmds[i];
cmd->addr = master->addrs[data->index];
cmd->rnw = xfers[i].flags & I2C_M_RD;
cmd->in = cmd->rnw ? xfers[i].buf : NULL;
cmd->out = cmd->rnw ? NULL : xfers[i].buf;
cmd->len = xfers[i].len;
cmd->actual_len = cmd->rnw ? xfers[i].len : 0 ;
cmd->continued = (i + 1 < nxfers);
}
mutex_lock(&master->lock);
svc_i3c_master_enqueue_xfer(master, xfer);
if (!wait_for_completion_timeout(&xfer->comp, m->i2c.timeout))
svc_i3c_master_dequeue_xfer(master, xfer);
mutex_unlock(&master->lock);
ret = xfer->ret;
svc_i3c_master_free_xfer(xfer);
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;
if (dev->ibi->max_payload_len > SVC_I3C_FIFO_SIZE) {
dev_err(master->dev, "IBI max payload %d should be < %d\n" ,
dev->ibi->max_payload_len, SVC_I3C_FIFO_SIZE);
return -ERANGE;
}
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_slots; i++) {
if (!master->ibi.slots[i]) {
data->ibi = i;
master->ibi.slots[i] = dev;
break ;
}
}
spin_unlock_irqrestore(&master->ibi.lock, flags);
if (i < master->ibi.num_slots)
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.slots[data->ibi] = NULL;
data->ibi = -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)
{
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct svc_i3c_master *master = to_svc_i3c_master(m);
int ret;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0 ) {
dev_err(master->dev, "<%s> Cannot get runtime PM.\n" , __func__);
return ret;
}
master->enabled_events++;
svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
}
static int svc_i3c_master_disable_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);
int ret;
master->enabled_events--;
if (!master->enabled_events)
svc_i3c_master_disable_interrupts(master);
ret = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
pm_runtime_put_autosuspend(master->dev);
return ret;
}
static int svc_i3c_master_enable_hotjoin(struct i3c_master_controller *m)
{
struct svc_i3c_master *master = to_svc_i3c_master(m);
int ret;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0 ) {
dev_err(master->dev, "<%s> Cannot get runtime PM.\n" , __func__);
return ret;
}
master->enabled_events |= SVC_I3C_EVENT_HOTJOIN;
svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
return 0 ;
}
static int svc_i3c_master_disable_hotjoin(struct i3c_master_controller *m)
{
struct svc_i3c_master *master = to_svc_i3c_master(m);
master->enabled_events &= ~SVC_I3C_EVENT_HOTJOIN;
if (!master->enabled_events)
svc_i3c_master_disable_interrupts(master);
pm_runtime_put_autosuspend(master->dev);
return 0 ;
}
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,
.bus_cleanup = svc_i3c_master_bus_cleanup,
.attach_i3c_dev = svc_i3c_master_attach_i3c_dev,
.detach_i3c_dev = svc_i3c_master_detach_i3c_dev,
.reattach_i3c_dev = svc_i3c_master_reattach_i3c_dev,
.attach_i2c_dev = svc_i3c_master_attach_i2c_dev,
.detach_i2c_dev = svc_i3c_master_detach_i2c_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,
.i2c_xfers = svc_i3c_master_i2c_xfers,
.request_ibi = svc_i3c_master_request_ibi,
.free_ibi = svc_i3c_master_free_ibi,
.recycle_ibi_slot = svc_i3c_master_recycle_ibi_slot,
.enable_ibi = svc_i3c_master_enable_ibi,
.disable_ibi = svc_i3c_master_disable_ibi,
.enable_hotjoin = svc_i3c_master_enable_hotjoin,
.disable_hotjoin = svc_i3c_master_disable_hotjoin,
.set_speed = svc_i3c_master_set_speed,
};
static int svc_i3c_master_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct svc_i3c_master *master;
int ret, i;
master = devm_kzalloc(dev, sizeof (*master), GFP_KERNEL);
if (!master)
return -ENOMEM;
master->drvdata = of_device_get_match_data(dev);
if (!master->drvdata)
return -EINVAL;
master->regs = devm_platform_ioremap_resource(pdev, 0 );
if (IS_ERR(master->regs))
return PTR_ERR(master->regs);
master->num_clks = devm_clk_bulk_get_all(dev, &master->clks);
if (master->num_clks < 0 )
return dev_err_probe(dev, -EINVAL, "can't get I3C clocks\n" );
for (i = 0 ; i < master->num_clks; i++) {
if (!strcmp(master->clks[i].id, "fast_clk" ))
break ;
}
if (i == master->num_clks)
return dev_err_probe(dev, -EINVAL,
"can't get I3C peripheral clock\n" );
master->fclk = master->clks[i].clk;
if (IS_ERR(master->fclk))
return PTR_ERR(master->fclk);
master->irq = platform_get_irq(pdev, 0 );
if (master->irq < 0 )
return master->irq;
master->dev = dev;
ret = clk_bulk_prepare_enable(master->num_clks, master->clks);
if (ret)
return dev_err_probe(dev, ret, "can't enable I3C clocks\n" );
INIT_WORK(&master->hj_work, svc_i3c_master_hj_work);
mutex_init(&master->lock);
ret = devm_request_irq(dev, master->irq, svc_i3c_master_irq_handler,
IRQF_NO_SUSPEND, "svc-i3c-irq" , master);
if (ret)
goto err_disable_clks;
master->free_slots = GENMASK(SVC_I3C_MAX_DEVS - 1 , 0 );
spin_lock_init(&master->xferqueue.lock);
INIT_LIST_HEAD(&master->xferqueue.list);
spin_lock_init(&master->ibi.lock);
master->ibi.num_slots = SVC_I3C_MAX_DEVS;
master->ibi.slots = devm_kcalloc(&pdev->dev, master->ibi.num_slots,
sizeof (*master->ibi.slots),
GFP_KERNEL);
if (!master->ibi.slots) {
ret = -ENOMEM;
goto err_disable_clks;
}
platform_set_drvdata(pdev, master);
pm_runtime_set_autosuspend_delay(&pdev->dev, SVC_I3C_PM_TIMEOUT_MS);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
svc_i3c_master_reset(master);
/* Register the master */
ret = i3c_master_register(&master->base, &pdev->dev,
&svc_i3c_master_ops, false );
if (ret)
goto rpm_disable;
pm_runtime_put_autosuspend(&pdev->dev);
return 0 ;
rpm_disable:
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
err_disable_clks:
clk_bulk_disable_unprepare(master->num_clks, master->clks);
return ret;
}
static void svc_i3c_master_remove(struct platform_device *pdev)
{
struct svc_i3c_master *master = platform_get_drvdata(pdev);
cancel_work_sync(&master->hj_work);
i3c_master_unregister(&master->base);
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
static void svc_i3c_save_regs(struct svc_i3c_master *master)
{
master->saved_regs.mconfig = readl(master->regs + SVC_I3C_MCONFIG);
master->saved_regs.mdynaddr = readl(master->regs + SVC_I3C_MDYNADDR);
}
static void svc_i3c_restore_regs(struct svc_i3c_master *master)
{
if (readl(master->regs + SVC_I3C_MDYNADDR) !=
master->saved_regs.mdynaddr) {
writel(master->saved_regs.mconfig,
master->regs + SVC_I3C_MCONFIG);
writel(master->saved_regs.mdynaddr,
master->regs + SVC_I3C_MDYNADDR);
}
}
static int __maybe_unused svc_i3c_runtime_suspend(struct device *dev)
{
struct svc_i3c_master *master = dev_get_drvdata(dev);
svc_i3c_save_regs(master);
clk_bulk_disable_unprepare(master->num_clks, master->clks);
pinctrl_pm_select_sleep_state(dev);
return 0 ;
}
static int __maybe_unused svc_i3c_runtime_resume(struct device *dev)
{
struct svc_i3c_master *master = dev_get_drvdata(dev);
int ret;
pinctrl_pm_select_default_state(dev);
ret = clk_bulk_prepare_enable(master->num_clks, master->clks);
if (ret)
return ret;
svc_i3c_restore_regs(master);
return 0 ;
}
static const struct dev_pm_ops svc_i3c_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(svc_i3c_runtime_suspend,
svc_i3c_runtime_resume, NULL)
};
static const struct svc_i3c_drvdata npcm845_drvdata = {
.quirks = SVC_I3C_QUIRK_FIFO_EMPTY |
SVC_I3C_QUIRK_FALSE_SLVSTART |
SVC_I3C_QUIRK_DAA_CORRUPT,
};
static const struct svc_i3c_drvdata svc_default_drvdata = {};
static const struct of_device_id svc_i3c_master_of_match_tbl[] = {
{ .compatible = "nuvoton,npcm845-i3c" , .data = &npcm845_drvdata },
{ .compatible = "silvaco,i3c-master-v1" , .data = &svc_default_drvdata },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, svc_i3c_master_of_match_tbl);
static struct platform_driver svc_i3c_master = {
.probe = svc_i3c_master_probe,
.remove = svc_i3c_master_remove,
.driver = {
.name = "silvaco-i3c-master" ,
.of_match_table = svc_i3c_master_of_match_tbl,
.pm = &svc_i3c_pm_ops,
},
};
module_platform_driver(svc_i3c_master);
MODULE_AUTHOR("Conor Culhane <conor.culhane@silvaco.com>" );
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>" );
MODULE_DESCRIPTION("Silvaco dual-role I3C master driver" );
MODULE_LICENSE("GPL v2" );
Messung V0.5 in Prozent C=97 H=93 G=94
¤ Dauer der Verarbeitung: 0.56 Sekunden
(vorverarbeitet am 2026-09-30)
¤
*© Formatika GbR, Deutschland