^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * JZ47xx SoCs TCU Operating System Timer driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2016 Maarten ter Huurne <maarten@treewalker.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Copyright (C) 2020 Paul Cercueil <paul@crapouillou.net>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/clk.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/clocksource.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/mfd/ingenic-tcu.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/mfd/syscon.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/of.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/pm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/regmap.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/sched_clock.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #define TCU_OST_TCSR_MASK 0xffc0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #define TCU_OST_TCSR_CNT_MD BIT(15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #define TCU_OST_CHANNEL 15
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) * The TCU_REG_OST_CNT{L,R} from <linux/mfd/ingenic-tcu.h> are only for the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * regmap; these are for use with the __iomem pointer.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #define OST_REG_CNTL 0x4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #define OST_REG_CNTH 0x8
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) struct ingenic_ost_soc_info {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) bool is64bit;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) struct ingenic_ost {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) void __iomem *regs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) struct clk *clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) struct clocksource cs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) static struct ingenic_ost *ingenic_ost;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) static u64 notrace ingenic_ost_read_cntl(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) /* Read using __iomem pointer instead of regmap to avoid locking */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) return readl(ingenic_ost->regs + OST_REG_CNTL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) static u64 notrace ingenic_ost_read_cnth(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) /* Read using __iomem pointer instead of regmap to avoid locking */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) return readl(ingenic_ost->regs + OST_REG_CNTH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) static u64 notrace ingenic_ost_clocksource_readl(struct clocksource *cs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) return ingenic_ost_read_cntl();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) static u64 notrace ingenic_ost_clocksource_readh(struct clocksource *cs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) return ingenic_ost_read_cnth();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) static int __init ingenic_ost_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) const struct ingenic_ost_soc_info *soc_info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) struct ingenic_ost *ost;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) struct clocksource *cs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) struct regmap *map;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) unsigned long rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) soc_info = device_get_match_data(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) if (!soc_info)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) ost = devm_kzalloc(dev, sizeof(*ost), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) if (!ost)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) ingenic_ost = ost;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) ost->regs = devm_platform_ioremap_resource(pdev, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) if (IS_ERR(ost->regs))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) return PTR_ERR(ost->regs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) map = device_node_to_regmap(dev->parent->of_node);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) if (IS_ERR(map)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) dev_err(dev, "regmap not found");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) return PTR_ERR(map);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) ost->clk = devm_clk_get(dev, "ost");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) if (IS_ERR(ost->clk))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) return PTR_ERR(ost->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) err = clk_prepare_enable(ost->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) /* Clear counter high/low registers */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) if (soc_info->is64bit)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) regmap_write(map, TCU_REG_OST_CNTL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) regmap_write(map, TCU_REG_OST_CNTH, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) /* Don't reset counter at compare value. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) regmap_update_bits(map, TCU_REG_OST_TCSR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) TCU_OST_TCSR_MASK, TCU_OST_TCSR_CNT_MD);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) rate = clk_get_rate(ost->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) /* Enable OST TCU channel */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) regmap_write(map, TCU_REG_TESR, BIT(TCU_OST_CHANNEL));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) cs = &ost->cs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) cs->name = "ingenic-ost";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) cs->rating = 320;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) cs->mask = CLOCKSOURCE_MASK(32);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) if (soc_info->is64bit)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) cs->read = ingenic_ost_clocksource_readl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) cs->read = ingenic_ost_clocksource_readh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) err = clocksource_register_hz(cs, rate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) if (err) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) dev_err(dev, "clocksource registration failed");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) clk_disable_unprepare(ost->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) if (soc_info->is64bit)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) sched_clock_register(ingenic_ost_read_cntl, 32, rate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) sched_clock_register(ingenic_ost_read_cnth, 32, rate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) static int __maybe_unused ingenic_ost_suspend(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) struct ingenic_ost *ost = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) clk_disable(ost->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) static int __maybe_unused ingenic_ost_resume(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) struct ingenic_ost *ost = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) return clk_enable(ost->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) static const struct dev_pm_ops __maybe_unused ingenic_ost_pm_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) /* _noirq: We want the OST clock to be gated last / ungated first */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) .suspend_noirq = ingenic_ost_suspend,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) .resume_noirq = ingenic_ost_resume,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) static const struct ingenic_ost_soc_info jz4725b_ost_soc_info = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) .is64bit = false,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) static const struct ingenic_ost_soc_info jz4770_ost_soc_info = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) .is64bit = true,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) static const struct of_device_id ingenic_ost_of_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) { .compatible = "ingenic,jz4725b-ost", .data = &jz4725b_ost_soc_info, },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) { .compatible = "ingenic,jz4770-ost", .data = &jz4770_ost_soc_info, },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) { }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) static struct platform_driver ingenic_ost_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) .name = "ingenic-ost",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) #ifdef CONFIG_PM_SUSPEND
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) .pm = &ingenic_ost_pm_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) .of_match_table = ingenic_ost_of_match,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) builtin_platform_driver_probe(ingenic_ost_driver, ingenic_ost_probe);