Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clocksource: Add driver for the Ingenic JZ47xx OST

OST is the OS Timer, a 64-bit timer/counter with buffered reading.

SoCs before the JZ4770 had (if any) a 32-bit OST; the JZ4770 and
JZ4780 have a 64-bit OST.

This driver will register both a clocksource and a sched_clock to the
system.

Signed-off-by: Maarten ter Huurne <maarten@treewalker.org>
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
Tested-by: Artur Rojek <contact@artur-rojek.eu>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200212180408.30872-1-paul@crapouillou.net

authored by

Maarten ter Huurne and committed by
Daniel Lezcano
ca7b72b5 5be8badc

+198
+8
drivers/clocksource/Kconfig
··· 697 697 help 698 698 Support for the timer/counter unit of the Ingenic JZ SoCs. 699 699 700 + config INGENIC_OST 701 + bool "Clocksource for Ingenic OS Timer" 702 + depends on MIPS || COMPILE_TEST 703 + depends on COMMON_CLK 704 + select MFD_SYSCON 705 + help 706 + Support for the Operating System Timer of the Ingenic JZ SoCs. 707 + 700 708 config MICROCHIP_PIT64B 701 709 bool "Microchip PIT64B support" 702 710 depends on OF || COMPILE_TEST
+1
drivers/clocksource/Makefile
··· 80 80 obj-$(CONFIG_H8300_TMR8) += h8300_timer8.o 81 81 obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o 82 82 obj-$(CONFIG_H8300_TPU) += h8300_tpu.o 83 + obj-$(CONFIG_INGENIC_OST) += ingenic-ost.o 83 84 obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o 84 85 obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o 85 86 obj-$(CONFIG_X86_NUMACHIP) += numachip.o
+189
drivers/clocksource/ingenic-ost.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * JZ47xx SoCs TCU Operating System Timer driver 4 + * 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 0xffc0 20 + #define TCU_OST_TCSR_CNT_MD BIT(15) 21 + 22 + #define TCU_OST_CHANNEL 15 23 + 24 + /* 25 + * The TCU_REG_OST_CNT{L,R} from <linux/mfd/ingenic-tcu.h> are only for the 26 + * regmap; these are for use with the __iomem pointer. 27 + */ 28 + #define OST_REG_CNTL 0x4 29 + #define OST_REG_CNTH 0x8 30 + 31 + struct ingenic_ost_soc_info { 32 + bool is64bit; 33 + }; 34 + 35 + struct ingenic_ost { 36 + void __iomem *regs; 37 + struct clk *clk; 38 + 39 + struct clocksource cs; 40 + }; 41 + 42 + static struct ingenic_ost *ingenic_ost; 43 + 44 + static 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 + 50 + static 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 + 56 + static u64 notrace ingenic_ost_clocksource_readl(struct clocksource *cs) 57 + { 58 + return ingenic_ost_read_cntl(); 59 + } 60 + 61 + static u64 notrace ingenic_ost_clocksource_readh(struct clocksource *cs) 62 + { 63 + return ingenic_ost_read_cnth(); 64 + } 65 + 66 + static 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 (!map) { 92 + dev_err(dev, "regmap not found"); 93 + return -EINVAL; 94 + } 95 + 96 + ost->clk = devm_clk_get(dev, "ost"); 97 + if (IS_ERR(ost->clk)) 98 + return PTR_ERR(ost->clk); 99 + 100 + err = clk_prepare_enable(ost->clk); 101 + if (err) 102 + return err; 103 + 104 + /* Clear counter high/low registers */ 105 + if (soc_info->is64bit) 106 + regmap_write(map, TCU_REG_OST_CNTL, 0); 107 + regmap_write(map, TCU_REG_OST_CNTH, 0); 108 + 109 + /* Don't reset counter at compare value. */ 110 + regmap_update_bits(map, TCU_REG_OST_TCSR, 111 + TCU_OST_TCSR_MASK, TCU_OST_TCSR_CNT_MD); 112 + 113 + rate = clk_get_rate(ost->clk); 114 + 115 + /* Enable OST TCU channel */ 116 + regmap_write(map, TCU_REG_TESR, BIT(TCU_OST_CHANNEL)); 117 + 118 + cs = &ost->cs; 119 + cs->name = "ingenic-ost"; 120 + cs->rating = 320; 121 + cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; 122 + cs->mask = CLOCKSOURCE_MASK(32); 123 + 124 + if (soc_info->is64bit) 125 + cs->read = ingenic_ost_clocksource_readl; 126 + else 127 + cs->read = ingenic_ost_clocksource_readh; 128 + 129 + err = clocksource_register_hz(cs, rate); 130 + if (err) { 131 + dev_err(dev, "clocksource registration failed"); 132 + clk_disable_unprepare(ost->clk); 133 + return err; 134 + } 135 + 136 + if (soc_info->is64bit) 137 + sched_clock_register(ingenic_ost_read_cntl, 32, rate); 138 + else 139 + sched_clock_register(ingenic_ost_read_cnth, 32, rate); 140 + 141 + return 0; 142 + } 143 + 144 + static int __maybe_unused ingenic_ost_suspend(struct device *dev) 145 + { 146 + struct ingenic_ost *ost = dev_get_drvdata(dev); 147 + 148 + clk_disable(ost->clk); 149 + 150 + return 0; 151 + } 152 + 153 + static int __maybe_unused ingenic_ost_resume(struct device *dev) 154 + { 155 + struct ingenic_ost *ost = dev_get_drvdata(dev); 156 + 157 + return clk_enable(ost->clk); 158 + } 159 + 160 + static const struct dev_pm_ops __maybe_unused ingenic_ost_pm_ops = { 161 + /* _noirq: We want the OST clock to be gated last / ungated first */ 162 + .suspend_noirq = ingenic_ost_suspend, 163 + .resume_noirq = ingenic_ost_resume, 164 + }; 165 + 166 + static const struct ingenic_ost_soc_info jz4725b_ost_soc_info = { 167 + .is64bit = false, 168 + }; 169 + 170 + static const struct ingenic_ost_soc_info jz4770_ost_soc_info = { 171 + .is64bit = true, 172 + }; 173 + 174 + static const struct of_device_id ingenic_ost_of_match[] = { 175 + { .compatible = "ingenic,jz4725b-ost", .data = &jz4725b_ost_soc_info, }, 176 + { .compatible = "ingenic,jz4770-ost", .data = &jz4770_ost_soc_info, }, 177 + { } 178 + }; 179 + 180 + static struct platform_driver ingenic_ost_driver = { 181 + .driver = { 182 + .name = "ingenic-ost", 183 + #ifdef CONFIG_PM_SUSPEND 184 + .pm = &ingenic_ost_pm_ops, 185 + #endif 186 + .of_match_table = ingenic_ost_of_match, 187 + }, 188 + }; 189 + builtin_platform_driver_probe(ingenic_ost_driver, ingenic_ost_probe);