18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Clocksource using the Low Power Timer found in the Low Power Controller (LPC) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 STMicroelectronics – All Rights Reserved 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author(s): Francesco Virlinzi <francesco.virlinzi@st.com> 88c2ecf20Sopenharmony_ci * Ajit Pal Singh <ajitpal.singh@st.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/clk.h> 128c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/of_address.h> 158c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <dt-bindings/mfd/st-lpc.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* Low Power Timer */ 218c2ecf20Sopenharmony_ci#define LPC_LPT_LSB_OFF 0x400 228c2ecf20Sopenharmony_ci#define LPC_LPT_MSB_OFF 0x404 238c2ecf20Sopenharmony_ci#define LPC_LPT_START_OFF 0x408 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic struct st_clksrc_ddata { 268c2ecf20Sopenharmony_ci struct clk *clk; 278c2ecf20Sopenharmony_ci void __iomem *base; 288c2ecf20Sopenharmony_ci} ddata; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic void __init st_clksrc_reset(void) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci writel_relaxed(0, ddata.base + LPC_LPT_START_OFF); 338c2ecf20Sopenharmony_ci writel_relaxed(0, ddata.base + LPC_LPT_MSB_OFF); 348c2ecf20Sopenharmony_ci writel_relaxed(0, ddata.base + LPC_LPT_LSB_OFF); 358c2ecf20Sopenharmony_ci writel_relaxed(1, ddata.base + LPC_LPT_START_OFF); 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic u64 notrace st_clksrc_sched_clock_read(void) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return (u64)readl_relaxed(ddata.base + LPC_LPT_LSB_OFF); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int __init st_clksrc_init(void) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci unsigned long rate; 468c2ecf20Sopenharmony_ci int ret; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci st_clksrc_reset(); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci rate = clk_get_rate(ddata.clk); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci sched_clock_register(st_clksrc_sched_clock_read, 32, rate); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci ret = clocksource_mmio_init(ddata.base + LPC_LPT_LSB_OFF, 558c2ecf20Sopenharmony_ci "clksrc-st-lpc", rate, 300, 32, 568c2ecf20Sopenharmony_ci clocksource_mmio_readl_up); 578c2ecf20Sopenharmony_ci if (ret) { 588c2ecf20Sopenharmony_ci pr_err("clksrc-st-lpc: Failed to register clocksource\n"); 598c2ecf20Sopenharmony_ci return ret; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int __init st_clksrc_setup_clk(struct device_node *np) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct clk *clk; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci clk = of_clk_get(np, 0); 708c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 718c2ecf20Sopenharmony_ci pr_err("clksrc-st-lpc: Failed to get LPC clock\n"); 728c2ecf20Sopenharmony_ci return PTR_ERR(clk); 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (clk_prepare_enable(clk)) { 768c2ecf20Sopenharmony_ci pr_err("clksrc-st-lpc: Failed to enable LPC clock\n"); 778c2ecf20Sopenharmony_ci return -EINVAL; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (!clk_get_rate(clk)) { 818c2ecf20Sopenharmony_ci pr_err("clksrc-st-lpc: Failed to get LPC clock rate\n"); 828c2ecf20Sopenharmony_ci clk_disable_unprepare(clk); 838c2ecf20Sopenharmony_ci return -EINVAL; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci ddata.clk = clk; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int __init st_clksrc_of_register(struct device_node *np) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci int ret; 948c2ecf20Sopenharmony_ci uint32_t mode; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "st,lpc-mode", &mode); 978c2ecf20Sopenharmony_ci if (ret) { 988c2ecf20Sopenharmony_ci pr_err("clksrc-st-lpc: An LPC mode must be provided\n"); 998c2ecf20Sopenharmony_ci return ret; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* LPC can either run as a Clocksource or in RTC or WDT mode */ 1038c2ecf20Sopenharmony_ci if (mode != ST_LPC_MODE_CLKSRC) 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ddata.base = of_iomap(np, 0); 1078c2ecf20Sopenharmony_ci if (!ddata.base) { 1088c2ecf20Sopenharmony_ci pr_err("clksrc-st-lpc: Unable to map iomem\n"); 1098c2ecf20Sopenharmony_ci return -ENXIO; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci ret = st_clksrc_setup_clk(np); 1138c2ecf20Sopenharmony_ci if (ret) { 1148c2ecf20Sopenharmony_ci iounmap(ddata.base); 1158c2ecf20Sopenharmony_ci return ret; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci ret = st_clksrc_init(); 1198c2ecf20Sopenharmony_ci if (ret) { 1208c2ecf20Sopenharmony_ci clk_disable_unprepare(ddata.clk); 1218c2ecf20Sopenharmony_ci clk_put(ddata.clk); 1228c2ecf20Sopenharmony_ci iounmap(ddata.base); 1238c2ecf20Sopenharmony_ci return ret; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci pr_info("clksrc-st-lpc: clocksource initialised - running @ %luHz\n", 1278c2ecf20Sopenharmony_ci clk_get_rate(ddata.clk)); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return ret; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(ddata, "st,stih407-lpc", st_clksrc_of_register); 132