18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * JZ47xx SoCs TCU Operating System Timer driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Maarten ter Huurne <maarten@treewalker.org> 68c2ecf20Sopenharmony_ci * Copyright (C) 2020 Paul Cercueil <paul@crapouillou.net> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 118c2ecf20Sopenharmony_ci#include <linux/mfd/ingenic-tcu.h> 128c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/pm.h> 168c2ecf20Sopenharmony_ci#include <linux/regmap.h> 178c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define TCU_OST_TCSR_MASK 0xffc0 208c2ecf20Sopenharmony_ci#define TCU_OST_TCSR_CNT_MD BIT(15) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define TCU_OST_CHANNEL 15 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* 258c2ecf20Sopenharmony_ci * The TCU_REG_OST_CNT{L,R} from <linux/mfd/ingenic-tcu.h> are only for the 268c2ecf20Sopenharmony_ci * regmap; these are for use with the __iomem pointer. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci#define OST_REG_CNTL 0x4 298c2ecf20Sopenharmony_ci#define OST_REG_CNTH 0x8 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct ingenic_ost_soc_info { 328c2ecf20Sopenharmony_ci bool is64bit; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct ingenic_ost { 368c2ecf20Sopenharmony_ci void __iomem *regs; 378c2ecf20Sopenharmony_ci struct clk *clk; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci struct clocksource cs; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic struct ingenic_ost *ingenic_ost; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic u64 notrace ingenic_ost_read_cntl(void) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci /* Read using __iomem pointer instead of regmap to avoid locking */ 478c2ecf20Sopenharmony_ci return readl(ingenic_ost->regs + OST_REG_CNTL); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic u64 notrace ingenic_ost_read_cnth(void) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci /* Read using __iomem pointer instead of regmap to avoid locking */ 538c2ecf20Sopenharmony_ci return readl(ingenic_ost->regs + OST_REG_CNTH); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic u64 notrace ingenic_ost_clocksource_readl(struct clocksource *cs) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci return ingenic_ost_read_cntl(); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic u64 notrace ingenic_ost_clocksource_readh(struct clocksource *cs) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return ingenic_ost_read_cnth(); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int __init ingenic_ost_probe(struct platform_device *pdev) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci const struct ingenic_ost_soc_info *soc_info; 698c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 708c2ecf20Sopenharmony_ci struct ingenic_ost *ost; 718c2ecf20Sopenharmony_ci struct clocksource *cs; 728c2ecf20Sopenharmony_ci struct regmap *map; 738c2ecf20Sopenharmony_ci unsigned long rate; 748c2ecf20Sopenharmony_ci int err; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci soc_info = device_get_match_data(dev); 778c2ecf20Sopenharmony_ci if (!soc_info) 788c2ecf20Sopenharmony_ci return -EINVAL; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci ost = devm_kzalloc(dev, sizeof(*ost), GFP_KERNEL); 818c2ecf20Sopenharmony_ci if (!ost) 828c2ecf20Sopenharmony_ci return -ENOMEM; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci ingenic_ost = ost; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci ost->regs = devm_platform_ioremap_resource(pdev, 0); 878c2ecf20Sopenharmony_ci if (IS_ERR(ost->regs)) 888c2ecf20Sopenharmony_ci return PTR_ERR(ost->regs); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci map = device_node_to_regmap(dev->parent->of_node); 918c2ecf20Sopenharmony_ci if (IS_ERR(map)) { 928c2ecf20Sopenharmony_ci dev_err(dev, "regmap not found"); 938c2ecf20Sopenharmony_ci return PTR_ERR(map); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci ost->clk = devm_clk_get(dev, "ost"); 978c2ecf20Sopenharmony_ci if (IS_ERR(ost->clk)) 988c2ecf20Sopenharmony_ci return PTR_ERR(ost->clk); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci err = clk_prepare_enable(ost->clk); 1018c2ecf20Sopenharmony_ci if (err) 1028c2ecf20Sopenharmony_ci return err; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* Clear counter high/low registers */ 1058c2ecf20Sopenharmony_ci if (soc_info->is64bit) 1068c2ecf20Sopenharmony_ci regmap_write(map, TCU_REG_OST_CNTL, 0); 1078c2ecf20Sopenharmony_ci regmap_write(map, TCU_REG_OST_CNTH, 0); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Don't reset counter at compare value. */ 1108c2ecf20Sopenharmony_ci regmap_update_bits(map, TCU_REG_OST_TCSR, 1118c2ecf20Sopenharmony_ci TCU_OST_TCSR_MASK, TCU_OST_TCSR_CNT_MD); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci rate = clk_get_rate(ost->clk); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* Enable OST TCU channel */ 1168c2ecf20Sopenharmony_ci regmap_write(map, TCU_REG_TESR, BIT(TCU_OST_CHANNEL)); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci cs = &ost->cs; 1198c2ecf20Sopenharmony_ci cs->name = "ingenic-ost"; 1208c2ecf20Sopenharmony_ci cs->rating = 320; 1218c2ecf20Sopenharmony_ci cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; 1228c2ecf20Sopenharmony_ci cs->mask = CLOCKSOURCE_MASK(32); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (soc_info->is64bit) 1258c2ecf20Sopenharmony_ci cs->read = ingenic_ost_clocksource_readl; 1268c2ecf20Sopenharmony_ci else 1278c2ecf20Sopenharmony_ci cs->read = ingenic_ost_clocksource_readh; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci err = clocksource_register_hz(cs, rate); 1308c2ecf20Sopenharmony_ci if (err) { 1318c2ecf20Sopenharmony_ci dev_err(dev, "clocksource registration failed"); 1328c2ecf20Sopenharmony_ci clk_disable_unprepare(ost->clk); 1338c2ecf20Sopenharmony_ci return err; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (soc_info->is64bit) 1378c2ecf20Sopenharmony_ci sched_clock_register(ingenic_ost_read_cntl, 32, rate); 1388c2ecf20Sopenharmony_ci else 1398c2ecf20Sopenharmony_ci sched_clock_register(ingenic_ost_read_cnth, 32, rate); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int __maybe_unused ingenic_ost_suspend(struct device *dev) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct ingenic_ost *ost = dev_get_drvdata(dev); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci clk_disable(ost->clk); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int __maybe_unused ingenic_ost_resume(struct device *dev) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct ingenic_ost *ost = dev_get_drvdata(dev); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return clk_enable(ost->clk); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic const struct dev_pm_ops __maybe_unused ingenic_ost_pm_ops = { 1618c2ecf20Sopenharmony_ci /* _noirq: We want the OST clock to be gated last / ungated first */ 1628c2ecf20Sopenharmony_ci .suspend_noirq = ingenic_ost_suspend, 1638c2ecf20Sopenharmony_ci .resume_noirq = ingenic_ost_resume, 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic const struct ingenic_ost_soc_info jz4725b_ost_soc_info = { 1678c2ecf20Sopenharmony_ci .is64bit = false, 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic const struct ingenic_ost_soc_info jz4770_ost_soc_info = { 1718c2ecf20Sopenharmony_ci .is64bit = true, 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic const struct of_device_id ingenic_ost_of_match[] = { 1758c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4725b-ost", .data = &jz4725b_ost_soc_info, }, 1768c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4770-ost", .data = &jz4770_ost_soc_info, }, 1778c2ecf20Sopenharmony_ci { } 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic struct platform_driver ingenic_ost_driver = { 1818c2ecf20Sopenharmony_ci .driver = { 1828c2ecf20Sopenharmony_ci .name = "ingenic-ost", 1838c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SUSPEND 1848c2ecf20Sopenharmony_ci .pm = &ingenic_ost_pm_ops, 1858c2ecf20Sopenharmony_ci#endif 1868c2ecf20Sopenharmony_ci .of_match_table = ingenic_ost_of_match, 1878c2ecf20Sopenharmony_ci }, 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_cibuiltin_platform_driver_probe(ingenic_ost_driver, ingenic_ost_probe); 190