[PATCH v8 2/3] i2c: npcm7xx: Add Nuvoton NPCM I2C controller driver

Andy Shevchenko andriy.shevchenko at linux.intel.com
Mon Mar 2 21:50:53 AEDT 2020


On Mon, Mar 02, 2020 at 12:32:00AM +0200, Tali Perry wrote:
> Add Nuvoton NPCM BMC I2C controller driver.

> +#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>

...

> +enum i2c_mode {
> +	I2C_MASTER,

> +	I2C_SLAVE

If it's not a terminator line (like MAX or something like that) it's better to
have comma at the end. This applies to all enum:s in your code.

> +};

...

> +// I2Cus Operation type values

It's funny style of comments when you have different for multi-line ones. I
don't know Wolfram's opinion on that, but at least for me looks like a bit of
consistency should be applied.

Also in some of them you missed English grammar / punctuation.

> +enum i2c_oper {
> +	I2C_NO_OPER = 0,
> +	I2C_WRITE_OPER = 1,
> +	I2C_READ_OPER = 2

If it's not hardware related values, why we need to define them explicitly?

> +};

...

> +#define NPCM_I2CCTL1_RWS_FIELDS	  (NPCM_I2CCTL1_START | NPCM_I2CCTL1_STOP | \
> +				   NPCM_I2CCTL1_ACK)

Better do like

#define FOO			\
	(BAR | BAZ)

...

> +const unsigned int RETRIES_NUM =	10000;

> +const unsigned int I2C_FREQ_MIN = 10;
> +const unsigned int I2C_FREQ_MAX = 1000;
> +const unsigned int I2C_FREQ_100KHZ = 100;
> +const unsigned int I2C_FREQ_400KHZ = 400;
> +const unsigned int I2C_FREQ_1MHZ = 1000;

> +const unsigned int SCLFRQ_MIN = 10;
> +const unsigned int SCLFRQ_MAX = 511;

> +const unsigned int I2C_NUM_OF_ADDR = 10;

Hmm... Why they are not defines?

...

> +// Status of one I2C module
> +struct npcm_i2c {
> +	struct i2c_adapter	adap;
> +	struct device		*dev;
> +	unsigned char __iomem	*reg;

> +	spinlock_t		lock;   /* IRQ synchronization */

Perhaps you describe all of the fields in kernel-doc format?

> +	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 i2c_state		state;
> +	enum i2c_oper		operation;
> +	enum i2c_mode		master_or_slave;
> +	enum i2c_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

Ditto.

> +	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;
> +
> +};

...

> +static inline void npcm_i2c_select_bank(struct npcm_i2c *bus,
> +					enum i2c_bank bank)
> +{
> +	if (bank == I2C_BANK_0)
> +		iowrite8(ioread8(bus->reg + NPCM_I2CCTL3) & ~I2CCTL3_BNK_SEL,
> +			 bus->reg + NPCM_I2CCTL3);
> +	else
> +		iowrite8(ioread8(bus->reg + NPCM_I2CCTL3) | I2CCTL3_BNK_SEL,
> +			 bus->reg + NPCM_I2CCTL3);

Usual patter (better to read) is

	u8 value;

	value = ioread8(...);
	if (a)
		val ...;
	else
		val ...;
	iowrite8(val, ...);

> +}

...

> +static inline void npcm_i2c_rd_byte(struct npcm_i2c *bus, u8 *data)
> +{
> +	*data = ioread8(bus->reg + NPCM_I2CSDA);
> +}

Hmm... why not u8 as return type?

...

> +static inline u16 npcm_i2c_get_index(struct npcm_i2c *bus)
> +{
> +	u16 index = 0;
> +
> +	if (bus->operation == I2C_READ_OPER)
> +		index = bus->rd_ind;
> +	else if (bus->operation == I2C_WRITE_OPER)
> +		index = bus->wr_ind;
> +
> +	return index;

Why do you need temporary variable?

	if (a)
		return X;
	if (b)
		return Y;
	return 0;

> +}

...

> +static inline bool npcm_i2c_is_quick(struct npcm_i2c *bus)
> +{
> +	if (bus->wr_size == 0 && bus->rd_size == 0)
> +		return true;
> +	return false;

	return bus->wr_size == 0 && bus->rd_size == 0;

> +}

...

> +static void npcm_i2c_disable(struct npcm_i2c *bus)
> +{
> +	int i;
> +
> +	// select bank 0 for I2C addresses
> +	npcm_i2c_select_bank(bus, I2C_BANK_0);
> +
> +	// Slave addresses removal
> +	for (i = I2C_SLAVE_ADDR1; i < I2C_NUM_OF_ADDR; i++)
> +		iowrite8(0, bus->reg + npcm_i2caddr[i]);
> +
> +	npcm_i2c_select_bank(bus, I2C_BANK_1);
> +

> +	// Disable module.
> +	iowrite8(ioread8(bus->reg + NPCM_I2CCTL2) & ~I2CCTL2_ENABLE,
> +		 bus->reg + NPCM_I2CCTL2);


Usual pattern

	value = ioread8(...);
	value ...;
	iowrite(value, ...);

> +
> +	bus->state = I2C_DISABLE;
> +}

...

> +
> +static void npcm_i2c_enable(struct npcm_i2c *bus)
> +{
> +	iowrite8((ioread8(bus->reg + NPCM_I2CCTL2) | I2CCTL2_ENABLE),
> +		 bus->reg + NPCM_I2CCTL2);

Ditto. Applies to all your code.

> +
> +	bus->state = I2C_IDLE;
> +}

...

> +static bool npcm_i2c_wait_for_bus_free(struct npcm_i2c *bus, bool may_sleep)
> +{
> +	int cnt = 0;
> +	int max_count = 2; /* wait for 2 ms */
> +
> +	if (may_sleep)
> +		might_sleep();
> +	else
> +		max_count = max_count * 100; /* since each delay is 10 us */

> +	while  (ioread8(bus->reg + NPCM_I2CCST) & NPCM_I2CCST_BUSY) {
> +		if (cnt < max_count) {
> +			if (may_sleep)
> +				msleep_interruptible(1);
> +			else
> +				udelay(10);
> +			cnt++;
> +
> +		} else {
> +			bus->cmd_err = -EAGAIN;
> +			return false;
> +		}
> +	}

NIH of readx_poll_timeout{_atomic}().

> +	return true;
> +}

...

> +static inline void npcm_i2c_eob_int(struct npcm_i2c *bus, bool enable)
> +{
> +	// Clear EO_BUSY pending bit:
> +	iowrite8(ioread8(bus->reg + NPCM_I2CCST3) | NPCM_I2CCST3_EO_BUSY,
> +		 bus->reg + NPCM_I2CCST3);
> +
> +	if (enable) {
> +		iowrite8((ioread8(bus->reg + NPCM_I2CCTL1) |
> +			 NPCM_I2CCTL1_EOBINTE)  & ~NPCM_I2CCTL1_RWS_FIELDS,
> +			 bus->reg + NPCM_I2CCTL1);
> +	} else {
> +		iowrite8(ioread8(bus->reg + NPCM_I2CCTL1) &
> +			 ~NPCM_I2CCTL1_EOBINTE & ~NPCM_I2CCTL1_RWS_FIELDS,
> +			 bus->reg + NPCM_I2CCTL1);
> +	}

Hard to follow. See above comments.

> +}

...

> +	return (bool)FIELD_GET(NPCM_I2CTXF_STS_TX_THST, tx_fifo_sts);

!! will do better than explicit casting.
Ditto for the rest.

...

> +static int  npcm_i2c_slave_enable_l(struct npcm_i2c *bus,
> +				    enum i2c_addr addr_type, u8 addr,
> +				    bool enable);

Why is it here? Do you have recursion / circular dependency?

...

> +	if (!msgs)
> +		return;

Is it possible?

> +	if (completion_done(&bus->cmd_complete) == true)

' == true' is redundant. Same for ' == false' (use ! instead).

> +		return;

...

> +static void npcm_i2c_write_to_fifo_master(struct npcm_i2c *bus,
> +					  u16 max_bytes_to_send)
> +{
> +	// Fill the FIFO, while the FIFO is not full and there are more bytes to
> +	// write

> +	if (max_bytes_to_send == 0)
> +		return;

Duplicate check, thus redundant.

> +	while ((max_bytes_to_send--) && (I2C_HW_FIFO_SIZE -
> +					 npcm_i2c_get_fifo_fullness(bus))) {

Badly formatted line. Moreover, too many parentheses.

> +		if (bus->wr_ind < bus->wr_size)
> +			npcm_i2c_wr_byte(bus, bus->wr_buf[bus->wr_ind++]);
> +		else
> +			npcm_i2c_wr_byte(bus, 0xFF);
> +	}
> +}

...

> +		rxf_ctl = min_t(u16, (u16)nread, (u16)I2C_HW_FIFO_SIZE);

Explicit casting when use min_t()? It's strange. Have you chance to read what
min_t() does?

...

> +
> +static int npcm_i2c_master_abort(struct npcm_i2c *bus)
> +{

> +	int ret = 0;

I don't see why this variable is needed.

> +
> +	NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_ABORT);
> +
> +	// Only current master is allowed to issue Stop Condition
> +	if (npcm_i2c_is_master(bus)) {
> +		npcm_i2c_eob_int(bus, true);
> +		npcm_i2c_master_stop(bus);
> +
> +		// Clear NEGACK, STASTR and BER bits
> +		iowrite8(NPCM_I2CST_BER | NPCM_I2CST_NEGACK | NPCM_I2CST_STASTR,
> +			 bus->reg + NPCM_I2CST);
> +	}
> +
> +	return ret;
> +}

...

> +	pr_debug("i2c%d get SCL 0x%08X\n", bus->num, ret);

Do you need this in production code?

> +	return (ret >> (offset)) & 0x01;

Too many parentheses, but why not simple
	return !!(ret & BIT(offset));
?

Same for the rest similar code.

...

> +static int npcm_i2c_get_SDA(struct i2c_adapter *_adap)
> +{
> +	unsigned int ret = 0;
> +	struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);

> +	u32 offset = 0;
> +
> +	offset = 0;

What the point?

> +	ret = FIELD_GET(I2CCTL3_SDA_LVL, ioread32(bus->reg + NPCM_I2CCTL3));
> +
> +	pr_debug("i2c%d get SDA 0x%08X\n", bus->num, ret);
> +
> +	return (ret >> (offset)) & 0x01;
> +}

I (almost) stopped here, I thing this driver needs more work (style,
refactoring, etc) before real review.

...

> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	dev_dbg(bus->dev, "resource: %pR\n", res);
> +	bus->reg = devm_ioremap_resource(&pdev->dev, res);

devm_platform_ioremap_resource();

> +	if (IS_ERR((bus)->reg))
> +		return PTR_ERR((bus)->reg);

...

> +	bus->irq = platform_get_irq(pdev, 0);
> +	if (bus->irq < 0) {

> +		dev_err(bus->dev, "I2C platform_get_irq error\n");

Redundant.

> +		return -ENODEV;
> +	}

...

> +	dev_dbg(bus->dev, "irq = %d\n", bus->irq);

Why?! There are other means to get this information.

> +	ret = devm_request_irq(&pdev->dev, bus->irq, npcm_i2c_bus_irq, 0,
> +			       dev_name(&pdev->dev), (void *)bus);

Explicit casting?!

...

> +	pr_info("npcm7xx I2C bus %d is registered\n", bus->adap.nr);

Noise.

...

> +static const struct of_device_id npcm_i2c_bus_of_table[] = {
> +	{ .compatible = "nuvoton,npcm750-i2c", },

> +	{},

For terminator line comma is redundant.

> +};

...

> +MODULE_VERSION("0.1.1");

What the point?

-- 
With Best Regards,
Andy Shevchenko




More information about the openbmc mailing list