All of lore.kernel.org
 help / color / mirror / Atom feed
From: Lee Jones <lee.jones@linaro.org>
To: min.li.xe@renesas.com
Cc: sameo@linux.intel.com, grant.likely@linaro.org,
	robh+dt@kernel.org, linux-kernel@vger.kernel.org,
	devicetree@vger.kernel.org
Subject: Re: [PATCH mfd v1] mfd: Add Renesas Synchronization Management Unit (SMU) support
Date: Wed, 19 May 2021 09:08:07 +0100	[thread overview]
Message-ID: <20210519080807.GH805368@dell> (raw)
In-Reply-To: <1619466246-11198-1-git-send-email-min.li.xe@renesas.com>

On Mon, 26 Apr 2021, min.li.xe@renesas.com wrote:

> From: Min Li <min.li.xe@renesas.com>
> 
> Add support for ClockMatrix(TM) and 82P33xxx families of timing
> and synchronization devices. The access interface can be either
> SPI or I2C. Currently, it will create 2 types of MFD devices,
> which are to be used by the corresponding rsmu character device
> driver and the PTP hardware clock driver, respectively.
> 
> Signed-off-by: Min Li <min.li.xe@renesas.com>
> ---
>  drivers/mfd/Kconfig              |  28 ++
>  drivers/mfd/Makefile             |   3 +
>  drivers/mfd/rsmu_i2c.c           | 349 +++++++++++++++++
>  drivers/mfd/rsmu_private.h       |  32 ++
>  drivers/mfd/rsmu_spi.c           | 376 ++++++++++++++++++
>  include/linux/mfd/idt82p33_reg.h | 116 ++++++
>  include/linux/mfd/idt8a340_reg.h | 817 +++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/rsmu.h         |  54 +++
>  8 files changed, 1775 insertions(+)
>  create mode 100644 drivers/mfd/rsmu_i2c.c
>  create mode 100644 drivers/mfd/rsmu_private.h
>  create mode 100644 drivers/mfd/rsmu_spi.c
>  create mode 100644 include/linux/mfd/idt82p33_reg.h
>  create mode 100644 include/linux/mfd/idt8a340_reg.h
>  create mode 100644 include/linux/mfd/rsmu.h
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 21a131d..63b18b3 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -2172,5 +2172,33 @@ config MFD_INTEL_M10_BMC
>  	  additional drivers must be enabled in order to use the functionality
>  	  of the device.
>  
> +config MFD_RSMU_I2C
> +	tristate "Renesas Synchronization Management Unit with I2C"
> +	depends on I2C && OF
> +	depends on MFD_RSMU_SPI=n

Not sure I've seen a relationship like this before.

Devices capable of SPI and I2C usually have a core driver which
selects one or the other.  It will also save some code duplication you
have here.  Please take a look at some other examples in the MFD
subsystem.

> +	select MFD_CORE
> +	select REGMAP_I2C
> +	help
> +	  Support for the Renesas synchronization management unit, such as

As this is the name of the thing, it should be capitalised.

> +	  Clockmatrix and 82P33XXX series. This option supports I2C as
> +	  the control interface.
> +
> +	  This driver provides common support for accessing the device,

Nit: This should be a '.'

> +	  additional drivers must be enabled in order to use the functionality
> +	  of the device.
> +
> +config MFD_RSMU_SPI
> +	tristate "Renesas Synchronization Management Unit with SPI"
> +	depends on SPI && OF
> +	select MFD_CORE
> +	help
> +	  Support for the Renesas synchronization management unit, such as

As above.

> +	  Clockmatrix and 82P33XXX series. This option supports SPI as
> +	  the control interface.
> +
> +	  This driver provides common support for accessing the device,

As above.

> +	  additional drivers must be enabled in order to use the functionality
> +	  of the device.
> +
>  endmenu
>  endif
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 4f6d2b8..4ab32ef 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -271,3 +271,6 @@ obj-$(CONFIG_MFD_INTEL_M10_BMC)   += intel-m10-bmc.o
>  
>  obj-$(CONFIG_MFD_ATC260X)	+= atc260x-core.o
>  obj-$(CONFIG_MFD_ATC260X_I2C)	+= atc260x-i2c.o
> +
> +obj-$(CONFIG_MFD_RSMU_I2C)	+= rsmu_i2c.o
> +obj-$(CONFIG_MFD_RSMU_SPI)	+= rsmu_spi.o
> diff --git a/drivers/mfd/rsmu_i2c.c b/drivers/mfd/rsmu_i2c.c
> new file mode 100644
> index 0000000..2349f6d
> --- /dev/null
> +++ b/drivers/mfd/rsmu_i2c.c
> @@ -0,0 +1,349 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Multi-function driver for the IDT ClockMatrix(TM) and 82P33xxx families of

No such thing as a "Multi-function driver".  That is a Linux
implementation detail.  Please describe the device instead.

> + * timing and synchronization devices.
> + *
> + * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.

This is 2 years out of date.

> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/regmap.h>
> +#include <linux/of.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/rsmu.h>

Alphabetical.

'\n'

> +#include "rsmu_private.h"
> +
> +/*
> + * 16-bit register address: the lower 8 bits of the register address come
> + * from the offset addr byte and the upper 8 bits come from the page register.
> + */
> +#define	RSMU_CM_PAGE_ADDR		0xFD
> +#define	RSMU_CM_PAGE_WINDOW		256
> +
> +/*
> + * 15-bit register address: the lower 7 bits of the register address come
> + * from the offset addr byte and the upper 8 bits come from the page register.
> + */
> +#define	RSMU_SABRE_PAGE_ADDR		0x7F
> +#define	RSMU_SABRE_PAGE_WINDOW		128
> +
> +static bool rsmu_cm_volatile_reg(struct device *dev, unsigned int reg);
> +static bool rsmu_sabre_volatile_reg(struct device *dev, unsigned int reg);

No forward declarations please.

Better to reorder the functions themselves.

> +/* Current mfd device index */
> +static atomic_t rsmu_ndevs = ATOMIC_INIT(0);

Counting/indexing this way is generally not good/required.

> +/* Platform data */
> +static struct rsmu_pdata rsmu_pdata[RSMU_MAX_MFD_DEV];

Please create this dynamically using the *alloc() helpers.

> +/* clockmatrix phc devices */

Improve the naming, then these comments become redundant.

> +static struct mfd_cell rsmu_cm_pdev[RSMU_MAX_MFD_DEV] = {

If you're populating, you don't need to specify a size.

> +	[0] = {

If you need to index these, you should do it with defines.

> +		.name = "idtcm-ptp0",

That's terrible nomenclature.

What is an 'idtcm-ptp'?

> +		.of_compatible	= "renesas,idtcm-ptp0",

We don't usually allow device indexing in device tree.

Compatibles for the same device are usually identical.

> +	},
> +	[1] = {
> +		.name = "idtcm-ptp1",
> +		.of_compatible	= "renesas,idtcm-ptp1",
> +	},
> +	[2] = {
> +		.name = "idtcm-ptp2",
> +		.of_compatible	= "renesas,idtcm-ptp2",
> +	},
> +	[3] = {
> +		.name = "idtcm-ptp3",
> +		.of_compatible	= "renesas,idtcm-ptp3",
> +	},
> +};
> +
> +/* sabre phc devices */
> +static struct mfd_cell rsmu_sabre_pdev[RSMU_MAX_MFD_DEV] = {
> +	[0] = {
> +		.name = "idt82p33-ptp0",
> +		.of_compatible	= "renesas,idt82p33-ptp0",
> +	},
> +	[1] = {
> +		.name = "idt82p33-ptp1",
> +		.of_compatible	= "renesas,idt82p33-ptp1",
> +	},
> +	[2] = {
> +		.name = "idt82p33-ptp2",
> +		.of_compatible	= "renesas,idt82p33-ptp2",
> +	},
> +	[3] = {
> +		.name = "idt82p33-ptp3",
> +		.of_compatible	= "renesas,idt82p33-ptp3",
> +	},
> +};

As above.

> +/* rsmu character devices */
> +static struct mfd_cell rsmu_cdev[RSMU_MAX_MFD_DEV] = {
> +	[0] = {
> +		.name = "rsmu-cdev0",
> +		.of_compatible	= "renesas,rsmu-cdev0",
> +	},
> +	[1] = {
> +		.name = "rsmu-cdev1",
> +		.of_compatible	= "renesas,rsmu-cdev1",
> +	},
> +	[2] = {
> +		.name = "rsmu-cdev2",
> +		.of_compatible	= "renesas,rsmu-cdev2",
> +	},
> +	[3] = {
> +		.name = "rsmu-cdev3",
> +		.of_compatible	= "renesas,rsmu-cdev3",
> +	},
> +};

As above.

> +static const struct regmap_range_cfg rsmu_cm_range_cfg[] = {
> +	{
> +		.range_min = 0,
> +		.range_max = 0xD000,
> +		.selector_reg = RSMU_CM_PAGE_ADDR,
> +		.selector_mask = 0xFF,
> +		.selector_shift = 0,
> +		.window_start = 0,
> +		.window_len = RSMU_CM_PAGE_WINDOW,
> +	}
> +};
> +
> +static const struct regmap_range_cfg rsmu_sabre_range_cfg[] = {
> +	{
> +		.range_min = 0,
> +		.range_max = 0x400,
> +		.selector_reg = RSMU_SABRE_PAGE_ADDR,
> +		.selector_mask = 0xFF,
> +		.selector_shift = 0,
> +		.window_start = 0,
> +		.window_len = RSMU_SABRE_PAGE_WINDOW,
> +	}
> +};
> +
> +static const struct regmap_config rsmu_regmap_configs[] = {
> +	[RSMU_CM] = {
> +		.reg_bits = 8,
> +		.val_bits = 8,
> +		.max_register = 0xD000,
> +		.ranges = rsmu_cm_range_cfg,
> +		.num_ranges = ARRAY_SIZE(rsmu_cm_range_cfg),
> +		.volatile_reg = rsmu_cm_volatile_reg,
> +		.cache_type = REGCACHE_RBTREE,
> +		.can_multi_write = true,
> +	},
> +	[RSMU_SABRE] = {
> +		.reg_bits = 8,
> +		.val_bits = 8,
> +		.max_register = 0x400,
> +		.ranges = rsmu_sabre_range_cfg,
> +		.num_ranges = ARRAY_SIZE(rsmu_sabre_range_cfg),
> +		.volatile_reg = rsmu_sabre_volatile_reg,
> +		.cache_type = REGCACHE_RBTREE,
> +		.can_multi_write = true,
> +	},
> +};
> +
> +static bool rsmu_cm_volatile_reg(struct device *dev, unsigned int reg)
> +{
> +	switch (reg) {
> +	case RSMU_CM_PAGE_ADDR:
> +		return false;
> +	default:
> +		return true;
> +	}
> +}
> +
> +static bool rsmu_sabre_volatile_reg(struct device *dev, unsigned int reg)
> +{
> +	switch (reg) {
> +	case RSMU_SABRE_PAGE_ADDR:
> +		return false;
> +	default:
> +		return true;
> +	}
> +}
> +
> +int rsmu_read(struct device *dev, u16 reg, u8 *buf, u16 size)
> +{
> +	struct rsmu_dev *rsmu = dev_get_drvdata(dev);
> +
> +	return regmap_bulk_read(rsmu->regmap, reg, buf, size);
> +}
> +EXPORT_SYMBOL_GPL(rsmu_read);
> +
> +int rsmu_write(struct device *dev, u16 reg, u8 *buf, u16 size)
> +{
> +	struct rsmu_dev *rsmu = dev_get_drvdata(dev);
> +
> +	return regmap_bulk_write(rsmu->regmap, reg, buf, size);
> +}
> +EXPORT_SYMBOL_GPL(rsmu_write);
> +
> +static int rsmu_mfd_init(struct rsmu_dev *rsmu, struct mfd_cell *mfd,
> +			 struct rsmu_pdata *pdata)

This is not an init function

> +{
> +	int ret;
> +
> +	mfd->platform_data = pdata;
> +	mfd->pdata_size = sizeof(struct rsmu_pdata);
> +
> +	ret = mfd_add_devices(rsmu->dev, -1, mfd, 1, NULL, 0, NULL);
> +	if (ret < 0) {
> +		dev_err(rsmu->dev, "mfd_add_devices failed with %s\n",
> +			mfd->name);
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int rsmu_dev_init(struct rsmu_dev *rsmu)
> +{
> +	struct rsmu_pdata *pdata;
> +	struct mfd_cell *pmfd;
> +	struct mfd_cell *cmfd;
> +	int ret;
> +
> +	/* Initialize regmap */

This is superfluous.

> +	rsmu->regmap = devm_regmap_init_i2c(rsmu->client,
> +					    &rsmu_regmap_configs[rsmu->type]);
> +	if (IS_ERR(rsmu->regmap)) {
> +		ret = PTR_ERR(rsmu->regmap);
> +		dev_err(rsmu->dev, "Failed to allocate register map: %d\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	/* Initialize device index */
> +	rsmu->index = atomic_read(&rsmu_ndevs);
> +	if (rsmu->index >= RSMU_MAX_MFD_DEV)
> +		return -ENODEV;
> +
> +	/* Initialize platform data */
> +	pdata = &rsmu_pdata[rsmu->index];
> +	pdata->lock = &rsmu->lock;

What is this protecting?  Regmap will have it's own locking.

> +	pdata->type = rsmu->type;

What does the child use this for?

> +	pdata->index = rsmu->index;

What does the child use this for?

> +	/* Initialize MFD devices */
> +	cmfd = &rsmu_cdev[rsmu->index];
> +	if (rsmu->type == RSMU_CM)
> +		pmfd = &rsmu_cm_pdev[rsmu->index];
> +	else if (rsmu->type == RSMU_SABRE)
> +		pmfd = &rsmu_sabre_pdev[rsmu->index];
> +	else
> +		return -EINVAL;
> +
> +	ret = rsmu_mfd_init(rsmu, pmfd, pdata);
> +	if (ret)
> +		return ret;
> +
> +	ret = rsmu_mfd_init(rsmu, cmfd, pdata);
> +	if (ret)
> +		return ret;

The code would be much clearer and readable if you just called
mfd_add_device() twice here instead.  Breaking it out only serves to
obfuscate what you're trying to achieve here.

> +	atomic_inc(&rsmu_ndevs);
> +	return 0;
> +}
> +
> +static int rsmu_dt_init(struct rsmu_dev *rsmu)

This isn't initialising anything.

This should probably be called rsmu_dev_type_get()

Better still, move the type into the table's .data. (see below).

> +{
> +	struct device_node *np = rsmu->dev->of_node;
> +
> +	rsmu->type = RSMU_NONE;
> +	if (of_device_is_compatible(np, "idt,8a34000")) {

I'd give these a secondary, more forthcoming, compatible string.

One which is less, well, like this!

> +		rsmu->type = RSMU_CM;
> +	} else if (of_device_is_compatible(np, "idt,82p33810")) {
> +		rsmu->type = RSMU_SABRE;
> +	} else {
> +		dev_err(rsmu->dev, "unknown RSMU device\n");

"Unknown RSMU device type"

> +		return -EINVAL;

Not sure this is even possible, but should probably be -ENODEV.

> +	}
> +
> +	return 0;
> +}
> +
> +static int rsmu_probe(struct i2c_client *client, const struct i2c_device_id *id)
> +{
> +	struct rsmu_dev *rsmu;
> +	int ret;
> +
> +	rsmu = devm_kzalloc(&client->dev, sizeof(struct rsmu_dev),
> +			       GFP_KERNEL);
> +	if (rsmu == NULL)

if (!rsmu)

> +		return -ENOMEM;
> +
> +	i2c_set_clientdata(client, rsmu);
> +	mutex_init(&rsmu->lock);
> +	rsmu->dev = &client->dev;
> +	rsmu->client = client;
> +
> +	ret = rsmu_dt_init(rsmu);
> +	if (ret)
> +		return ret;
> +
> +	mutex_lock(&rsmu->lock);

Pretty sure you don't need mutexes in .probe()

> +	ret = rsmu_dev_init(rsmu);
> +	mutex_unlock(&rsmu->lock);
> +
> +	if (ret == 0)
> +		dev_info(rsmu->dev, "Probe %s successful\n",
> +			 rsmu_name(rsmu->type));

No thank you.  Please remove this.

> +	return ret;
> +}
> +
> +static int rsmu_remove(struct i2c_client *client)
> +{
> +	struct rsmu_dev *rsmu = i2c_get_clientdata(client);
> +
> +	mfd_remove_devices(&client->dev);

Use devm_*()

> +	mutex_destroy(&rsmu->lock);

Don't mutexes are required in this driver.

> +	atomic_dec(&rsmu_ndevs);

Nor this.

> +	return 0;
> +}
> +
> +static const struct i2c_device_id rsmu_id[] = {
> +	{ "8a34000", 0 },
> +	{ "82p33810", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, rsmu_id);

Use .probe_new, then you can delete this table.

> +static const struct of_device_id rsmu_of_match[] = {
> +	{.compatible = "idt,8a34000", },
> +	{.compatible = "idt,82p33810", },

Missing ' ' after the '{'.

Put the device type in .data instead of matching later on.

> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, rsmu_of_match);
> +
> +static struct i2c_driver rsmu_driver = {
> +	.driver = {
> +		   .name = "rsmu-i2c",
> +		   .of_match_table = of_match_ptr(rsmu_of_match),
> +	},
> +	.probe = rsmu_probe,
> +	.remove	= rsmu_remove,
> +	.id_table = rsmu_id,
> +};
> +
> +static int __init rsmu_init(void)
> +{
> +	return i2c_add_driver(&rsmu_driver);
> +}
> +/* init early so consumer devices can complete system boot */
> +subsys_initcall(rsmu_init);
> +
> +static void __exit rsmu_exit(void)
> +{
> +	i2c_del_driver(&rsmu_driver);
> +}
> +module_exit(rsmu_exit);
> +
> +MODULE_DESCRIPTION("Renesas SMU I2C multi-function driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/mfd/rsmu_private.h b/drivers/mfd/rsmu_private.h
> new file mode 100644
> index 0000000..8841a24
> --- /dev/null
> +++ b/drivers/mfd/rsmu_private.h
> @@ -0,0 +1,32 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Multi-function driver for the IDT ClockMatrix(TM) and 82p33xxx families of
> + * timing and synchronization devices.
> + *
> + * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
> + */
> +
> +#ifndef __RSMU_MFD_PRIVATE_H
> +#define __RSMU_MFD_PRIVATE_H
> +
> +#include <linux/mfd/rsmu.h>
> +
> +/* Maximum number of mfd devices */
> +#define RSMU_MAX_MFD_DEV		4
> +
> +struct rsmu_dev {
> +	struct device *dev;
> +	void *client;

What is in client?

> +	struct regmap *regmap;

> +	struct mutex lock;
> +	enum rsmu_type type;

Why are these in 'dev' and 'pdata'?

> +	u8 index;
> +	u16 page;

Seems odd to pass this in as ddata.

> +};
> +
> +enum rsmu_mfd_type {
> +	RSMU_MFD_PTP		= 0,
> +	RSMU_MFD_CDEV		= 1,
> +	RSMU_MFD_NUM		= 2,
> +};
> +#endif /*  __LINUX_MFD_RSMU_H */
> diff --git a/drivers/mfd/rsmu_spi.c b/drivers/mfd/rsmu_spi.c

Stopping here, as most of this is duplicated.

Please create a core driver instead.

-- 
Lee Jones [李琼斯]
Senior Technical Lead - Developer Services
Linaro.org │ Open source software for Arm SoCs
Follow Linaro: Facebook | Twitter | Blog

  parent reply	other threads:[~2021-05-19  8:08 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-04-26 19:44 [PATCH mfd v1] mfd: Add Renesas Synchronization Management Unit (SMU) support min.li.xe
2021-05-05 14:11 ` Min Li
2021-05-05 15:10   ` Lee Jones
2021-05-19  8:08 ` Lee Jones [this message]
2021-05-21 14:36   ` Min Li
2021-05-21 15:02     ` Lee Jones
2021-05-21 20:04       ` Min Li
2021-05-25 15:20   ` Min Li
2021-05-25 18:23     ` Lee Jones
2021-05-25 19:35       ` Min Li
2021-05-26  8:01         ` Lee Jones
2021-05-26 18:26   ` Min Li
2021-05-27  8:43     ` Lee Jones
2021-06-03 19:31   ` Min Li
  -- strict thread matches above, loose matches on Subject: below --
2021-03-20 15:43 min.li.xe
2021-03-22 15:03 ` Lee Jones
2021-03-22 15:22   ` Min Li
2021-03-23  8:44     ` Lee Jones
2021-03-23 15:01       ` Min Li
2021-03-17 21:53 min.li.xe

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=20210519080807.GH805368@dell \
    --to=lee.jones@linaro.org \
    --cc=devicetree@vger.kernel.org \
    --cc=grant.likely@linaro.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=min.li.xe@renesas.com \
    --cc=robh+dt@kernel.org \
    --cc=sameo@linux.intel.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
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.