[PATCH 1/2] reset: add driver for lpc18xx rgu

Philipp Zabel p.zabel at pengutronix.de
Mon May 4 01:00:27 PDT 2015


Hi Joachim,

thank you for the patch! Some comments below:

Am Dienstag, den 28.04.2015, 00:30 +0200 schrieb Joachim Eastwood:
> Signed-off-by: Joachim Eastwood <manabian at gmail.com>
> ---
>  drivers/reset/Makefile        |   1 +
>  drivers/reset/reset-lpc18xx.c | 260 ++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 261 insertions(+)
>  create mode 100644 drivers/reset/reset-lpc18xx.c
> 
> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
> index 157d421f755b..1d41feeb2dce 100644
> --- a/drivers/reset/Makefile
> +++ b/drivers/reset/Makefile
> @@ -1,4 +1,5 @@
>  obj-$(CONFIG_RESET_CONTROLLER) += core.o
> +obj-$(CONFIG_ARCH_LPC18XX) += reset-lpc18xx.o
>  obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o
>  obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o
>  obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o
> diff --git a/drivers/reset/reset-lpc18xx.c b/drivers/reset/reset-lpc18xx.c
> new file mode 100644
> index 000000000000..9564d78ab9d2
> --- /dev/null
> +++ b/drivers/reset/reset-lpc18xx.c
> @@ -0,0 +1,260 @@
> +/*
> + * Reset driver for NXP LPC18xx/43xx Reset Generation Unit (RGU).
> + *
> + * Copyright (C) 2014 Joachim Eastwood <manabian at gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/reboot.h>
> +#include <linux/reset-controller.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +#include <asm/system_misc.h>

Are all of these necessary? I don't see why of_address.h and
system_misc.h are included, for example.

> +/* LPC18xx RGU registers */
> +#define LPC18XX_RGU_CTRL0		0x100
> +#define LPC18XX_RGU_CTRL1		0x104
> +#define LPC18XX_RGU_ACTIVE_STATUS0	0x150
> +#define LPC18XX_RGU_ACTIVE_STATUS1	0x154
> +
> +#define LPC18XX_RGU_RESETS_PER_REG	32
> +
> +/* Internal reset outputs */
> +#define LPC18XX_RGU_CORE_RST	0
> +#define LPC18XX_RGU_M0SUB_RST	12
> +#define LPC18XX_RGU_M0APP_RST	56

In the LPC18xx User Manual these are marked as reserved, I think using
the LPC43XX_ prefix here could avoid confusion. Or are there also
LPC18xx that have these reset lines?

> +
> +struct lpc18xx_rgu_data {
> +	struct reset_controller_dev rcdev;
> +	struct clk *clk_delay;
> +	struct clk *clk_reg;
> +	void __iomem *base;
> +	spinlock_t lock;
> +	u32 delay_us;
> +};
> +
> +#define to_rgu_data(rcdev) container_of(rcdev, struct lpc18xx_rgu_data, rcdev)
> +
> +static void __iomem *rgu_base;
> +
> +static int lpc18xx_rgu_restart(struct notifier_block *this, unsigned long mode,
> +			       void *cmd)
> +{
> +	writel(BIT(LPC18XX_RGU_CORE_RST), rgu_base + LPC18XX_RGU_CTRL0);
> +	mdelay(2000);
> +
> +	pr_emerg("%s: unable to restart system\n", __func__);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static struct notifier_block lpc18xx_rgu_restart_nb = {
> +	.notifier_call = lpc18xx_rgu_restart,
> +	.priority = 192,
> +};
> +
> +/*
> + * The LPC18xx RGU has mostly self-deasserting resets except for the
> + * two reset lines going to the internal Cortex-M0 cores.
> + *
> + * To prevent the M0 cores from accidentally getting deasserting the

"To prevent the M0 core resets from accidentally getting deasserted" ?

> + * status register must be check and bits in control register set to
> + * preserve the state.
> + */
> +static void lpc18xx_rgu_setclear_reset(struct reset_controller_dev *rcdev,
> +				       unsigned long id, bool set)
> +{
> +	struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev);
> +	u32 stat_offset = LPC18XX_RGU_ACTIVE_STATUS0;
> +	u32 ctrl_offset = LPC18XX_RGU_CTRL0;
> +	unsigned long flags;
> +	u32 stat, rst_bit;
> +
> +	stat_offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32);
> +	ctrl_offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32);
> +	rst_bit = 1 << (id % LPC18XX_RGU_RESETS_PER_REG);
> +
> +	spin_lock_irqsave(&rc->lock, flags);
> +	stat = ~readl(rc->base + stat_offset);
> +	if (set)
> +		writel(stat | rst_bit, rc->base + ctrl_offset);
> +	else
> +		writel(stat & ~rst_bit, rc->base + ctrl_offset);
> +	spin_unlock_irqrestore(&rc->lock, flags);
> +}
> +
> +static int lpc18xx_rgu_reset(struct reset_controller_dev *rcdev,
> +			     unsigned long id)
> +{
> +	struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev);
> +
> +	lpc18xx_rgu_setclear_reset(rcdev, id, true);
> +	udelay(rc->delay_us);
> +
> +	return 0;
> +}

This is missing a deassert for the M0 resets, see below.

> +
> +static int lpc18xx_rgu_assert(struct reset_controller_dev *rcdev,
> +			      unsigned long id)
> +{
> +	return lpc18xx_rgu_reset(rcdev, id);
> +}

lpc18xx_rgu_assert shouldn't include the delay. Could you have
lpc18xx_rgu_assert call lpc18xx_rgu_setclear_reset directly?

Let lpc18xx_rgu_reset then call lpc18xx_rgu_assert, udelay, and
lpc18xx_rgu_deassert. Or could you wait for the active status bit to
clear instead of the fixed delay for the self-clearing resets?

> +/* Only M0 cores require explicit reset deassert */
> +static int lpc18xx_rgu_deassert(struct reset_controller_dev *rcdev,
> +				unsigned long id)
> +{
> +	switch (id) {
> +	case LPC18XX_RGU_M0SUB_RST:
> +	case LPC18XX_RGU_M0APP_RST:
> +		lpc18xx_rgu_setclear_reset(rcdev, id, false);
> +	}
> +
> +	return 0;
> +}

If writing 0 to the self-clearing reset bits does nothing,
lpc18xx_rgu_setclear_reset(... false) could be called unconditionally.

[...]

regards
Philipp




More information about the linux-arm-kernel mailing list