All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Michał Mirosław" <mirq-linux@rere.qmqm.pl>
To: LakshmiPraveen Kopparthi <LakshmiPraveen.Kopparthi@microchip.com>
Cc: wsa@kernel.org, andriy.shevchenko@linux.intel.com,
	digetx@gmail.com, treding@nvidia.com, s.shtylyov@omp.ru,
	linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org,
	UNGLinuxDriver@microchip.com
Subject: Re: [PATCH v1 1/2] i2c:busses:Register PCI1XXXX adapter to I2C subsystem
Date: Wed, 29 Sep 2021 19:12:23 +0200	[thread overview]
Message-ID: <YVSed/F9pk8n9O2P@qmqm.qmqm.pl> (raw)
In-Reply-To: <20210929062215.23905-2-LakshmiPraveen.Kopparthi@microchip.com>

On Wed, Sep 29, 2021 at 11:52:14AM +0530, LakshmiPraveen Kopparthi wrote:
> Register the adapter to the I2C subsystem. Also the power management
> routines are added.
[...]
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-mchp-pci1xxxx.c
> @@ -0,0 +1,616 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Microchip PCI1XXXX I2C adapter driver for PCIe Switch
> + * which has I2C controller in one of its downstream functions
> + *
> + * * Copyright 2020-2021 Microchip Technology, Inc
> + *
> + * Author: LakshmiPraveen Kopparthi <LakshmiPraveen.Kopparthi@microchip.com>
> + */

Is there a datasheet you can link to? Some register bits have cryptic names
that might not be understandable without the DS.

[...]
> +#define BAR0    0
> +#define BAR1    1
> +#define BAR2    2
> +#define BAR3    3

Those don't add much value and only BAR0 is actually used.

[...]
> +#define SMB_CORE_CTRL_ESO		(0x40)
> +#define SMB_CORE_CTRL_FW_ACK		(0x10)

Those parentheses are not needed.

[...]
> +#define I2C_FOD_EN		(0x10)
> +#define I2C_PULL_UP_EN		(0x08)
> +#define I2C_PULL_DOWN_EN	(0x04)
> +#define I2C_INPUT_EN		(0x02)
> +#define I2C_OUTPUT_EN		(0x01)

I guess the HW can do pull-ups, but the driver doesn't support that. Is it
on purpose?

[...]
> +#define PCI1XXXX_I2C_TIMEOUT	(msecs_to_jiffies(1000))

Define the timeout (in ms) and add msecs_to_jiffies() at the use site 
where needed. But... it doesn't seem to be used?

[...]
> +struct pci1xxxx_i2c {
> +	struct i2c_adapter adap;
> +	struct device *dev;

I think you can use adap.dev.parent for this.

[...]
> +static irqreturn_t pci1xxxx_i2c_isr(int irq, void *dev)
> +{
> +	struct pci1xxxx_i2c *i2c = dev;
> +	bool intr_handled = false;
> +	unsigned long flags;
> +	u16 regval;
> +	u8 regval1;
> +
> +	spin_lock_irqsave(&i2c->lock, flags);

This is hardirq context, so spin_lock() is enough. But, it looks like nothing
else uses this lock so it's eithier superfluous or missing somewhere else.

> +	/* Mask the interrupt */
> +	regval = readw(i2c->i2c_base + SMBUS_GEN_INT_MASK_REG_OFF);
> +	regval |= (SMBALERT_INTR_MASK | I2C_BUF_MSTR_INTR_MASK);
> +	writew(regval, (i2c->i2c_base + SMBUS_GEN_INT_MASK_REG_OFF));

No need to mask and unmask the interrupt as it will be blocked anyway
until the ISR returns.

> +	/*
> +	 * Read the SMBus interrupt status register to see if the
> +	 * DMA_TERM interrupt has caused this callback
> +	 */
> +	regval = readw(i2c->i2c_base + SMBUS_GEN_INT_STAT_REG_OFF);
> +
> +	if (regval & I2C_BUF_MSTR_INTR_MASK) {
> +		regval1 = readb(i2c->i2c_base + SMBUS_INTR_STAT_REG_OFF);
> +		if (regval1 & INTR_STAT_DMA_TERM) {
> +			complete(&i2c->i2c_xfer_done);
> +			intr_handled = true;
> +			writeb(INTR_STAT_DMA_TERM,
> +			       (i2c->i2c_base + SMBUS_INTR_STAT_REG_OFF));
> +		}
> +		/* ACK the high level interrupt */
> +		writew(I2C_BUF_MSTR_INTR_MASK,
> +		       (i2c->i2c_base + SMBUS_GEN_INT_STAT_REG_OFF));

You already have pci1xxxx_ack_high_level_intr() - why not use or delete it?

> +	}
> +
> +	if (regval & SMBALERT_INTR_MASK) {
> +		intr_handled = true;
> +		/* ACK the high level interrupt */
> +		writew(SMBALERT_INTR_MASK,
> +		       (i2c->i2c_base + SMBUS_GEN_INT_STAT_REG_OFF));

Nothing is done with the interrupt here - why enable it, then?

> +	}
> +
> +	regval = readw(i2c->i2c_base + SMBUS_GEN_INT_MASK_REG_OFF);
> +	/* UnMask the interrupt */
> +	regval &= ~(I2C_BUF_MSTR_INTR_MASK | SMBALERT_INTR_MASK);
> +	writew(regval, (i2c->i2c_base + SMBUS_GEN_INT_MASK_REG_OFF));
> +
> +	spin_unlock_irqrestore(&i2c->lock, flags);
> +
> +	if (intr_handled)
> +		return IRQ_HANDLED;
> +	else
> +		return IRQ_NONE;
> +}
[...]
> +static void pci1xxxx_i2c_set_freq(struct pci1xxxx_i2c *i2c)
> +{
> +	/*
> +	 * The SMB core needs specific values to be set in the BUS_CLK register
> +	 * for the corresponding frequency
> +	 */
> +	switch (i2c->freq) {
> +	case I2C_MAX_STANDARD_MODE_FREQ:
> +		writeb(SR_HOLD_TIME_100KHZ,
> +		       (i2c->i2c_base + SMB_CORE_SR_HOLD_TIME_REG_OFF));
> +		writel(SMB_IDLE_SCALING_100KHZ,
> +		       (i2c->i2c_base + SMB_CORE_IDLE_SCALING_REG_OFF));
> +		writew(BUS_CLK_100KHZ,
> +		       (i2c->i2c_base + SMB_CORE_BUS_CLK_REG_OFF));
> +		writel(CLK_SYNC_100KHZ,
> +		       (i2c->i2c_base + SMB_CORE_CLK_SYNC_REG_OFF));
> +		writel(DATA_TIMING_100KHZ,
> +		       (i2c->i2c_base + SMB_CORE_DATA_TIMING_REG_OFF));
> +		writel(TO_SCALING_100KHZ,
> +		       (i2c->i2c_base + SMB_CORE_TO_SCALING_REG_OFF));
> +		break;
> +
> +	case I2C_MAX_FAST_MODE_PLUS_FREQ:
[...]

Is it necessary to limit the frequencies to the three specified? Can't the
register values be calculated based on the exact frequency requested? It is
sometimes needed to run the bus at a lower frequency due to electrical or
chip design issues.

[...]
> +/*
> + * We could have used I2C_FUNC_SMBUS_EMUL but that includes
> + * SMBUS_QUICK as well.We dnt support SMBUS_QUICK hence the
> + * need for a lengthy funcs callback
> + */

You could say: I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK. But
there are also more missing bits like PEC. BTW, is the hardware
not able to handle zero-sized transfer?

> +static const struct i2c_adapter pci1xxxx_i2c_ops = {
> +	.owner	= THIS_MODULE,
> +	.name	= "Pci1xxxx I2c Adapter",

I2C

> +	.algo	= &pci1xxxx_i2c_algo,
> +};
> 
> +static int pci1xxxx_i2c_suspend(struct device *dev)
> +{
> +	struct pci1xxxx_i2c *i2c = dev_get_drvdata(dev);
> +	struct pci_dev *pdev = to_pci_dev(dev);
> +	u32 regval;
> +
> +	i2c_mark_adapter_suspended(&i2c->adap);
> +
> +	pci1xxxx_ack_high_level_intr(i2c, ALL_HIGH_LAYER_INTR);

There should be no active interrupt signals, unless they can wait until
resume for servicing. Either way, acking them blankly looks suspicious.
i2c_mark_adapter_suspended() should guarantee there are no transfers
coming (or being serviced) after it returns.

> +	pci1xxxx_i2c_config_high_level_intr(i2c, ALL_HIGH_LAYER_INTR, false);
> +
> +	/*
> +	 * Enable the PERST_DIS bit to mask the PERST from
> +	 * resetting the core regs
> +	 */
> +	regval = readl(i2c->i2c_base + SMBUS_RESET_REG);
> +	regval |= PERI_SMBUS_D3_RESET_DIS;
> +	writel(regval, i2c->i2c_base + SMBUS_RESET_REG);
> +
> +	return 0;
> +}
> 
> +static int pci1xxxx_i2c_resume(struct device *dev)
> +{
> +	struct pci1xxxx_i2c *i2c = dev_get_drvdata(dev);
> +	struct pci_dev *pdev = to_pci_dev(dev);
> +	u32 regval;
> +
> +	i2c_mark_adapter_resumed(&i2c->adap);

This should go at the end, after preparing the HW. BTW, the interrupt
config is not restored.

> +
> +	regval = readl(i2c->i2c_base + SMBUS_RESET_REG);
> +	regval &= ~PERI_SMBUS_D3_RESET_DIS;
> +	writel(regval, i2c->i2c_base + SMBUS_RESET_REG);
> +
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(pci1xxxx_i2c_pm_ops, pci1xxxx_i2c_suspend,
> +			 pci1xxxx_i2c_resume);
> +
> +static int pci1xxxx_i2c_probe_pci(struct pci_dev *pdev,
> +				  const struct pci_device_id *ent)
> +{
> +	struct pci1xxxx_i2c *i2c;
> +	int ret;
> +
> +	i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
> +	if (!i2c)
> +		return -ENOMEM;
> +
> +	i2c->dev = &pdev->dev;
> +
> +	pci_set_drvdata(pdev, i2c);
> +	ret = pcim_enable_device(pdev);
> +	if (ret)
> +		return ret;
> +
> +	ret = pci_request_regions(pdev, pci_name(pdev));
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * We are getting the base address of the SMB core. SMB core uses
> +	 * BAR0 and 32K is the size here pci_resource_len returns 32K by
> +	 * reading BAR0
> +	 */
> +
> +	i2c->i2c_base = pcim_iomap(pdev, BAR0, pci_resource_len(pdev, BAR0));

pcim_iomap_regions()

> +	if (!i2c->i2c_base) {
> +		ret = -ENOMEM;
> +		goto err_free_region;
> +	}
> +
> +	ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
> +	if (ret < 0)
> +		goto err_free_region;
> +
> +	i2c->irq = pci_irq_vector(pdev, 0);

'irq' field doesn't seem to be used past the request_irq(), so maybe can
be removed?

> +	/* Register the isr. we are not using any isr flags here */
> +	ret = devm_request_irq(&pdev->dev, i2c->irq, pci1xxxx_i2c_isr,
> +			       PCI1XXXX_IRQ_FLAGS,
> +			       pci_name(pdev), i2c);
> [...]
> 
> +	pci_set_master(pdev);
> +
> +	init_completion(&i2c->i2c_xfer_done);
> +
> +	spin_lock_init(&i2c->lock);
> +
> +	pci1xxxx_i2c_init(i2c);

This all should be done before request_irq().

> +	i2c->adap = pci1xxxx_i2c_ops;
> +
> +	i2c->adap.class = I2C_CLASS_SPD;
> +	i2c->adap.dev.parent = &pdev->dev;
> +
> +	snprintf(i2c->adap.name, sizeof(i2c->adap.name),
> +		 "MCHP PCI1xxxx i2c adapter at %s", pci_name(pdev));
> +
> +	i2c_set_adapdata(&i2c->adap, i2c);
> +
> +	ret = i2c_add_adapter(&i2c->adap);
> +	if (ret) {
> +		dev_err(&pdev->dev, "i2c add adapter failed = %d\n", ret);
> +		goto err_free_region;
> +	}
> +
> +	return 0;
> +
> +err_free_region:
> +	pci_release_regions(pdev);
> +	return ret;
> +}
[...]

It would be better to have the driver in one patch, than split in two.

Best Regards
Michał Mirosław

  parent reply	other threads:[~2021-09-29 17:12 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-09-29  6:22 [PATCH v1 0/2] i2c:busses:Microchip PCI1XXXX I2C adapter LakshmiPraveen Kopparthi
2021-09-29  6:22 ` [PATCH v1 1/2] i2c:busses:Register PCI1XXXX adapter to I2C subsystem LakshmiPraveen Kopparthi
2021-09-29  7:18   ` Dmitry Osipenko
2021-09-29 14:35     ` Dmitry Osipenko
2021-10-05  8:49       ` LakshmiPraveen Kopparthi
2021-09-29 16:44     ` Andy Shevchenko
2021-10-05  8:49       ` LakshmiPraveen Kopparthi
2021-10-05  8:50     ` LakshmiPraveen Kopparthi
2021-10-07 16:24       ` Dmitry Osipenko
2021-10-07 16:33         ` Andy Shevchenko
2021-10-07 16:30       ` Dmitry Osipenko
2021-09-29 17:12   ` Michał Mirosław [this message]
2021-10-08  6:22     ` LakshmiPraveen Kopparthi
2021-10-07 16:40   ` Dmitry Osipenko
2021-10-07 17:05     ` Andy Shevchenko
2021-09-29  6:22 ` [PATCH v1 2/2] i2c:busses:Read and Write routines for PCI1XXXX I2C module LakshmiPraveen Kopparthi
2021-09-29  7:20   ` Dmitry Osipenko
2021-10-05  8:48     ` LakshmiPraveen Kopparthi
2021-10-07 16:15       ` Dmitry Osipenko

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=YVSed/F9pk8n9O2P@qmqm.qmqm.pl \
    --to=mirq-linux@rere.qmqm.pl \
    --cc=LakshmiPraveen.Kopparthi@microchip.com \
    --cc=UNGLinuxDriver@microchip.com \
    --cc=andriy.shevchenko@linux.intel.com \
    --cc=digetx@gmail.com \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=s.shtylyov@omp.ru \
    --cc=treding@nvidia.com \
    --cc=wsa@kernel.org \
    /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
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.