Linux-i2c Archive on lore.kernel.org
 help / color / Atom feed
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
To: Yicong Yang <yangyicong@hisilicon.com>
Cc: wsa@kernel.org, linux-i2c@vger.kernel.org, digetx@gmail.com,
	treding@nvidia.com, jarkko.nikula@linux.intel.com,
	rmk+kernel@armlinux.org.uk, song.bao.hua@hisilicon.com,
	john.garry@huawei.com, prime.zeng@huawei.com,
	linuxarm@huawei.com
Subject: Re: [PATCH v4 3/4] i2c: add support for HiSilicon I2C controller
Date: Tue, 30 Mar 2021 16:29:25 +0300
Message-ID: <YGMntYT2iz72wgrd@smile.fi.intel.com> (raw)
In-Reply-To: <1617109549-4013-4-git-send-email-yangyicong@hisilicon.com>

On Tue, Mar 30, 2021 at 09:05:48PM +0800, Yicong Yang wrote:
> Add HiSilicon I2C controller driver for the Kunpeng SoC. It provides
> the access to the i2c busses, which connects to the eeprom, rtc, etc.
> 
> The driver works with IRQ mode, and supports basic I2C features and 10bit
> address. The DMA is not supported.

FWIW,
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>

Some nit-picks below.

> Signed-off-by: Yicong Yang <yangyicong@hisilicon.com>
> ---
>  drivers/i2c/busses/Kconfig    |  10 +
>  drivers/i2c/busses/Makefile   |   1 +
>  drivers/i2c/busses/i2c-hisi.c | 510 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 521 insertions(+)
>  create mode 100644 drivers/i2c/busses/i2c-hisi.c
> 
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index 05ebf75..f6b8b71 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -645,6 +645,16 @@ config I2C_HIGHLANDER
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called i2c-highlander.
>  
> +config I2C_HISI
> +	tristate "HiSilicon I2C controller"

> +	depends on (ARM64 && ACPI) || COMPILE_TEST

ACPI can be safely dropped. There is no direct dependency on it.

> +	help
> +	  Say Y here if you want to have Hisilicon I2C controller support
> +	  available on the Kunpeng Server.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called i2c-hisi.
> +
>  config I2C_IBM_IIC
>  	tristate "IBM PPC 4xx on-chip I2C interface"
>  	depends on 4xx
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 615f35e..e1c9292 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -63,6 +63,7 @@ obj-$(CONFIG_I2C_EMEV2)		+= i2c-emev2.o
>  obj-$(CONFIG_I2C_EXYNOS5)	+= i2c-exynos5.o
>  obj-$(CONFIG_I2C_GPIO)		+= i2c-gpio.o
>  obj-$(CONFIG_I2C_HIGHLANDER)	+= i2c-highlander.o
> +obj-$(CONFIG_I2C_HISI)		+= i2c-hisi.o
>  obj-$(CONFIG_I2C_HIX5HD2)	+= i2c-hix5hd2.o
>  obj-$(CONFIG_I2C_IBM_IIC)	+= i2c-ibm_iic.o
>  obj-$(CONFIG_I2C_IMG)		+= i2c-img-scb.o
> diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c
> new file mode 100644
> index 0000000..8bd6433
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-hisi.c
> @@ -0,0 +1,510 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * HiSilicon I2C Controller Driver for Kunpeng SoC
> + *
> + * Copyright (c) 2021 HiSilicon Limited.
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/bitfield.h>
> +#include <linux/completion.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/platform_device.h>
> +#include <linux/property.h>
> +
> +#define HISI_I2C_FRAME_CTRL		0x0000
> +#define   HISI_I2C_FRAME_CTRL_SPEED_MODE	GENMASK(1, 0)
> +#define   HISI_I2C_FRAME_CTRL_ADDR_TEN	BIT(2)
> +#define HISI_I2C_SLV_ADDR		0x0004
> +#define   HISI_I2C_SLV_ADDR_VAL		GENMASK(9, 0)
> +#define   HISI_I2C_SLV_ADDR_GC_S_MODE	BIT(10)
> +#define   HISI_I2C_SLV_ADDR_GC_S_EN	BIT(11)
> +#define HISI_I2C_CMD_TXDATA		0x0008
> +#define   HISI_I2C_CMD_TXDATA_DATA	GENMASK(7, 0)
> +#define   HISI_I2C_CMD_TXDATA_RW	BIT(8)
> +#define   HISI_I2C_CMD_TXDATA_P_EN	BIT(9)
> +#define   HISI_I2C_CMD_TXDATA_SR_EN	BIT(10)
> +#define HISI_I2C_RXDATA			0x000c
> +#define   HISI_I2C_RXDATA_DATA		GENMASK(7, 0)
> +#define HISI_I2C_SS_SCL_HCNT		0x0010
> +#define HISI_I2C_SS_SCL_LCNT		0x0014
> +#define HISI_I2C_FS_SCL_HCNT		0x0018
> +#define HISI_I2C_FS_SCL_LCNT		0x001c
> +#define HISI_I2C_HS_SCL_HCNT		0x0020
> +#define HISI_I2C_HS_SCL_LCNT		0x0024
> +#define HISI_I2C_FIFO_CTRL		0x0028
> +#define   HISI_I2C_FIFO_RX_CLR		BIT(0)
> +#define   HISI_I2C_FIFO_TX_CLR		BIT(1)
> +#define   HISI_I2C_FIFO_RX_AF_THRESH	GENMASK(7, 2)
> +#define   HISI_I2C_FIFO_TX_AE_THRESH	GENMASK(13, 8)
> +#define HISI_I2C_FIFO_STATE		0x002c
> +#define   HISI_I2C_FIFO_STATE_RX_RERR	BIT(0)
> +#define   HISI_I2C_FIFO_STATE_RX_WERR	BIT(1)
> +#define   HISI_I2C_FIFO_STATE_RX_EMPTY	BIT(3)
> +#define   HISI_I2C_FIFO_STATE_TX_RERR	BIT(6)
> +#define   HISI_I2C_FIFO_STATE_TX_WERR	BIT(7)
> +#define   HISI_I2C_FIFO_STATE_TX_FULL	BIT(11)
> +#define HISI_I2C_SDA_HOLD		0x0030
> +#define   HISI_I2C_SDA_HOLD_TX		GENMASK(15, 0)
> +#define   HISI_I2C_SDA_HOLD_RX		GENMASK(23, 16)
> +#define HISI_I2C_FS_SPK_LEN		0x0038
> +#define   HISI_I2C_FS_SPK_LEN_CNT	GENMASK(7, 0)
> +#define HISI_I2C_HS_SPK_LEN		0x003c
> +#define   HISI_I2C_HS_SPK_LEN_CNT	GENMASK(7, 0)
> +#define HISI_I2C_INT_MSTAT		0x0044
> +#define HISI_I2C_INT_CLR		0x0048
> +#define HISI_I2C_INT_MASK		0x004C
> +#define HISI_I2C_TRANS_STATE		0x0050
> +#define HISI_I2C_TRANS_ERR		0x0054
> +#define HISI_I2C_VERSION		0x0058
> +
> +#define HISI_I2C_INT_ALL	GENMASK(4, 0)
> +#define HISI_I2C_INT_TRANS_CPLT	BIT(0)
> +#define HISI_I2C_INT_TRANS_ERR	BIT(1)
> +#define HISI_I2C_INT_FIFO_ERR	BIT(2)
> +#define HISI_I2C_INT_RX_FULL	BIT(3)
> +#define HISI_I2C_INT_TX_EMPTY	BIT(4)
> +#define HISI_I2C_INT_ERR \
> +	(HISI_I2C_INT_TRANS_ERR | HISI_I2C_INT_FIFO_ERR)
> +
> +#define HISI_I2C_STD_SPEED_MODE		0
> +#define HISI_I2C_FAST_SPEED_MODE	1
> +#define HISI_I2C_HIGH_SPEED_MODE	2
> +
> +#define HISI_I2C_TX_FIFO_DEPTH		64
> +#define HISI_I2C_RX_FIFO_DEPTH		64
> +#define HISI_I2C_TX_F_AE_THRESH		1
> +#define HISI_I2C_RX_F_AF_THRESH		60
> +
> +#define HZ_PER_KHZ	1000
> +
> +#define NSEC_TO_CYCLES(ns, clk_rate_khz) \
> +	DIV_ROUND_UP_ULL((clk_rate_khz) * (ns), NSEC_PER_MSEC)
> +
> +struct hisi_i2c_controller {
> +	struct i2c_adapter adapter;
> +	void __iomem *iobase;
> +	struct device *dev;
> +	int irq;
> +
> +	/* Intermediates for recording the transfer process */
> +	struct completion *completion;
> +	struct i2c_msg *msgs;
> +	int msg_num;
> +	int msg_tx_idx;
> +	int buf_tx_idx;
> +	int msg_rx_idx;
> +	int buf_rx_idx;
> +	u16 tar_addr;
> +	u32 xfer_err;
> +
> +	/* I2C bus configuration */
> +	struct i2c_timings t;
> +	u64 clk_rate_khz;
> +	u32 spk_len;
> +};
> +
> +static void hisi_i2c_enable_int(struct hisi_i2c_controller *ctlr, u32 mask)
> +{
> +	writel_relaxed(mask, ctlr->iobase + HISI_I2C_INT_MASK);
> +}
> +
> +static void hisi_i2c_disable_int(struct hisi_i2c_controller *ctlr, u32 mask)
> +{
> +	writel_relaxed((~mask) & HISI_I2C_INT_ALL, ctlr->iobase + HISI_I2C_INT_MASK);
> +}
> +
> +static void hisi_i2c_clear_int(struct hisi_i2c_controller *ctlr, u32 mask)
> +{
> +	writel_relaxed(mask, ctlr->iobase + HISI_I2C_INT_CLR);
> +}
> +
> +static void hisi_i2c_handle_errors(struct hisi_i2c_controller *ctlr)
> +{
> +	u32 int_err = ctlr->xfer_err, reg;
> +
> +	if (int_err & HISI_I2C_INT_FIFO_ERR) {
> +		reg = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
> +
> +		if (reg & HISI_I2C_FIFO_STATE_RX_RERR)
> +			dev_err(ctlr->dev, "rx fifo error read\n");
> +
> +		if (reg & HISI_I2C_FIFO_STATE_RX_WERR)
> +			dev_err(ctlr->dev, "rx fifo error write\n");
> +
> +		if (reg & HISI_I2C_FIFO_STATE_TX_RERR)
> +			dev_err(ctlr->dev, "tx fifo error read\n");
> +
> +		if (reg & HISI_I2C_FIFO_STATE_TX_WERR)
> +			dev_err(ctlr->dev, "tx fifo error write\n");
> +	}
> +}
> +
> +static int hisi_i2c_start_xfer(struct hisi_i2c_controller *ctlr)
> +{
> +	struct i2c_msg *msg = ctlr->msgs;
> +	u32 reg;
> +
> +	reg = readl(ctlr->iobase + HISI_I2C_FRAME_CTRL);
> +	reg &= ~HISI_I2C_FRAME_CTRL_ADDR_TEN;
> +	if (msg->flags & I2C_M_TEN)
> +		reg |= HISI_I2C_FRAME_CTRL_ADDR_TEN;
> +	writel(reg, ctlr->iobase + HISI_I2C_FRAME_CTRL);
> +
> +	reg = readl(ctlr->iobase + HISI_I2C_SLV_ADDR);
> +	reg &= ~HISI_I2C_SLV_ADDR_VAL;
> +	reg |= FIELD_PREP(HISI_I2C_SLV_ADDR_VAL, msg->addr);
> +	writel(reg, ctlr->iobase + HISI_I2C_SLV_ADDR);
> +
> +	reg = readl(ctlr->iobase + HISI_I2C_FIFO_CTRL);
> +	reg |= HISI_I2C_FIFO_RX_CLR | HISI_I2C_FIFO_TX_CLR;
> +	writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL);
> +	reg &= ~(HISI_I2C_FIFO_RX_CLR | HISI_I2C_FIFO_TX_CLR);
> +	writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL);
> +
> +	hisi_i2c_clear_int(ctlr, HISI_I2C_INT_ALL);
> +	hisi_i2c_enable_int(ctlr, HISI_I2C_INT_ALL);
> +
> +	return 0;
> +}
> +
> +static void hisi_i2c_reset_xfer(struct hisi_i2c_controller *ctlr)
> +{
> +	ctlr->msg_num = 0;
> +	ctlr->xfer_err = 0;
> +	ctlr->msg_tx_idx = 0;
> +	ctlr->msg_rx_idx = 0;
> +	ctlr->buf_tx_idx = 0;
> +	ctlr->buf_rx_idx = 0;
> +}
> +
> +/*
> + * Initialize the transfer information and start the I2C bus transfer.
> + * We only configure the transfer and do some pre/post works here, and
> + * wait for the transfer done. The major transfer process is performed
> + * in the IRQ handler.
> + */
> +static int hisi_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
> +				int num)
> +{
> +	struct hisi_i2c_controller *ctlr = i2c_get_adapdata(adap);
> +	DECLARE_COMPLETION_ONSTACK(done);
> +	int ret = num;
> +
> +	hisi_i2c_reset_xfer(ctlr);
> +	ctlr->completion = &done;
> +	ctlr->msg_num = num;
> +	ctlr->msgs = msgs;
> +
> +	hisi_i2c_start_xfer(ctlr);
> +
> +	if (!wait_for_completion_timeout(ctlr->completion, adap->timeout)) {
> +		hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL);
> +		synchronize_irq(ctlr->irq);
> +		i2c_recover_bus(&ctlr->adapter);
> +		dev_err(ctlr->dev, "bus transfer timeout\n");
> +		ret = -EIO;
> +	}
> +
> +	if (ctlr->xfer_err) {
> +		hisi_i2c_handle_errors(ctlr);
> +		ret = -EIO;
> +	}
> +
> +	hisi_i2c_reset_xfer(ctlr);
> +	ctlr->completion = NULL;
> +
> +	return ret;
> +}
> +
> +static u32 hisi_i2c_functionality(struct i2c_adapter *adap)
> +{
> +	return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm hisi_i2c_algo = {
> +	.master_xfer	= hisi_i2c_master_xfer,
> +	.functionality	= hisi_i2c_functionality,
> +};
> +
> +static int hisi_i2c_read_rx_fifo(struct hisi_i2c_controller *ctlr)
> +{
> +	struct i2c_msg *cur_msg;
> +	u32 fifo_state;
> +
> +	while (ctlr->msg_rx_idx < ctlr->msg_num) {
> +		cur_msg = ctlr->msgs + ctlr->msg_rx_idx;
> +
> +		if (!(cur_msg->flags & I2C_M_RD)) {
> +			ctlr->msg_rx_idx++;
> +			continue;
> +		}
> +
> +		fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
> +		while (!(fifo_state & HISI_I2C_FIFO_STATE_RX_EMPTY) &&
> +		       ctlr->buf_rx_idx < cur_msg->len) {

I would put c under f, but it's up to you an maintainer (also applicable to
other sites where similar conditionals are in use).

> +			cur_msg->buf[ctlr->buf_rx_idx++] = readl(ctlr->iobase + HISI_I2C_RXDATA);
> +			fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
> +		}
> +
> +		if (ctlr->buf_rx_idx == cur_msg->len) {
> +			ctlr->buf_rx_idx = 0;
> +			ctlr->msg_rx_idx++;
> +		}
> +
> +		if (fifo_state & HISI_I2C_FIFO_STATE_RX_EMPTY)
> +			break;
> +	}
> +
> +	return 0;
> +}
> +
> +static void hisi_i2c_xfer_msg(struct hisi_i2c_controller *ctlr)
> +{
> +	int max_write = HISI_I2C_TX_FIFO_DEPTH;
> +	bool need_restart = false, last_msg;
> +	struct i2c_msg *cur_msg;
> +	u32 cmd, fifo_state;
> +
> +	while (ctlr->msg_tx_idx < ctlr->msg_num) {
> +		cur_msg = ctlr->msgs + ctlr->msg_tx_idx;
> +		last_msg = (ctlr->msg_tx_idx == ctlr->msg_num - 1);
> +
> +		/* Signal the SR bit when we start transferring a new message */
> +		if (ctlr->msg_tx_idx && !ctlr->buf_tx_idx)
> +			need_restart = true;
> +
> +		fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
> +		while (!(fifo_state & HISI_I2C_FIFO_STATE_TX_FULL) &&

> +		       ctlr->buf_tx_idx < cur_msg->len &&
> +		       max_write) {

One line?

> +			cmd = 0;
> +
> +			if (need_restart) {
> +				cmd |= HISI_I2C_CMD_TXDATA_SR_EN;
> +				need_restart = false;
> +			}
> +
> +			/* Signal the STOP bit at the last frame of the last message */

> +			if (ctlr->buf_tx_idx == cur_msg->len - 1 &&
> +			    last_msg)

One line?

> +				cmd |= HISI_I2C_CMD_TXDATA_P_EN;
> +
> +			if (cur_msg->flags & I2C_M_RD)
> +				cmd |= HISI_I2C_CMD_TXDATA_RW;
> +			else
> +				cmd |= FIELD_PREP(HISI_I2C_CMD_TXDATA_DATA,
> +						  cur_msg->buf[ctlr->buf_tx_idx]);
> +
> +			writel(cmd, ctlr->iobase + HISI_I2C_CMD_TXDATA);
> +			ctlr->buf_tx_idx++;
> +			max_write--;
> +
> +			fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
> +		}
> +
> +		/* Update the transfer index after per message transfer is done. */
> +		if (ctlr->buf_tx_idx == cur_msg->len) {
> +			ctlr->buf_tx_idx = 0;
> +			ctlr->msg_tx_idx++;
> +		}
> +
> +		if ((fifo_state & HISI_I2C_FIFO_STATE_TX_FULL) ||
> +		    max_write == 0)
> +			break;
> +	}
> +}
> +
> +static irqreturn_t hisi_i2c_irq(int irq, void *context)
> +{
> +	struct hisi_i2c_controller *ctlr = context;
> +	u32 int_stat;
> +
> +	int_stat = readl(ctlr->iobase + HISI_I2C_INT_MSTAT);
> +	hisi_i2c_clear_int(ctlr, int_stat);
> +	if (!(int_stat & HISI_I2C_INT_ALL))
> +		return IRQ_NONE;
> +
> +	if (int_stat & HISI_I2C_INT_TX_EMPTY)
> +		hisi_i2c_xfer_msg(ctlr);
> +
> +	if (int_stat & HISI_I2C_INT_ERR) {
> +		ctlr->xfer_err = int_stat;
> +		goto out;
> +	}
> +
> +	/* Drain the rx fifo before finish the transfer */
> +	if (int_stat & (HISI_I2C_INT_TRANS_CPLT | HISI_I2C_INT_RX_FULL))
> +		hisi_i2c_read_rx_fifo(ctlr);
> +
> +out:
> +	if (int_stat & HISI_I2C_INT_TRANS_CPLT || ctlr->xfer_err) {
> +		hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL);
> +		hisi_i2c_clear_int(ctlr, HISI_I2C_INT_ALL);
> +		complete(ctlr->completion);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * Helper function for calculating and configuring the HIGH and LOW
> + * periods of SCL clock. The caller will pass the ratio of the
> + * counts (divide / divisor) according to the target speed mode,
> + * and the target registers.
> + */
> +static void hisi_i2c_set_scl(struct hisi_i2c_controller *ctlr,
> +			     u32 divide, u32 divisor,
> +			     u32 reg_hcnt, u32 reg_lcnt)
> +{
> +	u32 total_cnt, t_scl_hcnt, t_scl_lcnt, scl_fall_cnt, scl_rise_cnt;
> +	u32 scl_hcnt, scl_lcnt;
> +
> +	/* Total SCL clock cycles per speed period */
> +	total_cnt = DIV_ROUND_UP_ULL(ctlr->clk_rate_khz * 1000, ctlr->t.bus_freq_hz);

HZ_PER_KHZ ?

> +	/* Total HIGH level SCL clock cycles including edges */
> +	t_scl_hcnt = DIV_ROUND_UP_ULL(total_cnt * divide, divisor);
> +	/* Total LOW level SCL clock cycles including edges */
> +	t_scl_lcnt = total_cnt - t_scl_hcnt;
> +	/* Fall edge SCL clock cycles */
> +	scl_fall_cnt = NSEC_TO_CYCLES(ctlr->t.scl_fall_ns, ctlr->clk_rate_khz);
> +	/* Rise edge SCL clock cycles */
> +	scl_rise_cnt = NSEC_TO_CYCLES(ctlr->t.scl_rise_ns, ctlr->clk_rate_khz);
> +
> +	/* Calculated HIGH and LOW periods of SCL clock */
> +	scl_hcnt = t_scl_hcnt - ctlr->spk_len - 7 - scl_fall_cnt;
> +	scl_lcnt = t_scl_lcnt - 1 - scl_rise_cnt;
> +
> +	writel(scl_hcnt, ctlr->iobase + reg_hcnt);
> +	writel(scl_lcnt, ctlr->iobase + reg_lcnt);
> +}
> +
> +static void hisi_i2c_configure_bus(struct hisi_i2c_controller *ctlr)
> +{
> +	u32 reg, sda_hold_cnt, speed_mode;
> +
> +	i2c_parse_fw_timings(ctlr->dev, &ctlr->t, true);
> +	ctlr->spk_len = NSEC_TO_CYCLES(ctlr->t.digital_filter_width_ns, ctlr->clk_rate_khz);
> +
> +	switch (ctlr->t.bus_freq_hz) {
> +	case I2C_MAX_FAST_MODE_FREQ:
> +		speed_mode = HISI_I2C_FAST_SPEED_MODE;
> +		hisi_i2c_set_scl(ctlr, 26, 76, HISI_I2C_FS_SCL_HCNT, HISI_I2C_FS_SCL_LCNT);
> +		break;
> +	case I2C_MAX_HIGH_SPEED_MODE_FREQ:
> +		speed_mode = HISI_I2C_HIGH_SPEED_MODE;
> +		hisi_i2c_set_scl(ctlr, 6, 22, HISI_I2C_HS_SCL_HCNT, HISI_I2C_HS_SCL_LCNT);
> +		break;
> +	case I2C_MAX_STANDARD_MODE_FREQ:
> +	default:
> +		speed_mode = HISI_I2C_STD_SPEED_MODE;
> +
> +		/* For default condition force the bus speed to standard mode. */
> +		ctlr->t.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
> +		hisi_i2c_set_scl(ctlr, 40, 87, HISI_I2C_SS_SCL_HCNT, HISI_I2C_SS_SCL_LCNT);
> +		break;
> +	}
> +
> +	reg = readl(ctlr->iobase + HISI_I2C_FRAME_CTRL);
> +	reg &= ~HISI_I2C_FRAME_CTRL_SPEED_MODE;
> +	reg |= FIELD_PREP(HISI_I2C_FRAME_CTRL_SPEED_MODE, speed_mode);
> +	writel(reg, ctlr->iobase + HISI_I2C_FRAME_CTRL);
> +
> +	sda_hold_cnt = NSEC_TO_CYCLES(ctlr->t.sda_hold_ns, ctlr->clk_rate_khz);
> +
> +	reg = FIELD_PREP(HISI_I2C_SDA_HOLD_TX, sda_hold_cnt);
> +	writel(reg, ctlr->iobase + HISI_I2C_SDA_HOLD);
> +
> +	writel(ctlr->spk_len, ctlr->iobase + HISI_I2C_FS_SPK_LEN);
> +
> +	reg = FIELD_PREP(HISI_I2C_FIFO_RX_AF_THRESH, HISI_I2C_RX_F_AF_THRESH);
> +	reg |= FIELD_PREP(HISI_I2C_FIFO_TX_AE_THRESH, HISI_I2C_TX_F_AE_THRESH);
> +	writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL);
> +}
> +
> +static int hisi_i2c_probe(struct platform_device *pdev)
> +{
> +	struct hisi_i2c_controller *ctlr;
> +	struct device *dev = &pdev->dev;
> +	struct i2c_adapter *adapter;
> +	u32 hw_version;
> +	int ret;
> +
> +	ctlr = devm_kzalloc(dev, sizeof(*ctlr), GFP_KERNEL);
> +	if (!ctlr)
> +		return -ENOMEM;
> +
> +	ctlr->iobase = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(ctlr->iobase))
> +		return PTR_ERR(ctlr->iobase);
> +
> +	ctlr->irq = platform_get_irq(pdev, 0);
> +	if (ctlr->irq < 0)
> +		return ctlr->irq;
> +
> +	ctlr->dev = dev;
> +
> +	hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL);

> +	ret = devm_request_irq(dev, ctlr->irq, hisi_i2c_irq,
> +			       0, "hisi-i2c", ctlr);

One line?

> +	if (ret) {
> +		dev_err(dev, "failed to request irq handler, ret = %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = device_property_read_u64(dev, "clk_rate", &ctlr->clk_rate_khz);
> +	if (ret) {
> +		dev_err(dev, "failed to get clock frequency, ret = %d\n", ret);
> +		return ret;
> +	}
> +
> +	ctlr->clk_rate_khz = DIV_ROUND_UP_ULL(ctlr->clk_rate_khz, HZ_PER_KHZ);
> +
> +	hisi_i2c_configure_bus(ctlr);
> +
> +	adapter = &ctlr->adapter;
> +	snprintf(adapter->name, sizeof(adapter->name),
> +		 "HiSilicon I2C Controller %s", dev_name(dev));
> +	adapter->owner = THIS_MODULE;
> +	adapter->algo = &hisi_i2c_algo;
> +	adapter->dev.parent = dev;
> +	i2c_set_adapdata(adapter, ctlr);
> +
> +	ret = devm_i2c_add_adapter(dev, adapter);
> +	if (ret) {
> +		dev_err(dev, "failed to add i2c adapter, ret = %d\n", ret);
> +		return ret;
> +	}
> +
> +	hw_version = readl(ctlr->iobase + HISI_I2C_VERSION);
> +	dev_info(ctlr->dev, "speed mode is %s. hw version 0x%x\n",
> +		 i2c_freq_mode_string(ctlr->t.bus_freq_hz), hw_version);
> +
> +	return 0;
> +}
> +
> +static const struct acpi_device_id hisi_i2c_acpi_ids[] = {
> +	{ "HISI03D1", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(acpi, hisi_i2c_acpi_ids);
> +MODULE_ALIAS("platform:hisi-i2c");
> +
> +static struct platform_driver hisi_i2c_driver = {
> +	.probe		= hisi_i2c_probe,
> +	.driver		= {
> +		.name	= "hisi-i2c",
> +		.acpi_match_table = hisi_i2c_acpi_ids,
> +	},
> +};

> +

No need for blank line here.

> +module_platform_driver(hisi_i2c_driver);
> +
> +MODULE_AUTHOR("Yicong Yang <yangyicong@hisilicon.com>");
> +MODULE_DESCRIPTION("HiSilicon I2C Controller Driver");
> +MODULE_LICENSE("GPL");
> -- 
> 2.8.1
> 

-- 
With Best Regards,
Andy Shevchenko



  reply index

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-03-30 13:05 [PATCH v4 0/4] Add " Yicong Yang
2021-03-30 13:05 ` [PATCH v4 1/4] i2c: core: add managed function for adding i2c adapters Yicong Yang
2021-03-30 13:30   ` Andy Shevchenko
2021-03-30 13:05 ` [PATCH v4 2/4] i2c: core: add api to provide frequency mode strings Yicong Yang
2021-03-30 13:30   ` Andy Shevchenko
2021-03-30 13:05 ` [PATCH v4 3/4] i2c: add support for HiSilicon I2C controller Yicong Yang
2021-03-30 13:29   ` Andy Shevchenko [this message]
2021-03-30 13:05 ` [PATCH v4 4/4] MAINTAINERS: Add maintainer for HiSilicon I2C driver Yicong Yang
2021-03-30 13:39 ` [PATCH v4 0/4] Add support for HiSilicon I2C controller Andy Shevchenko
2021-03-30 14:04   ` Yicong Yang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=YGMntYT2iz72wgrd@smile.fi.intel.com \
    --to=andriy.shevchenko@linux.intel.com \
    --cc=digetx@gmail.com \
    --cc=jarkko.nikula@linux.intel.com \
    --cc=john.garry@huawei.com \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linuxarm@huawei.com \
    --cc=prime.zeng@huawei.com \
    --cc=rmk+kernel@armlinux.org.uk \
    --cc=song.bao.hua@hisilicon.com \
    --cc=treding@nvidia.com \
    --cc=wsa@kernel.org \
    --cc=yangyicong@hisilicon.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Linux-i2c Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-i2c/0 linux-i2c/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-i2c linux-i2c/ https://lore.kernel.org/linux-i2c \
		linux-i2c@vger.kernel.org
	public-inbox-index linux-i2c

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-i2c


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git