Re: [PATCH v7 2/2] i2c: npcm: Add Nuvoton NPCM I2C controller driver

From: Brendan Higgins
Date: Tue Feb 25 2020 - 15:01:12 EST


On Thu, Nov 21, 2019 at 11:53:50AM +0200, Tali Perry wrote:
> Add Nuvoton NPCM BMC i2c controller driver.
>
> Signed-off-by: Tali Perry <tali.perry1@xxxxxxxxx>
> ---
> drivers/i2c/busses/Kconfig | 11 +
> drivers/i2c/busses/Makefile | 1 +
> drivers/i2c/busses/i2c-npcm7xx.c | 2485 ++++++++++++++++++++++++++++++
> 3 files changed, 2497 insertions(+)
> create mode 100644 drivers/i2c/busses/i2c-npcm7xx.c
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index 146ce40d8e0a..9091b93aaf90 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -786,6 +786,17 @@ config I2C_NOMADIK
> I2C interface from ST-Ericsson's Nomadik and Ux500 architectures,
> as well as the STA2X11 PCIe I/O HUB.
>
> +config I2C_NPCM7XX
> + tristate "Nuvoton I2C Controller"
> + depends on ARCH_NPCM7XX

This should also depend on "|| COMPILE_TEST"

> + select I2C_SLAVE

Personally, I would prefer if this was optional, but it's up to you.

> + help
> + If you say yes to this option, support will be included for the
> + Nuvoton I2C controller.

Might want to elaborate about the capabilities of the driver/hardware.

> + This driver can also be built as a module. If so, the module
> + will be called i2c-npcm7xx.

Probably unnecessary; this follows the pattern that most other I2C
drivers do.

> config I2C_OCORES
> tristate "OpenCores I2C Controller"
> help
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 3ab8aebc39c9..4af59a806f3c 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -81,6 +81,7 @@ obj-$(CONFIG_I2C_MT7621) += i2c-mt7621.o
> obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
> obj-$(CONFIG_I2C_MXS) += i2c-mxs.o
> obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o
> +obj-$(CONFIG_I2C_NPCM7XX) += i2c-npcm7xx.o
> obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
> obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
> obj-$(CONFIG_I2C_OWL) += i2c-owl.o
> diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c
> new file mode 100644
> index 000000000000..ce9699d56835
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-npcm7xx.c
> @@ -0,0 +1,2485 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Nuvoton NPCM7xx SMB Controller driver
> + *
> + * Copyright (C) 2018 Nuvoton Technologies tali.perry@xxxxxxxxxxx

You should update the date.

> + */
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/errno.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/jiffies.h>
> +
> +#define I2C_VERSION "0.1.0"

nit: You only use this macro in one place. I think it would be clearer
to just use the raw string there.

> +
> +enum smb_mode {
> + SMB_SLAVE = 1,
> + SMB_MASTER
> +};

Please use proper namespacing.

> +/*
> + * External SMB Interface driver xfer indication values, which indicate status
> + * of the bus.
> + */
> +enum smb_state_ind {
> + SMB_NO_STATUS_IND = 0,
> + SMB_SLAVE_RCV_IND = 1,
> + SMB_SLAVE_XMIT_IND = 2,
> + SMB_SLAVE_XMIT_MISSING_DATA_IND = 3,
> + SMB_SLAVE_RESTART_IND = 4,
> + SMB_SLAVE_DONE_IND = 5,
> + SMB_MASTER_DONE_IND = 6,
> + SMB_NACK_IND = 8,
> + SMB_BUS_ERR_IND = 9,
> + SMB_WAKE_UP_IND = 10,
> + SMB_BLOCK_BYTES_ERR_IND = 12,
> + SMB_SLAVE_RCV_MISSING_DATA_IND = 14,
> +};
> +
> +// SMBus Operation type values
> +enum smb_oper {
> + SMB_NO_OPER = 0,
> + SMB_WRITE_OPER = 1,
> + SMB_READ_OPER = 2
> +};
> +
> +// SMBus Bank (FIFO mode)
> +enum smb_bank {
> + SMB_BANK_0 = 0,
> + SMB_BANK_1 = 1
> +};
> +
> +// Internal SMB states values (for the SMB module state machine).
> +enum smb_state {
> + SMB_DISABLE = 0,
> + SMB_IDLE,
> + SMB_MASTER_START,
> + SMB_SLAVE_MATCH,
> + SMB_OPER_STARTED,
> + SMB_STOP_PENDING
> +};
> +
> +// Module supports setting multiple own slave addresses
> +enum smb_addr {
> + SMB_SLAVE_ADDR1 = 0,
> + SMB_SLAVE_ADDR2,
> + SMB_SLAVE_ADDR3,
> + SMB_SLAVE_ADDR4,
> + SMB_SLAVE_ADDR5,
> + SMB_SLAVE_ADDR6,
> + SMB_SLAVE_ADDR7,
> + SMB_SLAVE_ADDR8,
> + SMB_SLAVE_ADDR9,
> + SMB_SLAVE_ADDR10,
> + SMB_GC_ADDR,
> + SMB_ARP_ADDR
> +};
> +
> +// global regs
> +static struct regmap *gcr_regmap;
> +static struct regmap *clk_regmap;

It's generally best to avoid global variables.

> +#define NPCM_I2CSEGCTL 0xE4
> +#define I2CSEGCTL_VAL 0x0333F000

What is this value? It doesn't look like a register offset.

> +// Common regs
> +#define NPCM_SMBSDA 0x000
> +#define NPCM_SMBST 0x002
> +#define NPCM_SMBCST 0x004
> +#define NPCM_SMBCTL1 0x006
> +#define NPCM_SMBADDR1 0x008
> +#define NPCM_SMBCTL2 0x00A
> +#define NPCM_SMBADDR2 0x00C
> +#define NPCM_SMBCTL3 0x00E
> +#define NPCM_SMBCST2 0x018
> +#define NPCM_SMBCST3 0x019
> +#define SMB_VER 0x01F

nit: I am guessing these are not 12 bit values. Can you use just two digits?

> +// BANK 0 regs
> +#define NPCM_SMBADDR3 0x010
> +#define NPCM_SMBADDR7 0x011
> +#define NPCM_SMBADDR4 0x012
> +#define NPCM_SMBADDR8 0x013
> +#define NPCM_SMBADDR5 0x014
> +#define NPCM_SMBADDR9 0x015
> +#define NPCM_SMBADDR6 0x016
> +#define NPCM_SMBADDR10 0x017
> +
> +// SMBADDR array: because the addr regs are sprinkled all over the address space
> +const int NPCM_SMBADDR[10] = {NPCM_SMBADDR1, NPCM_SMBADDR2, NPCM_SMBADDR3,

Please only use all caps for macros.

> + NPCM_SMBADDR4, NPCM_SMBADDR5, NPCM_SMBADDR6,
> + NPCM_SMBADDR7, NPCM_SMBADDR8, NPCM_SMBADDR9,
> + NPCM_SMBADDR10};
> +
> +#define NPCM_SMBCTL4 0x01A
> +#define NPCM_SMBCTL5 0x01B
> +#define NPCM_SMBSCLLT 0x01C // SCL Low Time
> +#define NPCM_SMBFIF_CTL 0x01D // FIFO Control
> +#define NPCM_SMBSCLHT 0x01E // SCL High Time
> +
> +// BANK 1 regs
> +#define NPCM_SMBFIF_CTS 0x010 // Both FIFOs Control and status
> +#define NPCM_SMBTXF_CTL 0x012 // Tx-FIFO Control
> +#define NPCM_SMBT_OUT 0x014 // Bus T.O.
> +#define NPCM_SMBPEC 0x016 // PEC Data
> +#define NPCM_SMBTXF_STS 0x01A // Tx-FIFO Status
> +#define NPCM_SMBRXF_STS 0x01C // Rx-FIFO Status
> +#define NPCM_SMBRXF_CTL 0x01E // Rx-FIFO Control
> +
> +// NPCM_SMBST reg fields
> +#define NPCM_SMBST_XMIT BIT(0)
> +#define NPCM_SMBST_MASTER BIT(1)
> +#define NPCM_SMBST_NMATCH BIT(2)
> +#define NPCM_SMBST_STASTR BIT(3)
> +#define NPCM_SMBST_NEGACK BIT(4)
> +#define NPCM_SMBST_BER BIT(5)
> +#define NPCM_SMBST_SDAST BIT(6)
> +#define NPCM_SMBST_SLVSTP BIT(7)
> +
> +// NPCM_SMBCST reg fields
> +#define NPCM_SMBCST_BUSY BIT(0)
> +#define NPCM_SMBCST_BB BIT(1)
> +#define NPCM_SMBCST_MATCH BIT(2)
> +#define NPCM_SMBCST_GCMATCH BIT(3)
> +#define NPCM_SMBCST_TSDA BIT(4)
> +#define NPCM_SMBCST_TGSCL BIT(5)
> +#define NPCM_SMBCST_MATCHAF BIT(6)
> +#define NPCM_SMBCST_ARPMATCH BIT(7)
> +
> +// NPCM_SMBCTL1 reg fields
> +#define NPCM_SMBCTL1_START BIT(0)
> +#define NPCM_SMBCTL1_STOP BIT(1)
> +#define NPCM_SMBCTL1_INTEN BIT(2)
> +#define NPCM_SMBCTL1_EOBINTE BIT(3)
> +#define NPCM_SMBCTL1_ACK BIT(4)
> +#define NPCM_SMBCTL1_GCMEN BIT(5)
> +#define NPCM_SMBCTL1_NMINTE BIT(6)
> +#define NPCM_SMBCTL1_STASTRE BIT(7)
> +
> +// RW1S fields (inside a RW reg):
> +#define NPCM_SMBCTL1_RWS_FIELDS (NPCM_SMBCTL1_START | NPCM_SMBCTL1_STOP | \
> + NPCM_SMBCTL1_ACK)
> +// NPCM_SMBADDR reg fields
> +#define NPCM_SMBADDR_A GENMASK(6, 0)
> +#define NPCM_SMBADDR_SAEN BIT(7)
> +
> +// NPCM_SMBCTL2 reg fields
> +#define SMBCTL2_ENABLE BIT(0)
> +#define SMBCTL2_SCLFRQ6_0 GENMASK(7, 1)
> +
> +// NPCM_SMBCTL3 reg fields
> +#define SMBCTL3_SCLFRQ8_7 GENMASK(1, 0)
> +#define SMBCTL3_ARPMEN BIT(2)
> +#define SMBCTL3_IDL_START BIT(3)
> +#define SMBCTL3_400K_MODE BIT(4)
> +#define SMBCTL3_BNK_SEL BIT(5)
> +#define SMBCTL3_SDA_LVL BIT(6)
> +#define SMBCTL3_SCL_LVL BIT(7)
> +
> +// NPCM_SMBCST2 reg fields
> +#define NPCM_SMBCST2_MATCHA1F BIT(0)
> +#define NPCM_SMBCST2_MATCHA2F BIT(1)
> +#define NPCM_SMBCST2_MATCHA3F BIT(2)
> +#define NPCM_SMBCST2_MATCHA4F BIT(3)
> +#define NPCM_SMBCST2_MATCHA5F BIT(4)
> +#define NPCM_SMBCST2_MATCHA6F BIT(5)
> +#define NPCM_SMBCST2_MATCHA7F BIT(5)
> +#define NPCM_SMBCST2_INTSTS BIT(7)
> +
> +// NPCM_SMBCST3 reg fields
> +#define NPCM_SMBCST3_MATCHA8F BIT(0)
> +#define NPCM_SMBCST3_MATCHA9F BIT(1)
> +#define NPCM_SMBCST3_MATCHA10F BIT(2)
> +#define NPCM_SMBCST3_EO_BUSY BIT(7)
> +
> +// NPCM_SMBCTL4 reg fields
> +#define SMBCTL4_HLDT GENMASK(5, 0)
> +#define SMBCTL4_LVL_WE BIT(7)
> +
> +// NPCM_SMBCTL5 reg fields
> +#define SMBCTL5_DBNCT GENMASK(3, 0)
> +
> +// NPCM_SMBFIF_CTS reg fields
> +#define NPCM_SMBFIF_CTS_RXF_TXE BIT(1)
> +#define NPCM_SMBFIF_CTS_RFTE_IE BIT(3)
> +#define NPCM_SMBFIF_CTS_CLR_FIFO BIT(6)
> +#define NPCM_SMBFIF_CTS_SLVRSTR BIT(7)
> +
> +// NPCM_SMBTXF_CTL reg fields
> +#define NPCM_SMBTXF_CTL_TX_THR GENMASK(4, 0)
> +#define NPCM_SMBTXF_CTL_THR_TXIE BIT(6)
> +
> +// NPCM_SMBT_OUT reg fields
> +#define NPCM_SMBT_OUT_TO_CKDIV GENMASK(5, 0)
> +#define NPCM_SMBT_OUT_T_OUTIE BIT(6)
> +#define NPCM_SMBT_OUT_T_OUTST BIT(7)
> +
> +// NPCM_SMBTXF_STS reg fields
> +#define NPCM_SMBTXF_STS_TX_BYTES GENMASK(4, 0)
> +#define NPCM_SMBTXF_STS_TX_THST BIT(6)
> +
> +// NPCM_SMBRXF_STS reg fields
> +#define NPCM_SMBRXF_STS_RX_BYTES GENMASK(4, 0)
> +#define NPCM_SMBRXF_STS_RX_THST BIT(6)
> +
> +// NPCM_SMBFIF_CTL reg fields
> +#define NPCM_SMBFIF_CTL_FIFO_EN BIT(4)
> +
> +// NPCM_SMBRXF_CTL reg fields
> +#define NPCM_SMBRXF_CTL_RX_THR GENMASK(4, 0)
> +#define NPCM_SMBRXF_CTL_LAST_PEC BIT(5)
> +#define NPCM_SMBRXF_CTL_THR_RXIE BIT(6)
> +
> +#define SMBUS_FIFO_SIZE 16
> +
> +// SMB_VER reg fields
> +#define SMB_VER_VERSION GENMASK(6, 0)
> +#define SMB_VER_FIFO_EN BIT(7)
> +
> +// stall/stuck timeout
> +const unsigned int DEFAULT_STALL_COUNT = 25;
> +
> +// retries in a loop for master abort
> +const unsigned int RETRIES_NUM = 10000;
> +
> +// SMBus spec. values in KHZ
> +const unsigned int SMBUS_FREQ_MIN = 10;
> +const unsigned int SMBUS_FREQ_MAX = 1000;
> +const unsigned int SMBUS_FREQ_100KHZ = 100;
> +const unsigned int SMBUS_FREQ_400KHZ = 400;
> +const unsigned int SMBUS_FREQ_1MHZ = 1000;

Does the hardware only support these clock speeds?

> +// SCLFRQ min/max field values
> +const unsigned int SCLFRQ_MIN = 10;
> +const unsigned int SCLFRQ_MAX = 511;
> +
> +// SCLFRQ field position
> +#define SCLFRQ_0_TO_6 GENMASK(6, 0)
> +#define SCLFRQ_7_TO_8 GENMASK(8, 7)
> +
> +const unsigned int SMB_NUM_OF_ADDR = 10;
> +
> +#define NPCM_I2C_EVENT_START BIT(0)
> +#define NPCM_I2C_EVENT_STOP BIT(1)
> +#define NPCM_I2C_EVENT_ABORT BIT(2)
> +#define NPCM_I2C_EVENT_WRITE BIT(3)
> +
> +#define NPCM_I2C_EVENT_READ BIT(4)
> +#define NPCM_I2C_EVENT_BER BIT(5)
> +#define NPCM_I2C_EVENT_NACK BIT(6)
> +#define NPCM_I2C_EVENT_TO BIT(7)
> +
> +#define NPCM_I2C_EVENT_EOB BIT(8)
> +#define NPCM_I2C_EVENT_STALL BIT(9)
> +#define NPCM_I2C_EVENT_CB BIT(10)
> +#define NPCM_I2C_EVENT_DONE BIT(11)
> +
> +#define NPCM_I2C_EVENT_READ1 BIT(12)
> +#define NPCM_I2C_EVENT_READ2 BIT(13)
> +#define NPCM_I2C_EVENT_READ3 BIT(14)
> +#define NPCM_I2C_EVENT_READ4 BIT(15)
> +
> +#define NPCM_I2C_EVENT_NMATCH_SLV BIT(16)
> +#define NPCM_I2C_EVENT_NMATCH_MSTR BIT(17)
> +#define NPCM_I2C_EVENT_BER_SLV BIT(18)
> +
> +#define NPCM_I2C_EVENT_LOG(event) (bus->event_log |= event)
> +
> +// Status of one SMBus module
> +struct npcm_i2c {
> + struct i2c_adapter adap;
> + struct device *dev;
> + unsigned char __iomem *reg;
> + spinlock_t lock; /* IRQ synchronization */
> + struct completion cmd_complete;
> + int irq;
> + int cmd_err;
> + struct i2c_msg *msgs;
> + int msgs_num;
> + int num;
> + u32 apb_clk;
> + struct i2c_bus_recovery_info rinfo;
> + enum smb_state state;
> + enum smb_oper operation;
> + enum smb_mode master_or_slave;
> + enum smb_state_ind stop_ind;
> + u8 dest_addr;
> + u8 *rd_buf;
> + u16 rd_size;
> + u16 rd_ind;
> + u8 *wr_buf;
> + u16 wr_size;
> + u16 wr_ind;
> + bool fifo_use;
> +
> + // PEC bit mask per slave address.
> + // 1: use PEC for this address,
> + // 0: do not use PEC for this address
> + u16 PEC_mask;
> + bool PEC_use;
> + bool read_block_use;
> + u8 int_cnt;
> + u32 event_log;
> + u32 event_log_prev;
> + u32 clk_period_us;
> + unsigned long int_time_stamp;
> + unsigned long bus_freq; // in kHz
> + u32 xmits;
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)

Aha! It appears that selecting I2C_SLAVE is unnecessary.

Since you already ifdeffed out all the I2C slave code, can you put it in
a separate patch to make this easier to review?

> + u8 own_slave_addr;
> + struct i2c_client *slave;
> +
> + // currently I2C slave IF only supports single byte operations.
> + // in order to utilyze the npcm HW FIFO, the driver will ask for 16bytes
> + // at a time, pack them in buffer, and then transmit them all together
> + // to the FIFO and onward to the bus .
> + // NACK on read will be once reached to bus->adap->quirks->max_read_len
> + // sending a NACK whever the backend requests for it is not supported.
> +
> + // This module can be master and slave at the same time. separate ptrs
> + // and counters:
> + int slv_rd_size;
> + int slv_rd_ind;
> + int slv_wr_size;
> + int slv_wr_ind;
> + u8 slv_rd_buf[SMBUS_FIFO_SIZE];
> + u8 slv_wr_buf[SMBUS_FIFO_SIZE];
> +#endif
> +};

Cheers!