Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * JZ47xx SoCs TCU Operating System Timer driver
0004  *
0005  * Copyright (C) 2016 Maarten ter Huurne <maarten@treewalker.org>
0006  * Copyright (C) 2020 Paul Cercueil <paul@crapouillou.net>
0007  */
0008 
0009 #include <linux/clk.h>
0010 #include <linux/clocksource.h>
0011 #include <linux/mfd/ingenic-tcu.h>
0012 #include <linux/mfd/syscon.h>
0013 #include <linux/of.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/pm.h>
0016 #include <linux/regmap.h>
0017 #include <linux/sched_clock.h>
0018 
0019 #define TCU_OST_TCSR_MASK   0xffc0
0020 #define TCU_OST_TCSR_CNT_MD BIT(15)
0021 
0022 #define TCU_OST_CHANNEL     15
0023 
0024 /*
0025  * The TCU_REG_OST_CNT{L,R} from <linux/mfd/ingenic-tcu.h> are only for the
0026  * regmap; these are for use with the __iomem pointer.
0027  */
0028 #define OST_REG_CNTL        0x4
0029 #define OST_REG_CNTH        0x8
0030 
0031 struct ingenic_ost_soc_info {
0032     bool is64bit;
0033 };
0034 
0035 struct ingenic_ost {
0036     void __iomem *regs;
0037     struct clk *clk;
0038 
0039     struct clocksource cs;
0040 };
0041 
0042 static struct ingenic_ost *ingenic_ost;
0043 
0044 static u64 notrace ingenic_ost_read_cntl(void)
0045 {
0046     /* Read using __iomem pointer instead of regmap to avoid locking */
0047     return readl(ingenic_ost->regs + OST_REG_CNTL);
0048 }
0049 
0050 static u64 notrace ingenic_ost_read_cnth(void)
0051 {
0052     /* Read using __iomem pointer instead of regmap to avoid locking */
0053     return readl(ingenic_ost->regs + OST_REG_CNTH);
0054 }
0055 
0056 static u64 notrace ingenic_ost_clocksource_readl(struct clocksource *cs)
0057 {
0058     return ingenic_ost_read_cntl();
0059 }
0060 
0061 static u64 notrace ingenic_ost_clocksource_readh(struct clocksource *cs)
0062 {
0063     return ingenic_ost_read_cnth();
0064 }
0065 
0066 static int __init ingenic_ost_probe(struct platform_device *pdev)
0067 {
0068     const struct ingenic_ost_soc_info *soc_info;
0069     struct device *dev = &pdev->dev;
0070     struct ingenic_ost *ost;
0071     struct clocksource *cs;
0072     struct regmap *map;
0073     unsigned long rate;
0074     int err;
0075 
0076     soc_info = device_get_match_data(dev);
0077     if (!soc_info)
0078         return -EINVAL;
0079 
0080     ost = devm_kzalloc(dev, sizeof(*ost), GFP_KERNEL);
0081     if (!ost)
0082         return -ENOMEM;
0083 
0084     ingenic_ost = ost;
0085 
0086     ost->regs = devm_platform_ioremap_resource(pdev, 0);
0087     if (IS_ERR(ost->regs))
0088         return PTR_ERR(ost->regs);
0089 
0090     map = device_node_to_regmap(dev->parent->of_node);
0091     if (IS_ERR(map)) {
0092         dev_err(dev, "regmap not found");
0093         return PTR_ERR(map);
0094     }
0095 
0096     ost->clk = devm_clk_get(dev, "ost");
0097     if (IS_ERR(ost->clk))
0098         return PTR_ERR(ost->clk);
0099 
0100     err = clk_prepare_enable(ost->clk);
0101     if (err)
0102         return err;
0103 
0104     /* Clear counter high/low registers */
0105     if (soc_info->is64bit)
0106         regmap_write(map, TCU_REG_OST_CNTL, 0);
0107     regmap_write(map, TCU_REG_OST_CNTH, 0);
0108 
0109     /* Don't reset counter at compare value. */
0110     regmap_update_bits(map, TCU_REG_OST_TCSR,
0111                TCU_OST_TCSR_MASK, TCU_OST_TCSR_CNT_MD);
0112 
0113     rate = clk_get_rate(ost->clk);
0114 
0115     /* Enable OST TCU channel */
0116     regmap_write(map, TCU_REG_TESR, BIT(TCU_OST_CHANNEL));
0117 
0118     cs = &ost->cs;
0119     cs->name    = "ingenic-ost";
0120     cs->rating  = 320;
0121     cs->flags   = CLOCK_SOURCE_IS_CONTINUOUS;
0122     cs->mask    = CLOCKSOURCE_MASK(32);
0123 
0124     if (soc_info->is64bit)
0125         cs->read = ingenic_ost_clocksource_readl;
0126     else
0127         cs->read = ingenic_ost_clocksource_readh;
0128 
0129     err = clocksource_register_hz(cs, rate);
0130     if (err) {
0131         dev_err(dev, "clocksource registration failed");
0132         clk_disable_unprepare(ost->clk);
0133         return err;
0134     }
0135 
0136     if (soc_info->is64bit)
0137         sched_clock_register(ingenic_ost_read_cntl, 32, rate);
0138     else
0139         sched_clock_register(ingenic_ost_read_cnth, 32, rate);
0140 
0141     return 0;
0142 }
0143 
0144 static int __maybe_unused ingenic_ost_suspend(struct device *dev)
0145 {
0146     struct ingenic_ost *ost = dev_get_drvdata(dev);
0147 
0148     clk_disable(ost->clk);
0149 
0150     return 0;
0151 }
0152 
0153 static int __maybe_unused ingenic_ost_resume(struct device *dev)
0154 {
0155     struct ingenic_ost *ost = dev_get_drvdata(dev);
0156 
0157     return clk_enable(ost->clk);
0158 }
0159 
0160 static const struct dev_pm_ops __maybe_unused ingenic_ost_pm_ops = {
0161     /* _noirq: We want the OST clock to be gated last / ungated first */
0162     .suspend_noirq = ingenic_ost_suspend,
0163     .resume_noirq  = ingenic_ost_resume,
0164 };
0165 
0166 static const struct ingenic_ost_soc_info jz4725b_ost_soc_info = {
0167     .is64bit = false,
0168 };
0169 
0170 static const struct ingenic_ost_soc_info jz4760b_ost_soc_info = {
0171     .is64bit = true,
0172 };
0173 
0174 static const struct of_device_id ingenic_ost_of_match[] = {
0175     { .compatible = "ingenic,jz4725b-ost", .data = &jz4725b_ost_soc_info, },
0176     { .compatible = "ingenic,jz4760b-ost", .data = &jz4760b_ost_soc_info, },
0177     { .compatible = "ingenic,jz4770-ost", .data = &jz4760b_ost_soc_info, },
0178     { }
0179 };
0180 
0181 static struct platform_driver ingenic_ost_driver = {
0182     .driver = {
0183         .name = "ingenic-ost",
0184 #ifdef CONFIG_PM_SUSPEND
0185         .pm = &ingenic_ost_pm_ops,
0186 #endif
0187         .of_match_table = ingenic_ost_of_match,
0188     },
0189 };
0190 builtin_platform_driver_probe(ingenic_ost_driver, ingenic_ost_probe);