Re: [PATCH v1 2/2] i3c: master: Add Nuvoton npcm845 i3c master driver
From: Stanley Chu <hidden>
Date: 2024-08-05 06:27:18
Also in:
linux-i3c, lkml, openbmc
On Thu, Aug 1, 2024 at 11:02 PM Krzysztof Kozlowski [off-list ref] wrote:
On 01/08/2024 09:19, Stanley Chu wrote:quoted
Add support for the Nuvoton npcm845 i3c controller which implements I3C master functionality as defined in the MIPI Alliance Specification for I3C, Version 1.0. The master role is supported in SDR mode only. IBI and Hot-join requsts are supported. Signed-off-by: Stanley Chu <yschu@nuvoton.com> Signed-off-by: James Chiang <redacted> --- MAINTAINERS | 7 + drivers/i3c/master/Kconfig | 14 + drivers/i3c/master/Makefile | 1 + drivers/i3c/master/npcm845-i3c-master.c | 2364 +++++++++++++++++++++++ 4 files changed, 2386 insertions(+) create mode 100644 drivers/i3c/master/npcm845-i3c-master.cdiff --git a/MAINTAINERS b/MAINTAINERS index 42decde38320..2d30b6e418d8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS@@ -2670,6 +2670,13 @@ F: Documentation/userspace-api/media/drivers/npcm-video.rst F: drivers/media/platform/nuvoton/ F: include/uapi/linux/npcm-video.h +ARM/NUVOTON NPCM845 I3C MASTER DRIVERUse proper terminology. Master is gone since 2021, right? This applies everywhere.
Thanks for the review. I will fix it in v2.
quoted
+M: Stanley Chu [off-list ref] +M: James Chiang [off-list ref] +S: Maintained +F: Documentation/devicetree/bindings/i3c/nuvoton,i3c-master.yaml +F: drivers/i3c/master/npcm845-i3c-master.c + ARM/NUVOTON WPCM450 ARCHITECTURE M: Jonathan Neuschäfer [off-list ref] L: openbmc@lists.ozlabs.org (moderated for non-subscribers)diff --git a/drivers/i3c/master/Kconfig b/drivers/i3c/master/Kconfig index 90dee3ec5520..a71d504d4744 100644 --- a/drivers/i3c/master/Kconfig +++ b/drivers/i3c/master/Kconfig@@ -44,6 +44,20 @@ config SVC_I3C_MASTER help Support for Silvaco I3C Dual-Role Master Controller. +config NPCM845_I3C_MASTER + tristate "Nuvoton NPCM845 I3C master driver" + depends on I3C + depends on HAS_IOMEM + depends on ARCH_NPCM || COMPILE_TEST + help + Support for Nuvoton NPCM845 I3C Master Controller. + + This hardware is an instance of the SVC I3C controller; this + driver adds platform specific support for NPCM845 hardware. + + This driver can also be built as a module. If so, the module + will be called npcm845-i3c-master. + config MIPI_I3C_HCI tristate "MIPI I3C Host Controller Interface driver (EXPERIMENTAL)" depends on I3Cdiff --git a/drivers/i3c/master/Makefile b/drivers/i3c/master/Makefile index 3e97960160bc..3ed55113190a 100644 --- a/drivers/i3c/master/Makefile +++ b/drivers/i3c/master/Makefile@@ -3,4 +3,5 @@ obj-$(CONFIG_CDNS_I3C_MASTER) += i3c-master-cdns.o obj-$(CONFIG_DW_I3C_MASTER) += dw-i3c-master.o obj-$(CONFIG_AST2600_I3C_MASTER) += ast2600-i3c-master.o obj-$(CONFIG_SVC_I3C_MASTER) += svc-i3c-master.o +obj-$(CONFIG_NPCM845_I3C_MASTER) += npcm845-i3c-master.o obj-$(CONFIG_MIPI_I3C_HCI) += mipi-i3c-hci/diff --git a/drivers/i3c/master/npcm845-i3c-master.c b/drivers/i3c/master/npcm845-i3c-master.c new file mode 100644 index 000000000000..19672fdbe2b8 --- /dev/null +++ b/drivers/i3c/master/npcm845-i3c-master.c@@ -0,0 +1,2364 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nuvoton NPCM845 I3C master driver + * + * Copyright (C) 2024 Nuvoton Technology Corp. + * Based on the work from svc i3c master driver and add platform + * specific support for the NPCM845 hardware. + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/debugfs.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/reset.h> +#include <linux/i3c/master.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> + +/* Master Mode Registers */ +#define NPCM_I3C_MCONFIG 0x000 +#define NPCM_I3C_MCONFIG_MASTER_EN BIT(0) +#define NPCM_I3C_MCONFIG_DISTO(x) FIELD_PREP(BIT(3), (x)) +#define NPCM_I3C_MCONFIG_HKEEP(x) FIELD_PREP(GENMASK(5, 4), (x)) +#define NPCM_I3C_MCONFIG_ODSTOP(x) FIELD_PREP(BIT(6), (x)) +#define NPCM_I3C_MCONFIG_PPBAUD(x) FIELD_PREP(GENMASK(11, 8), (x)) +#define NPCM_I3C_MCONFIG_PPLOW(x) FIELD_PREP(GENMASK(15, 12), (x)) +#define NPCM_I3C_MCONFIG_ODBAUD(x) FIELD_PREP(GENMASK(23, 16), (x)) +#define NPCM_I3C_MCONFIG_ODHPP(x) FIELD_PREP(BIT(24), (x)) +#define NPCM_I3C_MCONFIG_SKEW(x) FIELD_PREP(GENMASK(27, 25), (x)) +#define NPCM_I3C_MCONFIG_SKEW_MASK GENMASK(27, 25) +#define NPCM_I3C_MCONFIG_I2CBAUD(x) FIELD_PREP(GENMASK(31, 28), (x)) + +#define NPCM_I3C_MCTRL 0x084 +#define NPCM_I3C_MCTRL_REQUEST_MASK GENMASK(2, 0) +#define NPCM_I3C_MCTRL_REQUEST(x) FIELD_GET(GENMASK(2, 0), (x)) +#define NPCM_I3C_MCTRL_REQUEST_NONE 0 +#define NPCM_I3C_MCTRL_REQUEST_START_ADDR 1 +#define NPCM_I3C_MCTRL_REQUEST_STOP 2 +#define NPCM_I3C_MCTRL_REQUEST_IBI_ACKNACK 3 +#define NPCM_I3C_MCTRL_REQUEST_PROC_DAA 4 +#define NPCM_I3C_MCTRL_REQUEST_FORCE_EXIT 6 +#define NPCM_I3C_MCTRL_REQUEST_AUTO_IBI 7 +#define NPCM_I3C_MCTRL_TYPE_I3C 0 +#define NPCM_I3C_MCTRL_TYPE_I2C BIT(4) +#define NPCM_I3C_MCTRL_IBIRESP_AUTO 0 +#define NPCM_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE 0 +#define NPCM_I3C_MCTRL_IBIRESP_ACK_WITH_BYTE BIT(7) +#define NPCM_I3C_MCTRL_IBIRESP_NACK BIT(6) +#define NPCM_I3C_MCTRL_IBIRESP_MANUAL GENMASK(7, 6) +#define NPCM_I3C_MCTRL_DIR(x) FIELD_PREP(BIT(8), (x)) +#define NPCM_I3C_MCTRL_DIR_WRITE 0 +#define NPCM_I3C_MCTRL_DIR_READ 1 +#define NPCM_I3C_MCTRL_ADDR(x) FIELD_PREP(GENMASK(15, 9), (x)) +#define NPCM_I3C_MCTRL_RDTERM(x) FIELD_PREP(GENMASK(23, 16), (x)) + +#define NPCM_I3C_MSTATUS 0x088 +#define NPCM_I3C_MSTATUS_STATE(x) FIELD_GET(GENMASK(2, 0), (x)) +#define NPCM_I3C_MSTATUS_STATE_DAA(x) (NPCM_I3C_MSTATUS_STATE(x) == 5) +#define NPCM_I3C_MSTATUS_STATE_IDLE(x) (NPCM_I3C_MSTATUS_STATE(x) == 0) +#define NPCM_I3C_MSTATUS_STATE_SLVREQ(x) (NPCM_I3C_MSTATUS_STATE(x) == 1) +#define NPCM_I3C_MSTATUS_STATE_IBIACK(x) (NPCM_I3C_MSTATUS_STATE(x) == 6) +#define NPCM_I3C_MSTATUS_BETWEEN(x) FIELD_GET(BIT(4), (x)) +#define NPCM_I3C_MSTATUS_NACKED(x) FIELD_GET(BIT(5), (x)) +#define NPCM_I3C_MSTATUS_IBITYPE(x) FIELD_GET(GENMASK(7, 6), (x)) +#define NPCM_I3C_MSTATUS_IBITYPE_IBI 1 +#define NPCM_I3C_MSTATUS_IBITYPE_MASTER_REQUEST 2 +#define NPCM_I3C_MSTATUS_IBITYPE_HOT_JOIN 3 +#define NPCM_I3C_MINT_SLVSTART BIT(8) +#define NPCM_I3C_MINT_MCTRLDONE BIT(9) +#define NPCM_I3C_MINT_COMPLETE BIT(10) +#define NPCM_I3C_MINT_RXPEND BIT(11) +#define NPCM_I3C_MINT_TXNOTFULL BIT(12) +#define NPCM_I3C_MINT_IBIWON BIT(13) +#define NPCM_I3C_MINT_ERRWARN BIT(15) +#define NPCM_I3C_MSTATUS_SLVSTART(x) FIELD_GET(NPCM_I3C_MINT_SLVSTART, (x)) +#define NPCM_I3C_MSTATUS_MCTRLDONE(x) FIELD_GET(NPCM_I3C_MINT_MCTRLDONE, (x)) +#define NPCM_I3C_MSTATUS_COMPLETE(x) FIELD_GET(NPCM_I3C_MINT_COMPLETE, (x)) +#define NPCM_I3C_MSTATUS_RXPEND(x) FIELD_GET(NPCM_I3C_MINT_RXPEND, (x)) +#define NPCM_I3C_MSTATUS_TXNOTFULL(x) FIELD_GET(NPCM_I3C_MINT_TXNOTFULL, (x)) +#define NPCM_I3C_MSTATUS_IBIWON(x) FIELD_GET(NPCM_I3C_MINT_IBIWON, (x)) +#define NPCM_I3C_MSTATUS_ERRWARN(x) FIELD_GET(NPCM_I3C_MINT_ERRWARN, (x)) +#define NPCM_I3C_MSTATUS_IBIADDR(x) FIELD_GET(GENMASK(30, 24), (x)) + +#define NPCM_I3C_IBIRULES 0x08C +#define NPCM_I3C_IBIRULES_ADDR(slot, addr) FIELD_PREP(GENMASK(29, 0), \ + ((addr) & 0x3F) << ((slot) * 6)) +#define NPCM_I3C_IBIRULES_ADDRS 5 +#define NPCM_I3C_IBIRULES_MSB0 BIT(30) +#define NPCM_I3C_IBIRULES_NOBYTE BIT(31) +#define NPCM_I3C_IBIRULES_MANDBYTE 0 +#define NPCM_I3C_MINTSET 0x090 +#define NPCM_I3C_MINTCLR 0x094 +#define NPCM_I3C_MINTMASKED 0x098 +#define NPCM_I3C_MERRWARN 0x09C +#define NPCM_I3C_MERRWARN_NACK(x) FIELD_GET(BIT(2), (x)) +#define NPCM_I3C_MERRWARN_TIMEOUT BIT(20) +#define NPCM_I3C_MERRWARN_HCRC(x) FIELD_GET(BIT(10), (x)) +#define NPCM_I3C_MDMACTRL 0x0A0 +#define NPCM_I3C_MDMACTRL_DMAFB(x) FIELD_PREP(GENMASK(1, 0), (x)) +#define NPCM_I3C_MDMACTRL_DMATB(x) FIELD_PREP(GENMASK(3, 2), (x)) +#define NPCM_I3C_MDMACTRL_DMAWIDTH(x) FIELD_PREP(GENMASK(5, 4), (x)) +#define NPCM_I3C_MDATACTRL 0x0AC +#define NPCM_I3C_MDATACTRL_FLUSHTB BIT(0) +#define NPCM_I3C_MDATACTRL_FLUSHRB BIT(1) +#define NPCM_I3C_MDATACTRL_UNLOCK_TRIG BIT(3) +#define NPCM_I3C_MDATACTRL_TXTRIG_FIFO_NOT_FULL GENMASK(5, 4) +#define NPCM_I3C_MDATACTRL_RXTRIG_FIFO_NOT_EMPTY 0 +#define NPCM_I3C_MDATACTRL_RXCOUNT(x) FIELD_GET(GENMASK(28, 24), (x)) +#define NPCM_I3C_MDATACTRL_TXCOUNT(x) FIELD_GET(GENMASK(20, 16), (x)) +#define NPCM_I3C_MDATACTRL_TXFULL BIT(30) +#define NPCM_I3C_MDATACTRL_RXEMPTY BIT(31) + +#define NPCM_I3C_MWDATAB 0x0B0 +#define NPCM_I3C_MWDATAB_END BIT(8) + +#define NPCM_I3C_MWDATABE 0x0B4 +#define NPCM_I3C_MWDATAH 0x0B8 +#define NPCM_I3C_MWDATAHE 0x0BC +#define NPCM_I3C_MRDATAB 0x0C0 +#define NPCM_I3C_MRDATAH 0x0C8 + +#define NPCM_I3C_MDYNADDR 0x0E4 +#define NPCM_MDYNADDR_VALID BIT(0) +#define NPCM_MDYNADDR_ADDR(x) FIELD_PREP(GENMASK(7, 1), (x)) + +#define NPCM_I3C_PARTNO 0x06C +#define NPCM_I3C_VENDORID 0x074 +#define NPCM_I3C_VENDORID_VID(x) FIELD_GET(GENMASK(14, 0), (x)) + +#define NPCM_I3C_MAX_DEVS 32 +#define NPCM_I3C_PM_TIMEOUT_MS 1000 + +/* This parameter depends on the implementation and may be tuned */ +#define NPCM_I3C_FIFO_SIZE 16 +#define NPCM_I3C_MAX_IBI_PAYLOAD_SIZE 8 +#define NPCM_I3C_MAX_RDTERM 255 +#define NPCM_I3C_MAX_PPBAUD 15 +#define NPCM_I3C_MAX_PPLOW 15 +#define NPCM_I3C_MAX_ODBAUD 255 +#define NPCM_I3C_MAX_I2CBAUD 15 +#define I3C_SCL_PP_PERIOD_NS_MIN 40 +#define I3C_SCL_OD_LOW_PERIOD_NS_MIN 200 + +/* DMA definitions */ +#define MAX_DMA_COUNT 1024 +#define DMA_CH_TX 0 +#define DMA_CH_RX 1 +#define NPCM_GDMA_CTL(n) (n * 0x20 + 0x00) +#define NPCM_GDMA_CTL_GDMAMS(x) FIELD_PREP(GENMASK(3, 2), (x)) +#define NPCM_GDMA_CTL_TWS(x) FIELD_PREP(GENMASK(13, 12), (x)) +#define NPCM_GDMA_CTL_GDMAEN BIT(0) +#define NPCM_GDMA_CTL_DAFIX BIT(6) +#define NPCM_GDMA_CTL_SAFIX BIT(7) +#define NPCM_GDMA_CTL_SIEN BIT(8) +#define NPCM_GDMA_CTL_DM BIT(15) +#define NPCM_GDMA_CTL_TC BIT(18) +#define NPCM_GDMA_SRCB(n) (n * 0x20 + 0x04) +#define NPCM_GDMA_DSTB(n) (n * 0x20 + 0x08) +#define NPCM_GDMA_TCNT(n) (n * 0x20 + 0x0C) +#define NPCM_GDMA_CSRC(n) (n * 0x20 + 0x10) +#define NPCM_GDMA_CDST(n) (n * 0x20 + 0x14) +#define NPCM_GDMA_CTCNT(n) (n * 0x20 + 0x18) +#define NPCM_GDMA_MUX(n) (((n & 0xFFFF) >> 12) * 2 + 6) +#define GDMA_CH0_EN GENMASK(6, 5) +#define GDMA_CH1_EN GENMASK(22, 21) + +struct npcm_i3c_cmd { + u8 addr; + bool rnw; + u8 *in; + const void *out; + unsigned int len; + unsigned int read_len; + bool continued; + bool use_dma; +}; + +struct npcm_i3c_xfer { + struct list_head node; + struct completion comp; + int ret; + unsigned int type; + unsigned int ncmds; + struct npcm_i3c_cmd cmds[]; +}; + +struct npcm_i3c_regs_save { + u32 mconfig; + u32 mdynaddr; +}; + +struct npcm_dma_xfer_desc { + const u8 *out; + u8 *in; + u32 len; + bool rnw; + bool end; +}; +/** + * struct npcm_i3c_master - npcm845 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 + * @pclk: System clock + * @fclk: Fast clock (bus) + * @sclk: Slow clock (other events) + * @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, prevent concurrent daa/priv_xfer/ccc + * @req_lock: protect between IBI isr and bus operation request + */ +struct npcm_i3c_master { + struct i3c_master_controller base; + struct device *dev; + void __iomem *regs; + struct npcm_i3c_regs_save saved_regs; + u32 free_slots; + u8 addrs[NPCM_I3C_MAX_DEVS]; + struct i3c_dev_desc *descs[NPCM_I3C_MAX_DEVS]; + struct work_struct hj_work; + int irq; + struct clk *pclk; + struct clk *fclk; + struct { + u32 i3c_pp_hi; + u32 i3c_pp_lo; + u32 i3c_pp_sda_rd_skew; + u32 i3c_pp_sda_wr_skew; + u32 i3c_od_hi; + u32 i3c_od_lo; + } scl_timing; + struct { + struct list_head list; + struct npcm_i3c_xfer *cur; + } 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; + spinlock_t req_lock; + struct mutex lock; + struct dentry *debugfs; + + /* For DMA */ + void __iomem *dma_regs; + void __iomem *dma_ctl_regs; + bool use_dma; + struct completion xfer_comp; + char *dma_tx_buf; + char *dma_rx_buf; + dma_addr_t dma_tx_addr; + dma_addr_t dma_rx_addr; + struct npcm_dma_xfer_desc dma_xfer; + + bool en_hj; +}; + +/** + * struct npcm_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 npcm_i3c_i2c_dev_data { + u8 index; + int ibi; + struct i3c_generic_ibi_pool *ibi_pool; +}; + +static DEFINE_MUTEX(npcm_i3c_dma_lock);Why this is outside driver private data - npcm_i3c_master?quoted
+ +static int npcm_i3c_master_wait_for_complete(struct npcm_i3c_master *master); +static void npcm_i3c_master_stop_dma(struct npcm_i3c_master *master); + +static void npcm_i3c_master_dma_lock(void) +{ + mutex_lock(&npcm_i3c_dma_lock); +} + +static void npcm_i3c_master_dma_unlock(void) +{ + mutex_unlock(&npcm_i3c_dma_lock); +}...quoted
+ +static void npcm_i3c_init_debugfs(struct platform_device *pdev, + struct npcm_i3c_master *master) +{ + if (!npcm_i3c_debugfs_dir) { + npcm_i3c_debugfs_dir = debugfs_create_dir("npcm_i3c", NULL); + if (!npcm_i3c_debugfs_dir) + return; + } + + master->debugfs = debugfs_create_dir(dev_name(&pdev->dev), + npcm_i3c_debugfs_dir); + if (!master->debugfs) + return; + + debugfs_create_file("debug", 0444, master->debugfs, master, &debug_fops); +} + +static int npcm_i3c_setup_dma(struct platform_device *pdev, struct npcm_i3c_master *master) +{ + struct device *dev = &pdev->dev; + u32 dma_conn, dma_ctl, reg_base; + + if (!of_property_read_bool(dev->of_node, "use-dma")) + return 0; + + master->dma_regs = devm_platform_ioremap_resource_byname(pdev, "dma"); + if (IS_ERR(master->dma_regs)) + return 0; + + master->dma_ctl_regs = devm_platform_ioremap_resource_byname(pdev, "dma_ctl"); + if (IS_ERR(master->dma_ctl_regs))Hm? Why this is not a separate DMA controller?
I will implement a separate dma driver later, the dma configuration and lock will be removed from i3c driver.
quoted
+ return 0; + + /* DMA TX transfer width is 32 bits(MWDATAB width) for each byte sent to I3C bus */ + master->dma_tx_buf = dma_alloc_coherent(dev, MAX_DMA_COUNT * 4, + &master->dma_tx_addr, GFP_KERNEL); + if (!master->dma_tx_buf) + return -ENOMEM; + + master->dma_rx_buf = dma_alloc_coherent(dev, MAX_DMA_COUNT, + &master->dma_rx_addr, GFP_KERNEL); + if (!master->dma_rx_buf) { + dma_free_coherent(master->dev, MAX_DMA_COUNT * 4, master->dma_tx_buf, + master->dma_tx_addr); + return -ENOMEM; + } + + /* + * Set DMA channel connectivity + * channel 0: I3C TX, channel 1: I3C RX + */ + of_property_read_u32_index(dev->of_node, "reg", 0, ®_base); + dma_conn = NPCM_GDMA_MUX(reg_base); + dma_ctl = GDMA_CH0_EN | GDMA_CH1_EN | (dma_conn + 1) << 16 | dma_conn; + writel(dma_ctl, master->dma_ctl_regs); + master->use_dma = true; + dev_info(dev, "Using DMA (req_sel %d)\n", dma_conn); + + /* + * Setup GDMA Channel for TX (Memory to I3C FIFO) + */ + writel(master->dma_tx_addr, master->dma_regs + NPCM_GDMA_SRCB(DMA_CH_TX)); + writel(reg_base + NPCM_I3C_MWDATAB, master->dma_regs + + NPCM_GDMA_DSTB(DMA_CH_TX)); + /* + * Setup GDMA Channel for RX (I3C FIFO to Memory) + */ + writel(reg_base + NPCM_I3C_MRDATAB, master->dma_regs + + NPCM_GDMA_SRCB(DMA_CH_RX)); + writel(master->dma_rx_addr, master->dma_regs + NPCM_GDMA_DSTB(DMA_CH_RX)); + + return 0; +} + +static int npcm_i3c_master_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct npcm_i3c_master *master; + struct reset_control *reset; + u32 val; + int ret; + + master = devm_kzalloc(dev, sizeof(*master), GFP_KERNEL); + if (!master) + return -ENOMEM; + + master->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(master->regs)) + return PTR_ERR(master->regs); + + master->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(master->pclk)) + return PTR_ERR(master->pclk); + + master->fclk = devm_clk_get(dev, "fast_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 = npcm_i3c_master_prepare_clks(master); + if (ret) + return ret; + + reset = devm_reset_control_get(&pdev->dev, NULL);Use dev consistently.quoted
+ if (!IS_ERR(reset)) { + reset_control_assert(reset); + udelay(5); + reset_control_deassert(reset); + } + INIT_WORK(&master->hj_work, npcm_i3c_master_hj_work); + ret = devm_request_irq(dev, master->irq, npcm_i3c_master_irq_handler, + IRQF_NO_SUSPEND, "npcm-i3c-irq", master); + if (ret) + goto err_disable_clks; + + master->free_slots = GENMASK(NPCM_I3C_MAX_DEVS - 1, 0); + + mutex_init(&master->lock); + INIT_LIST_HEAD(&master->xferqueue.list); + + spin_lock_init(&master->req_lock); + spin_lock_init(&master->ibi.lock); + master->ibi.num_slots = NPCM_I3C_MAX_DEVS; + master->ibi.slots = devm_kcalloc(&pdev->dev, master->ibi.num_slots,Once allocation with dev, other with pdev->dev...quoted
+ sizeof(*master->ibi.slots), + GFP_KERNEL); + if (!master->ibi.slots) { + ret = -ENOMEM; + goto err_disable_clks; + } + + platform_set_drvdata(pdev, master); + + npcm_i3c_master_reset(master); + + if (of_property_read_bool(dev->of_node, "enable-hj"))quoted
+ master->en_hj = true; + if (!of_property_read_u32(dev->of_node, "i3c-pp-scl-hi-period-ns", &val)) + master->scl_timing.i3c_pp_hi = val; + + if (!of_property_read_u32(dev->of_node, "i3c-pp-scl-lo-period-ns", &val)) + master->scl_timing.i3c_pp_lo = val; + + if (!of_property_read_u32(dev->of_node, "i3c-pp-sda-rd-skew", &val)) + master->scl_timing.i3c_pp_sda_rd_skew = val; + + if (!of_property_read_u32(dev->of_node, "i3c-pp-sda-wr-skew", &val)) + master->scl_timing.i3c_pp_sda_wr_skew = val; + + if (!of_property_read_u32(dev->of_node, "i3c-od-scl-hi-period-ns", &val)) + master->scl_timing.i3c_od_hi = val; + + if (!of_property_read_u32(dev->of_node, "i3c-od-scl-lo-period-ns", &val)) + master->scl_timing.i3c_od_lo = val; + + npcm_i3c_master_clear_merrwarn(master); + npcm_i3c_master_flush_fifo(master); + + ret = npcm_i3c_setup_dma(pdev, master); + if (ret) + goto err_disable_clks; + + npcm_i3c_init_debugfs(pdev, master); + + /* Register the master */ + ret = i3c_master_register(&master->base, &pdev->dev, + &npcm_i3c_master_ops, false); + if (ret) + goto err_disable_clks; + + if (master->en_hj) { + dev_info(master->dev, "enable hot-join\n");Drop, not useful.quoted
+ npcm_i3c_master_enable_interrupts(master, NPCM_I3C_MINT_SLVSTART); + } + return 0; + + debugfs_remove_recursive(master->debugfs); + +err_disable_clks: + npcm_i3c_master_unprepare_clks(master); + + return ret; +} + +static int npcm_i3c_master_remove(struct platform_device *pdev) +{ + struct npcm_i3c_master *master = platform_get_drvdata(pdev); + + /* Avoid ibi events during driver unbinding */ + writel(NPCM_I3C_MINT_SLVSTART, master->regs + NPCM_I3C_MINTCLR); + + debugfs_remove_recursive(master->debugfs); + + i3c_master_unregister(&master->base); + + if (master->use_dma) { + dma_free_coherent(master->dev, MAX_DMA_COUNT * 4, master->dma_tx_buf, + master->dma_tx_addr); + dma_free_coherent(master->dev, MAX_DMA_COUNT, master->dma_rx_buf, + master->dma_rx_addr); + } + return 0; +} + +static const struct of_device_id npcm_i3c_master_of_match_tbl[] = { + { .compatible = "nuvoton,npcm845-i3c" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, npcm_i3c_master_of_match_tbl); + +static struct platform_driver npcm_i3c_master = { + .probe = npcm_i3c_master_probe, + .remove = npcm_i3c_master_remove, + .driver = { + .name = "npcm845-i3c-master", + .of_match_table = npcm_i3c_master_of_match_tbl, + }, +}; +module_platform_driver(npcm_i3c_master); + +MODULE_AUTHOR("Stanley Chu [off-list ref]"); +MODULE_AUTHOR("James Chiang [off-list ref]"); +MODULE_DESCRIPTION("Nuvoton NPCM845 I3C master driver"); +MODULE_LICENSE("GPL");Best regards, Krzysztof