From 61598093bbdd283a7edc367d900f223070ead8d2 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Fri, 10 May 2024 07:43:03 +0000
Subject: [PATCH] add ax88772C AX88772C_eeprom_tools
---
kernel/drivers/clocksource/sh_cmt.c | 172 +++++++++++++++++++++++++++++++++++++-------------------
1 files changed, 113 insertions(+), 59 deletions(-)
diff --git a/kernel/drivers/clocksource/sh_cmt.c b/kernel/drivers/clocksource/sh_cmt.c
index 7a6d4c4..89cddbf 100644
--- a/kernel/drivers/clocksource/sh_cmt.c
+++ b/kernel/drivers/clocksource/sh_cmt.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* SuperH Timer Support - CMT
*
* Copyright (C) 2008 Magnus Damm
- *
- * 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
- *
- * 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 <linux/clk.h>
@@ -21,6 +13,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/module.h>
@@ -32,6 +25,10 @@
#include <linux/sh_timer.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+
+#ifdef CONFIG_SUPERH
+#include <asm/platform_early.h>
+#endif
struct sh_cmt_device;
@@ -120,6 +117,7 @@
void __iomem *mapbase;
struct clk *clk;
unsigned long rate;
+ unsigned int reg_delay;
raw_spinlock_t lock; /* Protect the shared start/stop register */
@@ -239,6 +237,8 @@
#define CMCNT 1 /* channel register */
#define CMCOR 2 /* channel register */
+#define CMCLKE 0x1000 /* CLK Enable Register (R-Car Gen2) */
+
static inline u32 sh_cmt_read_cmstr(struct sh_cmt_channel *ch)
{
if (ch->iostart)
@@ -249,10 +249,17 @@
static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, u32 value)
{
- if (ch->iostart)
- ch->cmt->info->write_control(ch->iostart, 0, value);
- else
- ch->cmt->info->write_control(ch->cmt->mapbase, 0, value);
+ u32 old_value = sh_cmt_read_cmstr(ch);
+
+ if (value != old_value) {
+ if (ch->iostart) {
+ ch->cmt->info->write_control(ch->iostart, 0, value);
+ udelay(ch->cmt->reg_delay);
+ } else {
+ ch->cmt->info->write_control(ch->cmt->mapbase, 0, value);
+ udelay(ch->cmt->reg_delay);
+ }
+ }
}
static inline u32 sh_cmt_read_cmcsr(struct sh_cmt_channel *ch)
@@ -262,7 +269,12 @@
static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch, u32 value)
{
- ch->cmt->info->write_control(ch->ioctrl, CMCSR, value);
+ u32 old_value = sh_cmt_read_cmcsr(ch);
+
+ if (value != old_value) {
+ ch->cmt->info->write_control(ch->ioctrl, CMCSR, value);
+ udelay(ch->cmt->reg_delay);
+ }
}
static inline u32 sh_cmt_read_cmcnt(struct sh_cmt_channel *ch)
@@ -270,14 +282,33 @@
return ch->cmt->info->read_count(ch->ioctrl, CMCNT);
}
-static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, u32 value)
+static inline int sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, u32 value)
{
+ /* Tests showed that we need to wait 3 clocks here */
+ unsigned int cmcnt_delay = DIV_ROUND_UP(3 * ch->cmt->reg_delay, 2);
+ u32 reg;
+
+ if (ch->cmt->info->model > SH_CMT_16BIT) {
+ int ret = read_poll_timeout_atomic(sh_cmt_read_cmcsr, reg,
+ !(reg & SH_CMT32_CMCSR_WRFLG),
+ 1, cmcnt_delay, false, ch);
+ if (ret < 0)
+ return ret;
+ }
+
ch->cmt->info->write_count(ch->ioctrl, CMCNT, value);
+ udelay(cmcnt_delay);
+ return 0;
}
static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch, u32 value)
{
- ch->cmt->info->write_count(ch->ioctrl, CMCOR, value);
+ u32 old_value = ch->cmt->info->read_count(ch->ioctrl, CMCOR);
+
+ if (value != old_value) {
+ ch->cmt->info->write_count(ch->ioctrl, CMCOR, value);
+ udelay(ch->cmt->reg_delay);
+ }
}
static u32 sh_cmt_get_counter(struct sh_cmt_channel *ch, u32 *has_wrapped)
@@ -321,7 +352,7 @@
static int sh_cmt_enable(struct sh_cmt_channel *ch)
{
- int k, ret;
+ int ret;
pm_runtime_get_sync(&ch->cmt->pdev->dev);
dev_pm_syscore_device(&ch->cmt->pdev->dev, true);
@@ -349,26 +380,9 @@
}
sh_cmt_write_cmcor(ch, 0xffffffff);
- sh_cmt_write_cmcnt(ch, 0);
+ ret = sh_cmt_write_cmcnt(ch, 0);
- /*
- * According to the sh73a0 user's manual, as CMCNT can be operated
- * only by the RCLK (Pseudo 32 KHz), there's one restriction on
- * modifying CMCNT register; two RCLK cycles are necessary before
- * this register is either read or any modification of the value
- * it holds is reflected in the LSI's actual operation.
- *
- * While at it, we're supposed to clear out the CMCNT as of this
- * moment, so make sure it's processed properly here. This will
- * take RCLKx2 at maximum.
- */
- for (k = 0; k < 100; k++) {
- if (!sh_cmt_read_cmcnt(ch))
- break;
- udelay(1);
- }
-
- if (sh_cmt_read_cmcnt(ch)) {
+ if (ret || sh_cmt_read_cmcnt(ch)) {
dev_err(&ch->cmt->pdev->dev, "ch%u: cannot clear CMCNT\n",
ch->index);
ret = -ETIMEDOUT;
@@ -668,7 +682,7 @@
return;
sh_cmt_stop(ch, FLAG_CLOCKSOURCE);
- pm_genpd_syscore_poweroff(&ch->cmt->pdev->dev);
+ dev_pm_genpd_suspend(&ch->cmt->pdev->dev);
}
static void sh_cmt_clocksource_resume(struct clocksource *cs)
@@ -678,7 +692,7 @@
if (!ch->cs_enabled)
return;
- pm_genpd_syscore_poweron(&ch->cmt->pdev->dev);
+ dev_pm_genpd_resume(&ch->cmt->pdev->dev);
sh_cmt_start(ch, FLAG_CLOCKSOURCE);
}
@@ -770,7 +784,7 @@
{
struct sh_cmt_channel *ch = ced_to_sh_cmt(ced);
- pm_genpd_syscore_poweroff(&ch->cmt->pdev->dev);
+ dev_pm_genpd_suspend(&ch->cmt->pdev->dev);
clk_unprepare(ch->cmt->clk);
}
@@ -779,7 +793,7 @@
struct sh_cmt_channel *ch = ced_to_sh_cmt(ced);
clk_prepare(ch->cmt->clk);
- pm_genpd_syscore_poweron(&ch->cmt->pdev->dev);
+ dev_pm_genpd_resume(&ch->cmt->pdev->dev);
}
static int sh_cmt_register_clockevent(struct sh_cmt_channel *ch,
@@ -790,11 +804,8 @@
int ret;
irq = platform_get_irq(ch->cmt->pdev, ch->index);
- if (irq < 0) {
- dev_err(&ch->cmt->pdev->dev, "ch%u: failed to get irq\n",
- ch->index);
+ if (irq < 0)
return irq;
- }
ret = request_irq(irq, sh_cmt_interrupt,
IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
@@ -856,6 +867,7 @@
unsigned int hwidx, bool clockevent,
bool clocksource, struct sh_cmt_device *cmt)
{
+ u32 value;
int ret;
/* Skip unused channels. */
@@ -885,6 +897,11 @@
ch->iostart = cmt->mapbase + ch->hwidx * 0x100;
ch->ioctrl = ch->iostart + 0x10;
ch->timer_bit = 0;
+
+ /* Enable the clock supply to the channel */
+ value = ioread32(cmt->mapbase + CMCLKE);
+ value |= BIT(hwidx);
+ iowrite32(value, cmt->mapbase + CMCLKE);
break;
}
@@ -918,7 +935,7 @@
return -ENXIO;
}
- cmt->mapbase = ioremap_nocache(mem->start, resource_size(mem));
+ cmt->mapbase = ioremap(mem->start, resource_size(mem));
if (cmt->mapbase == NULL) {
dev_err(&cmt->pdev->dev, "failed to remap I/O memory\n");
return -ENXIO;
@@ -935,22 +952,48 @@
MODULE_DEVICE_TABLE(platform, sh_cmt_id_table);
static const struct of_device_id sh_cmt_of_table[] __maybe_unused = {
- { .compatible = "renesas,cmt-48", .data = &sh_cmt_info[SH_CMT_48BIT] },
+ {
+ /* deprecated, preserved for backward compatibility */
+ .compatible = "renesas,cmt-48",
+ .data = &sh_cmt_info[SH_CMT_48BIT]
+ },
{
/* deprecated, preserved for backward compatibility */
.compatible = "renesas,cmt-48-gen2",
.data = &sh_cmt_info[SH_CMT0_RCAR_GEN2]
},
- { .compatible = "renesas,rcar-gen2-cmt0", .data = &sh_cmt_info[SH_CMT0_RCAR_GEN2] },
- { .compatible = "renesas,rcar-gen2-cmt1", .data = &sh_cmt_info[SH_CMT1_RCAR_GEN2] },
+ {
+ .compatible = "renesas,r8a7740-cmt1",
+ .data = &sh_cmt_info[SH_CMT_48BIT]
+ },
+ {
+ .compatible = "renesas,sh73a0-cmt1",
+ .data = &sh_cmt_info[SH_CMT_48BIT]
+ },
+ {
+ .compatible = "renesas,rcar-gen2-cmt0",
+ .data = &sh_cmt_info[SH_CMT0_RCAR_GEN2]
+ },
+ {
+ .compatible = "renesas,rcar-gen2-cmt1",
+ .data = &sh_cmt_info[SH_CMT1_RCAR_GEN2]
+ },
+ {
+ .compatible = "renesas,rcar-gen3-cmt0",
+ .data = &sh_cmt_info[SH_CMT0_RCAR_GEN2]
+ },
+ {
+ .compatible = "renesas,rcar-gen3-cmt1",
+ .data = &sh_cmt_info[SH_CMT1_RCAR_GEN2]
+ },
{ }
};
MODULE_DEVICE_TABLE(of, sh_cmt_of_table);
static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
{
- unsigned int mask;
- unsigned int i;
+ unsigned int mask, i;
+ unsigned long rate;
int ret;
cmt->pdev = pdev;
@@ -986,17 +1029,21 @@
if (ret < 0)
goto err_clk_unprepare;
- if (cmt->info->width == 16)
- cmt->rate = clk_get_rate(cmt->clk) / 512;
- else
- cmt->rate = clk_get_rate(cmt->clk) / 8;
+ rate = clk_get_rate(cmt->clk);
+ if (!rate) {
+ ret = -EINVAL;
+ goto err_clk_disable;
+ }
- clk_disable(cmt->clk);
+ /* We shall wait 2 input clks after register writes */
+ if (cmt->info->model >= SH_CMT_48BIT)
+ cmt->reg_delay = DIV_ROUND_UP(2UL * USEC_PER_SEC, rate);
+ cmt->rate = rate / (cmt->info->width == 16 ? 512 : 8);
/* Map the memory resource(s). */
ret = sh_cmt_map_memory(cmt);
if (ret < 0)
- goto err_clk_unprepare;
+ goto err_clk_disable;
/* Allocate and setup the channels. */
cmt->num_channels = hweight8(cmt->hw_channels);
@@ -1024,6 +1071,8 @@
mask &= ~(1 << hwidx);
}
+ clk_disable(cmt->clk);
+
platform_set_drvdata(pdev, cmt);
return 0;
@@ -1031,6 +1080,8 @@
err_unmap:
kfree(cmt->channels);
iounmap(cmt->mapbase);
+err_clk_disable:
+ clk_disable(cmt->clk);
err_clk_unprepare:
clk_unprepare(cmt->clk);
err_clk_put:
@@ -1043,7 +1094,7 @@
struct sh_cmt_device *cmt = platform_get_drvdata(pdev);
int ret;
- if (!is_early_platform_device(pdev)) {
+ if (!is_sh_early_platform_device(pdev)) {
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
}
@@ -1063,7 +1114,7 @@
pm_runtime_idle(&pdev->dev);
return ret;
}
- if (is_early_platform_device(pdev))
+ if (is_sh_early_platform_device(pdev))
return 0;
out:
@@ -1100,7 +1151,10 @@
platform_driver_unregister(&sh_cmt_device_driver);
}
-early_platform_init("earlytimer", &sh_cmt_device_driver);
+#ifdef CONFIG_SUPERH
+sh_early_platform_init("earlytimer", &sh_cmt_device_driver);
+#endif
+
subsys_initcall(sh_cmt_init);
module_exit(sh_cmt_exit);
--
Gitblit v1.6.2