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