diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2018-11-09 10:55:05 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2018-11-09 10:55:05 +0100 |
commit | 227426f16163d3721fee82650980aae0eef01375 (patch) | |
tree | a64101906c15b814ec942468e88875ce8160e45d /drivers/clocksource | |
parent | c024f90f5dd5604d3f82dbbc055fd88a69d59187 (diff) | |
parent | ac7324a0ed12a67fc836b383388f2562d32e55ed (diff) | |
download | barebox-227426f16163d3721fee82650980aae0eef01375.tar.gz barebox-227426f16163d3721fee82650980aae0eef01375.tar.xz |
Merge branch 'for-next/net-switch-mv88e6xxx'
Diffstat (limited to 'drivers/clocksource')
-rw-r--r-- | drivers/clocksource/Kconfig | 10 | ||||
-rw-r--r-- | drivers/clocksource/Makefile | 2 | ||||
-rw-r--r-- | drivers/clocksource/arm_global_timer.c | 113 | ||||
-rw-r--r-- | drivers/clocksource/timer-imx-gpt.c | 192 |
4 files changed, 317 insertions, 0 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 3d63f7ff16..3caf72503a 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -1,3 +1,6 @@ +config ARCH_HAS_IMX_GPT + bool + config AMBA_SP804 bool depends on ARM_AMBA @@ -70,3 +73,10 @@ config CLOCKSOURCE_ARMV8_TIMER bool default y depends on ARM && CPU_64v8 + +config CLOCKSOURCE_ARM_GLOBAL_TIMER + bool + depends on ARM && CPU_V7 +config CLOCKSOURCE_IMX_GPT + def_bool y + depends on ARCH_HAS_IMX_GPT diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index ea33fff502..ce4d74137a 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -12,3 +12,5 @@ obj-$(CONFIG_CLOCKSOURCE_UEMD) += uemd.o obj-$(CONFIG_CLOCKSOURCE_ROCKCHIP)+= rk_timer.o obj-$(CONFIG_CLOCKSOURCE_ATMEL_PIT) += timer-atmel-pit.o obj-$(CONFIG_CLOCKSOURCE_ARMV8_TIMER) += armv8-timer.o +obj-$(CONFIG_CLOCKSOURCE_ARM_GLOBAL_TIMER) += arm_global_timer.o +obj-$(CONFIG_CLOCKSOURCE_IMX_GPT) += timer-imx-gpt.o diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c new file mode 100644 index 0000000000..44e3a3c762 --- /dev/null +++ b/drivers/clocksource/arm_global_timer.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Clocksource driver for generic Cortex A9 timer block + * + * Copyright (C) 2018 Zodiac Inflight Innovations + * Author: Andrey Smirnov <andrew.smirnov@gmail.com> + * + * based on corresponding driver from Linux kernel with the following + * copyright: + * + * drivers/clocksource/arm_global_timer.c + * + * Copyright (C) 2013 STMicroelectronics (R&D) Limited. + * Author: Stuart Menefy <stuart.menefy@st.com> + * Author: Srinivas Kandagatla <srinivas.kandagatla@st.com> + */ +#include <common.h> +#include <init.h> +#include <clock.h> +#include <linux/clk.h> +#include <io.h> +#include <asm/system.h> + +#define GT_COUNTER0 0x00 +#define GT_COUNTER1 0x04 + +#define GT_CONTROL 0x08 +#define GT_CONTROL_TIMER_ENABLE BIT(0) /* this bit is NOT banked */ + +static void __iomem *gt_base; + +/* + * To get the value from the Global Timer Counter register proceed as follows: + * 1. Read the upper 32-bit timer counter register + * 2. Read the lower 32-bit timer counter register + * 3. Read the upper 32-bit timer counter register again. If the value is + * different to the 32-bit upper value read previously, go back to step 2. + * Otherwise the 64-bit timer counter value is correct. + */ +static uint64_t arm_global_clocksource_read(void) +{ + uint64_t counter; + uint32_t lower; + uint32_t upper, old_upper; + + upper = readl(gt_base + GT_COUNTER1); + do { + old_upper = upper; + lower = readl(gt_base + GT_COUNTER0); + upper = readl(gt_base + GT_COUNTER1); + } while (upper != old_upper); + + counter = upper; + counter <<= 32; + counter |= lower; + return counter; +} + +static struct clocksource cs = { + .read = arm_global_clocksource_read, + .mask = CLOCKSOURCE_MASK(64), + .shift = 0, +}; + +static int arm_global_timer_probe(struct device_d *dev) +{ + struct resource *iores; + struct clk *clk; + int ret; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + clk = clk_get(dev, NULL); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(dev, "clock not found: %d\n", ret); + return ret; + } + + ret = clk_enable(clk); + if (ret) { + dev_err(dev, "clock failed to enable: %d\n", ret); + return ret; + } + + gt_base = IOMEM(iores->start); + + cs.mult = clocksource_hz2mult(clk_get_rate(clk), cs.shift); + + writel(0, gt_base + GT_CONTROL); + writel(0, gt_base + GT_COUNTER0); + writel(0, gt_base + GT_COUNTER1); + /* enables timer on all the cores */ + writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL); + + return init_clock(&cs); +} + +static struct of_device_id arm_global_timer_dt_ids[] = { + { .compatible = "arm,cortex-a9-global-timer", }, + { } +}; + +static struct driver_d arm_global_timer_driver = { + .name = "arm-global-timer", + .probe = arm_global_timer_probe, + .of_compatible = DRV_OF_COMPAT(arm_global_timer_dt_ids), +}; +postcore_platform_driver(arm_global_timer_driver); + diff --git a/drivers/clocksource/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c new file mode 100644 index 0000000000..4d6c6c2b0b --- /dev/null +++ b/drivers/clocksource/timer-imx-gpt.c @@ -0,0 +1,192 @@ +/* + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Alex Zuepke <azu@sysgo.de> + * + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, <gj@denx.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <common.h> +#include <init.h> +#include <clock.h> +#include <errno.h> +#include <of.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <notifier.h> +#include <io.h> + +/* Part 1: Registers */ +# define GPT_TCTL 0x00 +# define GPT_TPRER 0x04 + +/* Part 2: Bitfields */ +#define TCTL_SWR (1 << 15) /* Software reset */ +#define IMX1_TCTL_FRR (1 << 8) /* Freerun / restart */ +#define IMX31_TCTL_FRR (1 << 9) /* Freerun / restart */ +#define IMX1_TCTL_CLKSOURCE_PER (1 << 1) /* Clock source bit position */ +#define IMX31_TCTL_CLKSOURCE_IPG (1 << 6) /* Clock source bit position */ +#define IMX31_TCTL_CLKSOURCE_PER (2 << 6) /* Clock source bit position */ +#define TCTL_TEN (1 << 0) /* Timer enable */ + +static struct clk *clk_gpt; + +struct imx_gpt_regs { + unsigned int tcn; + uint32_t tctl_val; +}; + +static struct imx_gpt_regs regs_imx1 = { + .tcn = 0x10, + .tctl_val = IMX1_TCTL_FRR | IMX1_TCTL_CLKSOURCE_PER | TCTL_TEN, +}; + +static struct imx_gpt_regs regs_imx31 = { + .tcn = 0x24, + .tctl_val = IMX31_TCTL_FRR | IMX31_TCTL_CLKSOURCE_PER | TCTL_TEN, +}; + +static struct imx_gpt_regs *regs; +static void __iomem *timer_base; + +static uint64_t imx_clocksource_read(void) +{ + return readl(timer_base + regs->tcn); +} + +static struct clocksource cs = { + .read = imx_clocksource_read, + .mask = CLOCKSOURCE_MASK(32), + .shift = 10, +}; + +static int imx_clocksource_clock_change(struct notifier_block *nb, unsigned long event, void *data) +{ + cs.mult = clocksource_hz2mult(clk_get_rate(clk_gpt), cs.shift); + return 0; +} + +static struct notifier_block imx_clock_notifier = { + .notifier_call = imx_clocksource_clock_change, +}; + +static int imx_gpt_probe(struct device_d *dev) +{ + struct resource *iores; + int i; + int ret; + unsigned long rate; + + /* one timer is enough */ + if (timer_base) + return 0; + + ret = dev_get_drvdata(dev, (const void **)®s); + if (ret) + return ret; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + timer_base = IOMEM(iores->start); + + /* setup GP Timer 1 */ + writel(TCTL_SWR, timer_base + GPT_TCTL); + + for (i = 0; i < 100; i++) + writel(0, timer_base + GPT_TCTL); /* We have no udelay by now */ + + clk_gpt = clk_get(dev, "per"); + if (IS_ERR(clk_gpt)) { + rate = 20000000; + dev_err(dev, "failed to get clock, assume %lu Hz\n", rate); + } else { + rate = clk_get_rate(clk_gpt); + if (!rate) { + dev_err(dev, "clock reports rate == 0\n"); + return -EIO; + } + } + + writel(0, timer_base + GPT_TPRER); + writel(regs->tctl_val, timer_base + GPT_TCTL); + + cs.mult = clocksource_hz2mult(rate, cs.shift); + + init_clock(&cs); + + clock_register_client(&imx_clock_notifier); + + return 0; +} + +static __maybe_unused struct of_device_id imx_gpt_dt_ids[] = { + { + .compatible = "fsl,imx1-gpt", + .data = ®s_imx1, + }, { + .compatible = "fsl,imx21-gpt", + .data = ®s_imx1, + }, { + .compatible = "fsl,imx31-gpt", + .data = ®s_imx31, + }, { + .compatible = "fsl,imx6q-gpt", + .data = ®s_imx31, + }, { + .compatible = "fsl,imx6dl-gpt", + .data = ®s_imx31, + }, { + .compatible = "fsl,imx6ul-gpt", + .data = ®s_imx31, + }, { + .compatible = "fsl,imx7d-gpt", + .data = ®s_imx31, + }, { + /* sentinel */ + } +}; + +static struct platform_device_id imx_gpt_ids[] = { + { + .name = "imx1-gpt", + .driver_data = (unsigned long)®s_imx1, + }, { + .name = "imx31-gpt", + .driver_data = (unsigned long)®s_imx31, + }, { + /* sentinel */ + }, +}; + +static struct driver_d imx_gpt_driver = { + .name = "imx-gpt", + .probe = imx_gpt_probe, + .of_compatible = DRV_OF_COMPAT(imx_gpt_dt_ids), + .id_table = imx_gpt_ids, +}; + +static int imx_gpt_init(void) +{ + return platform_driver_register(&imx_gpt_driver); +} +postcore_initcall(imx_gpt_init); |