diff mbox

[v2,2/2] reset: stm32mp1: Enable stm32mp1 reset driver

Message ID 1521045001-30788-3-git-send-email-gabriel.fernandez@st.com (mailing list archive)
State New, archived
Headers show

Commit Message

Gabriel FERNANDEZ March 14, 2018, 4:30 p.m. UTC
From: Gabriel Fernandez <gabriel.fernandez@st.com>

stm32mp1 RCC IP 1 has a reset SET register and a reset CLEAR register.

Writing '0' on reset SET register has no effect
Writing '1' on reset SET register
	activates the reset of the corresponding peripheral

Writing '0' on reset CLEAR register	has no effect
Writing '1' on reset CLEAR register
	releases the reset of the corresponding peripheral

See Documentation/devicetree/bindings/clock/st,stm32mp1-rcc.txt

Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
 drivers/reset/Kconfig          |   6 ++
 drivers/reset/Makefile         |   1 +
 drivers/reset/reset-stm32mp1.c | 122 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 129 insertions(+)
 create mode 100644 drivers/reset/reset-stm32mp1.c

Comments

kernel test robot March 16, 2018, 12:22 p.m. UTC | #1
Hi Gabriel,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on next-20180309]
[also build test WARNING on v4.16-rc5]
[cannot apply to linus/master v4.16-rc4 v4.16-rc3 v4.16-rc2]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/gabriel-fernandez-st-com/Introduce-STM32MP1-Reset-driver/20180316-082607
reproduce:
        # apt-get install sparse
        make ARCH=x86_64 allmodconfig
        make C=1 CF=-D__CHECK_ENDIAN__


sparse warnings: (new ones prefixed by >>)

>> drivers/reset/reset-stm32mp1.c:73:32: sparse: symbol 'stm32_reset_ops' was not declared. Should it be static?

Please review and possibly fold the followup patch.

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Philipp Zabel March 16, 2018, 1:29 p.m. UTC | #2
Hi Gabriel,

this looks mostly good to me, a few questions and comments below:

On Wed, 2018-03-14 at 17:30 +0100, gabriel.fernandez@st.com wrote:
> From: Gabriel Fernandez <gabriel.fernandez@st.com>
> 
> stm32mp1 RCC IP 1 has a reset SET register and a reset CLEAR register.
> 
> Writing '0' on reset SET register has no effect
> Writing '1' on reset SET register
> 	activates the reset of the corresponding peripheral
> 
> Writing '0' on reset CLEAR register	has no effect
> Writing '1' on reset CLEAR register
> 	releases the reset of the corresponding peripheral
> 
> See Documentation/devicetree/bindings/clock/st,stm32mp1-rcc.txt
> 
> Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
> ---
>  drivers/reset/Kconfig          |   6 ++
>  drivers/reset/Makefile         |   1 +
>  drivers/reset/reset-stm32mp1.c | 122 +++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 129 insertions(+)
>  create mode 100644 drivers/reset/reset-stm32mp1.c
> 
> diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
> index 1efbc6c..c0b292b 100644
> --- a/drivers/reset/Kconfig
> +++ b/drivers/reset/Kconfig
> @@ -97,6 +97,12 @@ config RESET_SIMPLE
>  	   - Allwinner SoCs
>  	   - ZTE's zx2967 family
>  
> +config RESET_STM32MP157
> +	bool "STM32MP157 Reset Driver" if COMPILE_TEST
> +	default MACH_STM32MP157
> +	help
> +	  This enables the RCC reset controller driver for STM32 MPUs.
> +
>  config RESET_SUNXI
>  	bool "Allwinner SoCs Reset Driver" if COMPILE_TEST && !ARCH_SUNXI
>  	default ARCH_SUNXI
> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
> index 132c24f..c1261dc 100644
> --- a/drivers/reset/Makefile
> +++ b/drivers/reset/Makefile
> @@ -15,6 +15,7 @@ obj-$(CONFIG_RESET_MESON) += reset-meson.o
>  obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
>  obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
>  obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
> +obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o
>  obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
>  obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
>  obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o
> diff --git a/drivers/reset/reset-stm32mp1.c b/drivers/reset/reset-stm32mp1.c
> new file mode 100644
> index 0000000..5e25388
> --- /dev/null
> +++ b/drivers/reset/reset-stm32mp1.c
> @@ -0,0 +1,122 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
> + * Author: Gabriel Fernandez <gabriel.fernandez@st.com> for STMicroelectronics.
> + */
> +
> +#include <linux/arm-smccc.h>

This does not seem to be necessary.

> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/of.h>

> +#include <linux/of_device.h>

This does not seem to be necessary either.

> +#include <linux/platform_device.h>
> +#include <linux/reset-controller.h>
> +
> +#define CLR_OFFSET 0x4
> +
> +struct stm32_reset_data {
> +	struct reset_controller_dev	rcdev;
> +	void __iomem			*membase;
> +};
> +
> +static inline struct stm32_reset_data *
> +to_stm32_reset_data(struct reset_controller_dev *rcdev)
> +{
> +	return container_of(rcdev, struct stm32_reset_data, rcdev);
> +}
> +
> +static int stm32_reset_update(struct reset_controller_dev *rcdev,
> +			      unsigned long id, bool assert)
> +{
> +	struct stm32_reset_data *data = to_stm32_reset_data(rcdev);
> +	int reg_width = sizeof(u32);
> +	int bank = id / (reg_width * BITS_PER_BYTE);
> +	int offset = id % (reg_width * BITS_PER_BYTE);
> +	void __iomem *addr;
> +
> +	addr = data->membase + (bank * reg_width);
> +	if (!assert)
> +		addr += CLR_OFFSET;
> +
> +	writel(BIT(offset), addr);
> +
> +	return 0;
> +}
> +
> +static int stm32_reset_assert(struct reset_controller_dev *rcdev,
> +			      unsigned long id)
> +{
> +	return stm32_reset_update(rcdev, id, true);
> +}
> +
> +static int stm32_reset_deassert(struct reset_controller_dev *rcdev,
> +				unsigned long id)
> +{
> +	return stm32_reset_update(rcdev, id, false);
> +}
> +
> +static int stm32_reset_status(struct reset_controller_dev *rcdev,
> +			      unsigned long id)
> +{
> +	struct stm32_reset_data *data = to_stm32_reset_data(rcdev);
> +	int reg_width = sizeof(u32);
> +	int bank = id / (reg_width * BITS_PER_BYTE);
> +	int offset = id % (reg_width * BITS_PER_BYTE);
> +	u32 reg;
> +
> +	reg = readl(data->membase + (bank * reg_width));
> +
> +	return !(reg & BIT(offset));
> +}

So the SET register can be read back and returns 0 for reset lines that
are currently asserted and returns 1 for reset lines that are currently
deasserted?

> +
> +const struct reset_control_ops stm32_reset_ops = {
> +	.assert		= stm32_reset_assert,
> +	.deassert	= stm32_reset_deassert,
> +	.status		= stm32_reset_status,
> +};
> +
> +static const struct of_device_id stm32_reset_dt_ids[] = {
> +	{ .compatible = "st,stm32mp1-rcc"},
> +	{ /* sentinel */ },
> +};

From the DT bindings it looks like the clock and reset drivers are
sharing the same node. Is there just no clock platform_driver at all?

> +
> +static int stm32_reset_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct stm32_reset_data *data;
> +	void __iomem *membase;
> +	struct resource *res;
> +
> +	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	membase = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(membase))
> +		return PTR_ERR(membase);
> +
> +	data->membase = membase;
> +	data->rcdev.owner = THIS_MODULE;
> +	data->rcdev.nr_resets = resource_size(res) * BITS_PER_BYTE;
> +	data->rcdev.ops = &stm32_reset_ops;
> +	data->rcdev.of_node = dev->of_node;
> +
> +	return devm_reset_controller_register(dev, &data->rcdev);
> +}
> +
> +static struct platform_driver stm32_reset_driver = {
> +	.probe	= stm32_reset_probe,
> +	.driver = {
> +		.name		= "stm32mp1-reset",
> +		.of_match_table	= stm32_reset_dt_ids,
> +	},
> +};
> +
> +static int __init stm32_reset_init(void)
> +{
> +	return platform_driver_register(&stm32_reset_driver);
> +}
> +
> +postcore_initcall(stm32_reset_init);

Isn't builtin_platform_driver early enough?

regards
Philipp
Gabriel FERNANDEZ March 16, 2018, 2:35 p.m. UTC | #3
Hi Philipp,

Thanks for reviewing.

On 03/16/2018 02:29 PM, Philipp Zabel wrote:
> Hi Gabriel,
>
> this looks mostly good to me, a few questions and comments below:
>
> On Wed, 2018-03-14 at 17:30 +0100, gabriel.fernandez@st.com wrote:
>> From: Gabriel Fernandez <gabriel.fernandez@st.com>
>>
>> stm32mp1 RCC IP 1 has a reset SET register and a reset CLEAR register.
>>
>> Writing '0' on reset SET register has no effect
>> Writing '1' on reset SET register
>> 	activates the reset of the corresponding peripheral
>>
>> Writing '0' on reset CLEAR register	has no effect
>> Writing '1' on reset CLEAR register
>> 	releases the reset of the corresponding peripheral
>>
>> See Documentation/devicetree/bindings/clock/st,stm32mp1-rcc.txt
>>
>> Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
>> ---
>>   drivers/reset/Kconfig          |   6 ++
>>   drivers/reset/Makefile         |   1 +
>>   drivers/reset/reset-stm32mp1.c | 122 +++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 129 insertions(+)
>>   create mode 100644 drivers/reset/reset-stm32mp1.c
>>
>> diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
>> index 1efbc6c..c0b292b 100644
>> --- a/drivers/reset/Kconfig
>> +++ b/drivers/reset/Kconfig
>> @@ -97,6 +97,12 @@ config RESET_SIMPLE
>>   	   - Allwinner SoCs
>>   	   - ZTE's zx2967 family
>>   
>> +config RESET_STM32MP157
>> +	bool "STM32MP157 Reset Driver" if COMPILE_TEST
>> +	default MACH_STM32MP157
>> +	help
>> +	  This enables the RCC reset controller driver for STM32 MPUs.
>> +
>>   config RESET_SUNXI
>>   	bool "Allwinner SoCs Reset Driver" if COMPILE_TEST && !ARCH_SUNXI
>>   	default ARCH_SUNXI
>> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
>> index 132c24f..c1261dc 100644
>> --- a/drivers/reset/Makefile
>> +++ b/drivers/reset/Makefile
>> @@ -15,6 +15,7 @@ obj-$(CONFIG_RESET_MESON) += reset-meson.o
>>   obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
>>   obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
>>   obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
>> +obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o
>>   obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
>>   obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
>>   obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o
>> diff --git a/drivers/reset/reset-stm32mp1.c b/drivers/reset/reset-stm32mp1.c
>> new file mode 100644
>> index 0000000..5e25388
>> --- /dev/null
>> +++ b/drivers/reset/reset-stm32mp1.c
>> @@ -0,0 +1,122 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
>> + * Author: Gabriel Fernandez <gabriel.fernandez@st.com> for STMicroelectronics.
>> + */
>> +
>> +#include <linux/arm-smccc.h>
> This does not seem to be necessary.
right
>> +#include <linux/device.h>
>> +#include <linux/err.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
> This does not seem to be necessary either.
ok
>> +#include <linux/platform_device.h>
>> +#include <linux/reset-controller.h>
>> +
>> +#define CLR_OFFSET 0x4
>> +
>> +struct stm32_reset_data {
>> +	struct reset_controller_dev	rcdev;
>> +	void __iomem			*membase;
>> +};
>> +
>> +static inline struct stm32_reset_data *
>> +to_stm32_reset_data(struct reset_controller_dev *rcdev)
>> +{
>> +	return container_of(rcdev, struct stm32_reset_data, rcdev);
>> +}
>> +
>> +static int stm32_reset_update(struct reset_controller_dev *rcdev,
>> +			      unsigned long id, bool assert)
>> +{
>> +	struct stm32_reset_data *data = to_stm32_reset_data(rcdev);
>> +	int reg_width = sizeof(u32);
>> +	int bank = id / (reg_width * BITS_PER_BYTE);
>> +	int offset = id % (reg_width * BITS_PER_BYTE);
>> +	void __iomem *addr;
>> +
>> +	addr = data->membase + (bank * reg_width);
>> +	if (!assert)
>> +		addr += CLR_OFFSET;
>> +
>> +	writel(BIT(offset), addr);
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_reset_assert(struct reset_controller_dev *rcdev,
>> +			      unsigned long id)
>> +{
>> +	return stm32_reset_update(rcdev, id, true);
>> +}
>> +
>> +static int stm32_reset_deassert(struct reset_controller_dev *rcdev,
>> +				unsigned long id)
>> +{
>> +	return stm32_reset_update(rcdev, id, false);
>> +}
>> +
>> +static int stm32_reset_status(struct reset_controller_dev *rcdev,
>> +			      unsigned long id)
>> +{
>> +	struct stm32_reset_data *data = to_stm32_reset_data(rcdev);
>> +	int reg_width = sizeof(u32);
>> +	int bank = id / (reg_width * BITS_PER_BYTE);
>> +	int offset = id % (reg_width * BITS_PER_BYTE);
>> +	u32 reg;
>> +
>> +	reg = readl(data->membase + (bank * reg_width));
>> +
>> +	return !(reg & BIT(offset));
>> +}
> So the SET register can be read back and returns 0 for reset lines that
> are currently asserted and returns 1 for reset lines that are currently
> deasserted?
yes you have spotted a error, i will replace by'return !!(reg & 
BIT(offset));'
>> +
>> +const struct reset_control_ops stm32_reset_ops = {
>> +	.assert		= stm32_reset_assert,
>> +	.deassert	= stm32_reset_deassert,
>> +	.status		= stm32_reset_status,
>> +};
>> +
>> +static const struct of_device_id stm32_reset_dt_ids[] = {
>> +	{ .compatible = "st,stm32mp1-rcc"},
>> +	{ /* sentinel */ },
>> +};
>  From the DT bindings it looks like the clock and reset drivers are
> sharing the same node. Is there just no clock platform_driver at all?
there is one hardware block that is used for clock, reset and power, 
they share the same node but each have it own platform_driver

>> +
>> +static int stm32_reset_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct stm32_reset_data *data;
>> +	void __iomem *membase;
>> +	struct resource *res;
>> +
>> +	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>> +	if (!data)
>> +		return -ENOMEM;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	membase = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR(membase))
>> +		return PTR_ERR(membase);
>> +
>> +	data->membase = membase;
>> +	data->rcdev.owner = THIS_MODULE;
>> +	data->rcdev.nr_resets = resource_size(res) * BITS_PER_BYTE;
>> +	data->rcdev.ops = &stm32_reset_ops;
>> +	data->rcdev.of_node = dev->of_node;
>> +
>> +	return devm_reset_controller_register(dev, &data->rcdev);
>> +}
>> +
>> +static struct platform_driver stm32_reset_driver = {
>> +	.probe	= stm32_reset_probe,
>> +	.driver = {
>> +		.name		= "stm32mp1-reset",
>> +		.of_match_table	= stm32_reset_dt_ids,
>> +	},
>> +};
>> +
>> +static int __init stm32_reset_init(void)
>> +{
>> +	return platform_driver_register(&stm32_reset_driver);
>> +}
>> +
>> +postcore_initcall(stm32_reset_init);
> Isn't builtin_platform_driver early enough?
ok

Best Regards

Gabriel

>
> regards
> Philipp
diff mbox

Patch

diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 1efbc6c..c0b292b 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -97,6 +97,12 @@  config RESET_SIMPLE
 	   - Allwinner SoCs
 	   - ZTE's zx2967 family
 
+config RESET_STM32MP157
+	bool "STM32MP157 Reset Driver" if COMPILE_TEST
+	default MACH_STM32MP157
+	help
+	  This enables the RCC reset controller driver for STM32 MPUs.
+
 config RESET_SUNXI
 	bool "Allwinner SoCs Reset Driver" if COMPILE_TEST && !ARCH_SUNXI
 	default ARCH_SUNXI
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 132c24f..c1261dc 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -15,6 +15,7 @@  obj-$(CONFIG_RESET_MESON) += reset-meson.o
 obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
 obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
 obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
+obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o
 obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
 obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
 obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o
diff --git a/drivers/reset/reset-stm32mp1.c b/drivers/reset/reset-stm32mp1.c
new file mode 100644
index 0000000..5e25388
--- /dev/null
+++ b/drivers/reset/reset-stm32mp1.c
@@ -0,0 +1,122 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Gabriel Fernandez <gabriel.fernandez@st.com> for STMicroelectronics.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+
+#define CLR_OFFSET 0x4
+
+struct stm32_reset_data {
+	struct reset_controller_dev	rcdev;
+	void __iomem			*membase;
+};
+
+static inline struct stm32_reset_data *
+to_stm32_reset_data(struct reset_controller_dev *rcdev)
+{
+	return container_of(rcdev, struct stm32_reset_data, rcdev);
+}
+
+static int stm32_reset_update(struct reset_controller_dev *rcdev,
+			      unsigned long id, bool assert)
+{
+	struct stm32_reset_data *data = to_stm32_reset_data(rcdev);
+	int reg_width = sizeof(u32);
+	int bank = id / (reg_width * BITS_PER_BYTE);
+	int offset = id % (reg_width * BITS_PER_BYTE);
+	void __iomem *addr;
+
+	addr = data->membase + (bank * reg_width);
+	if (!assert)
+		addr += CLR_OFFSET;
+
+	writel(BIT(offset), addr);
+
+	return 0;
+}
+
+static int stm32_reset_assert(struct reset_controller_dev *rcdev,
+			      unsigned long id)
+{
+	return stm32_reset_update(rcdev, id, true);
+}
+
+static int stm32_reset_deassert(struct reset_controller_dev *rcdev,
+				unsigned long id)
+{
+	return stm32_reset_update(rcdev, id, false);
+}
+
+static int stm32_reset_status(struct reset_controller_dev *rcdev,
+			      unsigned long id)
+{
+	struct stm32_reset_data *data = to_stm32_reset_data(rcdev);
+	int reg_width = sizeof(u32);
+	int bank = id / (reg_width * BITS_PER_BYTE);
+	int offset = id % (reg_width * BITS_PER_BYTE);
+	u32 reg;
+
+	reg = readl(data->membase + (bank * reg_width));
+
+	return !(reg & BIT(offset));
+}
+
+const struct reset_control_ops stm32_reset_ops = {
+	.assert		= stm32_reset_assert,
+	.deassert	= stm32_reset_deassert,
+	.status		= stm32_reset_status,
+};
+
+static const struct of_device_id stm32_reset_dt_ids[] = {
+	{ .compatible = "st,stm32mp1-rcc"},
+	{ /* sentinel */ },
+};
+
+static int stm32_reset_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct stm32_reset_data *data;
+	void __iomem *membase;
+	struct resource *res;
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	membase = devm_ioremap_resource(dev, res);
+	if (IS_ERR(membase))
+		return PTR_ERR(membase);
+
+	data->membase = membase;
+	data->rcdev.owner = THIS_MODULE;
+	data->rcdev.nr_resets = resource_size(res) * BITS_PER_BYTE;
+	data->rcdev.ops = &stm32_reset_ops;
+	data->rcdev.of_node = dev->of_node;
+
+	return devm_reset_controller_register(dev, &data->rcdev);
+}
+
+static struct platform_driver stm32_reset_driver = {
+	.probe	= stm32_reset_probe,
+	.driver = {
+		.name		= "stm32mp1-reset",
+		.of_match_table	= stm32_reset_dt_ids,
+	},
+};
+
+static int __init stm32_reset_init(void)
+{
+	return platform_driver_register(&stm32_reset_driver);
+}
+
+postcore_initcall(stm32_reset_init);