[PATCH] timer: mchp-pit64b: add support for pit64b
Sean Anderson
seanga2 at gmail.com
Wed Oct 7 19:49:50 CEST 2020
On 9/7/20 11:36 AM, Claudiu Beznea wrote:
> Add support for Microchip PIT64B timer. The timer is 64 bit length and
> is used as a free running counter (in continuous mode with highest values
> for period registers). The clock feeding the timer would be no more
> than 12.5MHz.
>
> Signed-off-by: Claudiu Beznea <claudiu.beznea at microchip.com>
> ---
> drivers/timer/Kconfig | 7 +++
> drivers/timer/Makefile | 1 +
> drivers/timer/mchp-pit64b-timer.c | 109 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 117 insertions(+)
> create mode 100644 drivers/timer/mchp-pit64b-timer.c
>
> diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig
> index 5f4bc6edb67b..13a98be4ab92 100644
> --- a/drivers/timer/Kconfig
> +++ b/drivers/timer/Kconfig
> @@ -181,4 +181,11 @@ config MTK_TIMER
> Select this to enable support for the timer found on
> MediaTek devices.
>
> +config MCHP_PIT64B_TIMER
> + bool "Microchip 64-bit periodic interval timer support"
> + depends on TIMER
> + help
> + Select this to enable support for Microchip 64-bit periodic
> + interval timer.
> +
> endmenu
> diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile
> index fa35bea6c5b2..4744a8d9c93c 100644
> --- a/drivers/timer/Makefile
> +++ b/drivers/timer/Makefile
> @@ -21,3 +21,4 @@ obj-$(CONFIG_STI_TIMER) += sti-timer.o
> obj-$(CONFIG_STM32_TIMER) += stm32_timer.o
> obj-$(CONFIG_X86_TSC_TIMER) += tsc_timer.o
> obj-$(CONFIG_MTK_TIMER) += mtk_timer.o
> +obj-$(CONFIG_MCHP_PIT64B_TIMER) += mchp-pit64b-timer.o
> diff --git a/drivers/timer/mchp-pit64b-timer.c b/drivers/timer/mchp-pit64b-timer.c
> new file mode 100644
> index 000000000000..ead8c9b84ad5
> --- /dev/null
> +++ b/drivers/timer/mchp-pit64b-timer.c
> @@ -0,0 +1,109 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * 64-bit Periodic Interval Timer driver
> + *
> + * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
> + *
> + * Author: Claudiu Beznea <claudiu.beznea at microchip.com>
> + */
> +
> +#include <common.h>
> +#include <clk.h>
> +#include <dm.h>
> +#include <timer.h>
> +#include <asm/io.h>
> +
> +#define MCHP_PIT64B_CR 0x00 /* Control Register */
> +#define MCHP_PIT64B_CR_START BIT(0)
> +#define MCHP_PIT64B_CR_SWRST BIT(8)
> +#define MCHP_PIT64B_MR 0x04 /* Mode Register */
> +#define MCHP_PIT64B_MR_CONT BIT(0)
> +#define MCHP_PIT64B_LSB_PR 0x08 /* LSB Period Register */
> +#define MCHP_PIT64B_MSB_PR 0x0C /* MSB Period Register */
> +#define MCHP_PIT64B_TLSBR 0x20 /* Timer LSB Register */
> +#define MCHP_PIT64B_TMSBR 0x24 /* Timer MSB Register */
> +
> +struct mchp_pit64b_priv {
> + void __iomem *base;
> +};
> +
> +static int mchp_pit64b_get_count(struct udevice *dev, u64 *count)
> +{
> + struct mchp_pit64b_priv *priv = dev_get_priv(dev);
> +
> + u32 lsb = readl(priv->base + MCHP_PIT64B_TLSBR);
> + u32 msb = readl(priv->base + MCHP_PIT64B_TMSBR);
Is there a chance of rollover here? E.g. lets say the 64-bit counter is
0x0000_0000_FFFF_FFFF when we read lsb, but it increments to
0x0000_0001_0000_0000 when we read msb. Then the next time we read the
timer, it will look like we jumped backward in time.
One way to get around this is by doing something like
do {
msb = readl(priv->base + MCHP_PIT64B_TMSBR);
lsb = readl(priv->base + MCHP_PIT64B_TLSBR);
} while (msb != readl(priv->base + MCHP_PIT64B_TMSBR));
That way, if we ever roll-over between the two reads, we just redo the
lsb read. This is the method used by drivers/timer/riscv_timer.c on
32-bit systems.
--Sean
> +
> + *count = ((u64)msb << 32) | lsb;
> +
> + return 0;
> +}
> +
> +static int mchp_pit64b_probe(struct udevice *dev)
> +{
> + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> + struct mchp_pit64b_priv *priv = dev_get_priv(dev);
> + struct clk clk;
> + ulong rate;
> + int ret;
> +
> + priv->base = dev_read_addr_ptr(dev);
> + if (IS_ERR(priv->base))
> + return PTR_ERR(priv->base);
> +
> + ret = clk_get_by_index(dev, 0, &clk);
> + if (ret)
> + return ret;
> +
> + ret = clk_enable(&clk);
> + if (ret)
> + return ret;
> +
> + rate = clk_get_rate(&clk);
> + if (!rate) {
> + clk_disable(&clk);
> + return -ENOTSUPP;
> + }
> +
> + /* Reset the timer in case it was used by previous bootloaders. */
> + writel(MCHP_PIT64B_CR_SWRST, priv->base + MCHP_PIT64B_CR);
> +
> + /*
> + * Use highest prescaller (for a peripheral clock running at 200MHz
> + * this will lead to the timer running at 12.5MHz) and continuous mode.
> + */
> + writel((15 << 8) | MCHP_PIT64B_MR_CONT, priv->base + MCHP_PIT64B_MR);
> + uc_priv->clock_rate = rate / 16;
> +
> + /*
> + * Simulate free running counter by setting max values to period
> + * registers.
> + */
> + writel(~0UL, priv->base + MCHP_PIT64B_MSB_PR);
> + writel(~0UL, priv->base + MCHP_PIT64B_LSB_PR);
> +
> + /* Start the timer. */
> + writel(MCHP_PIT64B_CR_START, priv->base + MCHP_PIT64B_CR);
> +
> + return 0;
> +}
> +
> +static const struct timer_ops mchp_pit64b_ops = {
> + .get_count = mchp_pit64b_get_count,
> +};
> +
> +static const struct udevice_id mchp_pit64b_ids[] = {
> + { .compatible = "microchip,sam9x60-pit64b", },
> + { .compatible = "microchip,sama7g5-pit64b", },
> + { }
> +};
> +
> +U_BOOT_DRIVER(mchp_pit64b) = {
> + .name = "mchp-pit64b",
> + .id = UCLASS_TIMER,
> + .of_match = mchp_pit64b_ids,
> + .priv_auto_alloc_size = sizeof(struct mchp_pit64b_priv),
> + .probe = mchp_pit64b_probe,
> + .ops = &mchp_pit64b_ops,
> + .flags = DM_FLAG_PRE_RELOC,
> +};
>
More information about the U-Boot
mailing list