18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2018-2019 SiFive, Inc. 48c2ecf20Sopenharmony_ci * Wesley Terpstra 58c2ecf20Sopenharmony_ci * Paul Walmsley 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 88c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as 98c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 128c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 138c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 148c2ecf20Sopenharmony_ci * GNU General Public License for more details. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * The FU540 PRCI implements clock and reset control for the SiFive 178c2ecf20Sopenharmony_ci * FU540-C000 chip. This driver assumes that it has sole control 188c2ecf20Sopenharmony_ci * over all PRCI resources. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * This driver is based on the PRCI driver written by Wesley Terpstra: 218c2ecf20Sopenharmony_ci * https://github.com/riscv/riscv-linux/commit/999529edf517ed75b56659d456d221b2ee56bb60 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * References: 248c2ecf20Sopenharmony_ci * - SiFive FU540-C000 manual v1p0, Chapter 7 "Clocking and Reset" 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <dt-bindings/clock/sifive-fu540-prci.h> 288c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 298c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 308c2ecf20Sopenharmony_ci#include <linux/clk/analogbits-wrpll-cln28hpc.h> 318c2ecf20Sopenharmony_ci#include <linux/delay.h> 328c2ecf20Sopenharmony_ci#include <linux/err.h> 338c2ecf20Sopenharmony_ci#include <linux/io.h> 348c2ecf20Sopenharmony_ci#include <linux/module.h> 358c2ecf20Sopenharmony_ci#include <linux/of.h> 368c2ecf20Sopenharmony_ci#include <linux/of_clk.h> 378c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 388c2ecf20Sopenharmony_ci#include <linux/slab.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci * EXPECTED_CLK_PARENT_COUNT: how many parent clocks this driver expects: 428c2ecf20Sopenharmony_ci * hfclk and rtcclk 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci#define EXPECTED_CLK_PARENT_COUNT 2 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * Register offsets and bitmasks 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* COREPLLCFG0 */ 518c2ecf20Sopenharmony_ci#define PRCI_COREPLLCFG0_OFFSET 0x4 528c2ecf20Sopenharmony_ci# define PRCI_COREPLLCFG0_DIVR_SHIFT 0 538c2ecf20Sopenharmony_ci# define PRCI_COREPLLCFG0_DIVR_MASK (0x3f << PRCI_COREPLLCFG0_DIVR_SHIFT) 548c2ecf20Sopenharmony_ci# define PRCI_COREPLLCFG0_DIVF_SHIFT 6 558c2ecf20Sopenharmony_ci# define PRCI_COREPLLCFG0_DIVF_MASK (0x1ff << PRCI_COREPLLCFG0_DIVF_SHIFT) 568c2ecf20Sopenharmony_ci# define PRCI_COREPLLCFG0_DIVQ_SHIFT 15 578c2ecf20Sopenharmony_ci# define PRCI_COREPLLCFG0_DIVQ_MASK (0x7 << PRCI_COREPLLCFG0_DIVQ_SHIFT) 588c2ecf20Sopenharmony_ci# define PRCI_COREPLLCFG0_RANGE_SHIFT 18 598c2ecf20Sopenharmony_ci# define PRCI_COREPLLCFG0_RANGE_MASK (0x7 << PRCI_COREPLLCFG0_RANGE_SHIFT) 608c2ecf20Sopenharmony_ci# define PRCI_COREPLLCFG0_BYPASS_SHIFT 24 618c2ecf20Sopenharmony_ci# define PRCI_COREPLLCFG0_BYPASS_MASK (0x1 << PRCI_COREPLLCFG0_BYPASS_SHIFT) 628c2ecf20Sopenharmony_ci# define PRCI_COREPLLCFG0_FSE_SHIFT 25 638c2ecf20Sopenharmony_ci# define PRCI_COREPLLCFG0_FSE_MASK (0x1 << PRCI_COREPLLCFG0_FSE_SHIFT) 648c2ecf20Sopenharmony_ci# define PRCI_COREPLLCFG0_LOCK_SHIFT 31 658c2ecf20Sopenharmony_ci# define PRCI_COREPLLCFG0_LOCK_MASK (0x1 << PRCI_COREPLLCFG0_LOCK_SHIFT) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* DDRPLLCFG0 */ 688c2ecf20Sopenharmony_ci#define PRCI_DDRPLLCFG0_OFFSET 0xc 698c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG0_DIVR_SHIFT 0 708c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG0_DIVR_MASK (0x3f << PRCI_DDRPLLCFG0_DIVR_SHIFT) 718c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG0_DIVF_SHIFT 6 728c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG0_DIVF_MASK (0x1ff << PRCI_DDRPLLCFG0_DIVF_SHIFT) 738c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG0_DIVQ_SHIFT 15 748c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG0_DIVQ_MASK (0x7 << PRCI_DDRPLLCFG0_DIVQ_SHIFT) 758c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG0_RANGE_SHIFT 18 768c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG0_RANGE_MASK (0x7 << PRCI_DDRPLLCFG0_RANGE_SHIFT) 778c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG0_BYPASS_SHIFT 24 788c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG0_BYPASS_MASK (0x1 << PRCI_DDRPLLCFG0_BYPASS_SHIFT) 798c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG0_FSE_SHIFT 25 808c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG0_FSE_MASK (0x1 << PRCI_DDRPLLCFG0_FSE_SHIFT) 818c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG0_LOCK_SHIFT 31 828c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG0_LOCK_MASK (0x1 << PRCI_DDRPLLCFG0_LOCK_SHIFT) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* DDRPLLCFG1 */ 858c2ecf20Sopenharmony_ci#define PRCI_DDRPLLCFG1_OFFSET 0x10 868c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG1_CKE_SHIFT 24 878c2ecf20Sopenharmony_ci# define PRCI_DDRPLLCFG1_CKE_MASK (0x1 << PRCI_DDRPLLCFG1_CKE_SHIFT) 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* GEMGXLPLLCFG0 */ 908c2ecf20Sopenharmony_ci#define PRCI_GEMGXLPLLCFG0_OFFSET 0x1c 918c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG0_DIVR_SHIFT 0 928c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG0_DIVR_MASK (0x3f << PRCI_GEMGXLPLLCFG0_DIVR_SHIFT) 938c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG0_DIVF_SHIFT 6 948c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG0_DIVF_MASK (0x1ff << PRCI_GEMGXLPLLCFG0_DIVF_SHIFT) 958c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT 15 968c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG0_DIVQ_MASK (0x7 << PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT) 978c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG0_RANGE_SHIFT 18 988c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG0_RANGE_MASK (0x7 << PRCI_GEMGXLPLLCFG0_RANGE_SHIFT) 998c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT 24 1008c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG0_BYPASS_MASK (0x1 << PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT) 1018c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG0_FSE_SHIFT 25 1028c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG0_FSE_MASK (0x1 << PRCI_GEMGXLPLLCFG0_FSE_SHIFT) 1038c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG0_LOCK_SHIFT 31 1048c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG0_LOCK_MASK (0x1 << PRCI_GEMGXLPLLCFG0_LOCK_SHIFT) 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* GEMGXLPLLCFG1 */ 1078c2ecf20Sopenharmony_ci#define PRCI_GEMGXLPLLCFG1_OFFSET 0x20 1088c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG1_CKE_SHIFT 24 1098c2ecf20Sopenharmony_ci# define PRCI_GEMGXLPLLCFG1_CKE_MASK (0x1 << PRCI_GEMGXLPLLCFG1_CKE_SHIFT) 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* CORECLKSEL */ 1128c2ecf20Sopenharmony_ci#define PRCI_CORECLKSEL_OFFSET 0x24 1138c2ecf20Sopenharmony_ci# define PRCI_CORECLKSEL_CORECLKSEL_SHIFT 0 1148c2ecf20Sopenharmony_ci# define PRCI_CORECLKSEL_CORECLKSEL_MASK (0x1 << PRCI_CORECLKSEL_CORECLKSEL_SHIFT) 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* DEVICESRESETREG */ 1178c2ecf20Sopenharmony_ci#define PRCI_DEVICESRESETREG_OFFSET 0x28 1188c2ecf20Sopenharmony_ci# define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT 0 1198c2ecf20Sopenharmony_ci# define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT) 1208c2ecf20Sopenharmony_ci# define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT 1 1218c2ecf20Sopenharmony_ci# define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT) 1228c2ecf20Sopenharmony_ci# define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT 2 1238c2ecf20Sopenharmony_ci# define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT) 1248c2ecf20Sopenharmony_ci# define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT 3 1258c2ecf20Sopenharmony_ci# define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT) 1268c2ecf20Sopenharmony_ci# define PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT 5 1278c2ecf20Sopenharmony_ci# define PRCI_DEVICESRESETREG_GEMGXL_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT) 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* CLKMUXSTATUSREG */ 1308c2ecf20Sopenharmony_ci#define PRCI_CLKMUXSTATUSREG_OFFSET 0x2c 1318c2ecf20Sopenharmony_ci# define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT 1 1328c2ecf20Sopenharmony_ci# define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK (0x1 << PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT) 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* 1358c2ecf20Sopenharmony_ci * Private structures 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/** 1398c2ecf20Sopenharmony_ci * struct __prci_data - per-device-instance data 1408c2ecf20Sopenharmony_ci * @va: base virtual address of the PRCI IP block 1418c2ecf20Sopenharmony_ci * @hw_clks: encapsulates struct clk_hw records 1428c2ecf20Sopenharmony_ci * 1438c2ecf20Sopenharmony_ci * PRCI per-device instance data 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_cistruct __prci_data { 1468c2ecf20Sopenharmony_ci void __iomem *va; 1478c2ecf20Sopenharmony_ci struct clk_hw_onecell_data hw_clks; 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/** 1518c2ecf20Sopenharmony_ci * struct __prci_wrpll_data - WRPLL configuration and integration data 1528c2ecf20Sopenharmony_ci * @c: WRPLL current configuration record 1538c2ecf20Sopenharmony_ci * @enable_bypass: fn ptr to code to bypass the WRPLL (if applicable; else NULL) 1548c2ecf20Sopenharmony_ci * @disable_bypass: fn ptr to code to not bypass the WRPLL (or NULL) 1558c2ecf20Sopenharmony_ci * @cfg0_offs: WRPLL CFG0 register offset (in bytes) from the PRCI base address 1568c2ecf20Sopenharmony_ci * 1578c2ecf20Sopenharmony_ci * @enable_bypass and @disable_bypass are used for WRPLL instances 1588c2ecf20Sopenharmony_ci * that contain a separate external glitchless clock mux downstream 1598c2ecf20Sopenharmony_ci * from the PLL. The WRPLL internal bypass mux is not glitchless. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_cistruct __prci_wrpll_data { 1628c2ecf20Sopenharmony_ci struct wrpll_cfg c; 1638c2ecf20Sopenharmony_ci void (*enable_bypass)(struct __prci_data *pd); 1648c2ecf20Sopenharmony_ci void (*disable_bypass)(struct __prci_data *pd); 1658c2ecf20Sopenharmony_ci u8 cfg0_offs; 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/** 1698c2ecf20Sopenharmony_ci * struct __prci_clock - describes a clock device managed by PRCI 1708c2ecf20Sopenharmony_ci * @name: user-readable clock name string - should match the manual 1718c2ecf20Sopenharmony_ci * @parent_name: parent name for this clock 1728c2ecf20Sopenharmony_ci * @ops: struct clk_ops for the Linux clock framework to use for control 1738c2ecf20Sopenharmony_ci * @hw: Linux-private clock data 1748c2ecf20Sopenharmony_ci * @pwd: WRPLL-specific data, associated with this clock (if not NULL) 1758c2ecf20Sopenharmony_ci * @pd: PRCI-specific data associated with this clock (if not NULL) 1768c2ecf20Sopenharmony_ci * 1778c2ecf20Sopenharmony_ci * PRCI clock data. Used by the PRCI driver to register PRCI-provided 1788c2ecf20Sopenharmony_ci * clocks to the Linux clock infrastructure. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_cistruct __prci_clock { 1818c2ecf20Sopenharmony_ci const char *name; 1828c2ecf20Sopenharmony_ci const char *parent_name; 1838c2ecf20Sopenharmony_ci const struct clk_ops *ops; 1848c2ecf20Sopenharmony_ci struct clk_hw hw; 1858c2ecf20Sopenharmony_ci struct __prci_wrpll_data *pwd; 1868c2ecf20Sopenharmony_ci struct __prci_data *pd; 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci#define clk_hw_to_prci_clock(pwd) container_of(pwd, struct __prci_clock, hw) 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* 1928c2ecf20Sopenharmony_ci * Private functions 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/** 1968c2ecf20Sopenharmony_ci * __prci_readl() - read from a PRCI register 1978c2ecf20Sopenharmony_ci * @pd: PRCI context 1988c2ecf20Sopenharmony_ci * @offs: register offset to read from (in bytes, from PRCI base address) 1998c2ecf20Sopenharmony_ci * 2008c2ecf20Sopenharmony_ci * Read the register located at offset @offs from the base virtual 2018c2ecf20Sopenharmony_ci * address of the PRCI register target described by @pd, and return 2028c2ecf20Sopenharmony_ci * the value to the caller. 2038c2ecf20Sopenharmony_ci * 2048c2ecf20Sopenharmony_ci * Context: Any context. 2058c2ecf20Sopenharmony_ci * 2068c2ecf20Sopenharmony_ci * Return: the contents of the register described by @pd and @offs. 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_cistatic u32 __prci_readl(struct __prci_data *pd, u32 offs) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci return readl_relaxed(pd->va + offs); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void __prci_writel(u32 v, u32 offs, struct __prci_data *pd) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci writel_relaxed(v, pd->va + offs); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* WRPLL-related private functions */ 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci/** 2218c2ecf20Sopenharmony_ci * __prci_wrpll_unpack() - unpack WRPLL configuration registers into parameters 2228c2ecf20Sopenharmony_ci * @c: ptr to a struct wrpll_cfg record to write config into 2238c2ecf20Sopenharmony_ci * @r: value read from the PRCI PLL configuration register 2248c2ecf20Sopenharmony_ci * 2258c2ecf20Sopenharmony_ci * Given a value @r read from an FU540 PRCI PLL configuration register, 2268c2ecf20Sopenharmony_ci * split it into fields and populate it into the WRPLL configuration record 2278c2ecf20Sopenharmony_ci * pointed to by @c. 2288c2ecf20Sopenharmony_ci * 2298c2ecf20Sopenharmony_ci * The COREPLLCFG0 macros are used below, but the other *PLLCFG0 macros 2308c2ecf20Sopenharmony_ci * have the same register layout. 2318c2ecf20Sopenharmony_ci * 2328c2ecf20Sopenharmony_ci * Context: Any context. 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_cistatic void __prci_wrpll_unpack(struct wrpll_cfg *c, u32 r) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci u32 v; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci v = r & PRCI_COREPLLCFG0_DIVR_MASK; 2398c2ecf20Sopenharmony_ci v >>= PRCI_COREPLLCFG0_DIVR_SHIFT; 2408c2ecf20Sopenharmony_ci c->divr = v; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci v = r & PRCI_COREPLLCFG0_DIVF_MASK; 2438c2ecf20Sopenharmony_ci v >>= PRCI_COREPLLCFG0_DIVF_SHIFT; 2448c2ecf20Sopenharmony_ci c->divf = v; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci v = r & PRCI_COREPLLCFG0_DIVQ_MASK; 2478c2ecf20Sopenharmony_ci v >>= PRCI_COREPLLCFG0_DIVQ_SHIFT; 2488c2ecf20Sopenharmony_ci c->divq = v; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci v = r & PRCI_COREPLLCFG0_RANGE_MASK; 2518c2ecf20Sopenharmony_ci v >>= PRCI_COREPLLCFG0_RANGE_SHIFT; 2528c2ecf20Sopenharmony_ci c->range = v; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci c->flags &= (WRPLL_FLAGS_INT_FEEDBACK_MASK | 2558c2ecf20Sopenharmony_ci WRPLL_FLAGS_EXT_FEEDBACK_MASK); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* external feedback mode not supported */ 2588c2ecf20Sopenharmony_ci c->flags |= WRPLL_FLAGS_INT_FEEDBACK_MASK; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci/** 2628c2ecf20Sopenharmony_ci * __prci_wrpll_pack() - pack PLL configuration parameters into a register value 2638c2ecf20Sopenharmony_ci * @c: pointer to a struct wrpll_cfg record containing the PLL's cfg 2648c2ecf20Sopenharmony_ci * 2658c2ecf20Sopenharmony_ci * Using a set of WRPLL configuration values pointed to by @c, 2668c2ecf20Sopenharmony_ci * assemble a PRCI PLL configuration register value, and return it to 2678c2ecf20Sopenharmony_ci * the caller. 2688c2ecf20Sopenharmony_ci * 2698c2ecf20Sopenharmony_ci * Context: Any context. Caller must ensure that the contents of the 2708c2ecf20Sopenharmony_ci * record pointed to by @c do not change during the execution 2718c2ecf20Sopenharmony_ci * of this function. 2728c2ecf20Sopenharmony_ci * 2738c2ecf20Sopenharmony_ci * Returns: a value suitable for writing into a PRCI PLL configuration 2748c2ecf20Sopenharmony_ci * register 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_cistatic u32 __prci_wrpll_pack(const struct wrpll_cfg *c) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci u32 r = 0; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci r |= c->divr << PRCI_COREPLLCFG0_DIVR_SHIFT; 2818c2ecf20Sopenharmony_ci r |= c->divf << PRCI_COREPLLCFG0_DIVF_SHIFT; 2828c2ecf20Sopenharmony_ci r |= c->divq << PRCI_COREPLLCFG0_DIVQ_SHIFT; 2838c2ecf20Sopenharmony_ci r |= c->range << PRCI_COREPLLCFG0_RANGE_SHIFT; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* external feedback mode not supported */ 2868c2ecf20Sopenharmony_ci r |= PRCI_COREPLLCFG0_FSE_MASK; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return r; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci/** 2928c2ecf20Sopenharmony_ci * __prci_wrpll_read_cfg() - read the WRPLL configuration from the PRCI 2938c2ecf20Sopenharmony_ci * @pd: PRCI context 2948c2ecf20Sopenharmony_ci * @pwd: PRCI WRPLL metadata 2958c2ecf20Sopenharmony_ci * 2968c2ecf20Sopenharmony_ci * Read the current configuration of the PLL identified by @pwd from 2978c2ecf20Sopenharmony_ci * the PRCI identified by @pd, and store it into the local configuration 2988c2ecf20Sopenharmony_ci * cache in @pwd. 2998c2ecf20Sopenharmony_ci * 3008c2ecf20Sopenharmony_ci * Context: Any context. Caller must prevent the records pointed to by 3018c2ecf20Sopenharmony_ci * @pd and @pwd from changing during execution. 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_cistatic void __prci_wrpll_read_cfg(struct __prci_data *pd, 3048c2ecf20Sopenharmony_ci struct __prci_wrpll_data *pwd) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci __prci_wrpll_unpack(&pwd->c, __prci_readl(pd, pwd->cfg0_offs)); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci/** 3108c2ecf20Sopenharmony_ci * __prci_wrpll_write_cfg() - write WRPLL configuration into the PRCI 3118c2ecf20Sopenharmony_ci * @pd: PRCI context 3128c2ecf20Sopenharmony_ci * @pwd: PRCI WRPLL metadata 3138c2ecf20Sopenharmony_ci * @c: WRPLL configuration record to write 3148c2ecf20Sopenharmony_ci * 3158c2ecf20Sopenharmony_ci * Write the WRPLL configuration described by @c into the WRPLL 3168c2ecf20Sopenharmony_ci * configuration register identified by @pwd in the PRCI instance 3178c2ecf20Sopenharmony_ci * described by @c. Make a cached copy of the WRPLL's current 3188c2ecf20Sopenharmony_ci * configuration so it can be used by other code. 3198c2ecf20Sopenharmony_ci * 3208c2ecf20Sopenharmony_ci * Context: Any context. Caller must prevent the records pointed to by 3218c2ecf20Sopenharmony_ci * @pd and @pwd from changing during execution. 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_cistatic void __prci_wrpll_write_cfg(struct __prci_data *pd, 3248c2ecf20Sopenharmony_ci struct __prci_wrpll_data *pwd, 3258c2ecf20Sopenharmony_ci struct wrpll_cfg *c) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci __prci_writel(__prci_wrpll_pack(c), pwd->cfg0_offs, pd); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci memcpy(&pwd->c, c, sizeof(*c)); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/* Core clock mux control */ 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci/** 3358c2ecf20Sopenharmony_ci * __prci_coreclksel_use_hfclk() - switch the CORECLK mux to output HFCLK 3368c2ecf20Sopenharmony_ci * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg 3378c2ecf20Sopenharmony_ci * 3388c2ecf20Sopenharmony_ci * Switch the CORECLK mux to the HFCLK input source; return once complete. 3398c2ecf20Sopenharmony_ci * 3408c2ecf20Sopenharmony_ci * Context: Any context. Caller must prevent concurrent changes to the 3418c2ecf20Sopenharmony_ci * PRCI_CORECLKSEL_OFFSET register. 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_cistatic void __prci_coreclksel_use_hfclk(struct __prci_data *pd) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci u32 r; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); 3488c2ecf20Sopenharmony_ci r |= PRCI_CORECLKSEL_CORECLKSEL_MASK; 3498c2ecf20Sopenharmony_ci __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */ 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci/** 3558c2ecf20Sopenharmony_ci * __prci_coreclksel_use_corepll() - switch the CORECLK mux to output COREPLL 3568c2ecf20Sopenharmony_ci * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg 3578c2ecf20Sopenharmony_ci * 3588c2ecf20Sopenharmony_ci * Switch the CORECLK mux to the PLL output clock; return once complete. 3598c2ecf20Sopenharmony_ci * 3608c2ecf20Sopenharmony_ci * Context: Any context. Caller must prevent concurrent changes to the 3618c2ecf20Sopenharmony_ci * PRCI_CORECLKSEL_OFFSET register. 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_cistatic void __prci_coreclksel_use_corepll(struct __prci_data *pd) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci u32 r; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); 3688c2ecf20Sopenharmony_ci r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK; 3698c2ecf20Sopenharmony_ci __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */ 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci/* 3758c2ecf20Sopenharmony_ci * Linux clock framework integration 3768c2ecf20Sopenharmony_ci * 3778c2ecf20Sopenharmony_ci * See the Linux clock framework documentation for more information on 3788c2ecf20Sopenharmony_ci * these functions. 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic unsigned long sifive_fu540_prci_wrpll_recalc_rate(struct clk_hw *hw, 3828c2ecf20Sopenharmony_ci unsigned long parent_rate) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 3858c2ecf20Sopenharmony_ci struct __prci_wrpll_data *pwd = pc->pwd; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return wrpll_calc_output_rate(&pwd->c, parent_rate); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic long sifive_fu540_prci_wrpll_round_rate(struct clk_hw *hw, 3918c2ecf20Sopenharmony_ci unsigned long rate, 3928c2ecf20Sopenharmony_ci unsigned long *parent_rate) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 3958c2ecf20Sopenharmony_ci struct __prci_wrpll_data *pwd = pc->pwd; 3968c2ecf20Sopenharmony_ci struct wrpll_cfg c; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci memcpy(&c, &pwd->c, sizeof(c)); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci wrpll_configure_for_rate(&c, rate, *parent_rate); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return wrpll_calc_output_rate(&c, *parent_rate); 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic int sifive_fu540_prci_wrpll_set_rate(struct clk_hw *hw, 4068c2ecf20Sopenharmony_ci unsigned long rate, 4078c2ecf20Sopenharmony_ci unsigned long parent_rate) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 4108c2ecf20Sopenharmony_ci struct __prci_wrpll_data *pwd = pc->pwd; 4118c2ecf20Sopenharmony_ci struct __prci_data *pd = pc->pd; 4128c2ecf20Sopenharmony_ci int r; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci r = wrpll_configure_for_rate(&pwd->c, rate, parent_rate); 4158c2ecf20Sopenharmony_ci if (r) 4168c2ecf20Sopenharmony_ci return r; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (pwd->enable_bypass) 4198c2ecf20Sopenharmony_ci pwd->enable_bypass(pd); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci __prci_wrpll_write_cfg(pd, pwd, &pwd->c); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci udelay(wrpll_calc_max_lock_us(&pwd->c)); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (pwd->disable_bypass) 4268c2ecf20Sopenharmony_ci pwd->disable_bypass(pd); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic const struct clk_ops sifive_fu540_prci_wrpll_clk_ops = { 4328c2ecf20Sopenharmony_ci .set_rate = sifive_fu540_prci_wrpll_set_rate, 4338c2ecf20Sopenharmony_ci .round_rate = sifive_fu540_prci_wrpll_round_rate, 4348c2ecf20Sopenharmony_ci .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate, 4358c2ecf20Sopenharmony_ci}; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic const struct clk_ops sifive_fu540_prci_wrpll_ro_clk_ops = { 4388c2ecf20Sopenharmony_ci .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate, 4398c2ecf20Sopenharmony_ci}; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/* TLCLKSEL clock integration */ 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic unsigned long sifive_fu540_prci_tlclksel_recalc_rate(struct clk_hw *hw, 4448c2ecf20Sopenharmony_ci unsigned long parent_rate) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 4478c2ecf20Sopenharmony_ci struct __prci_data *pd = pc->pd; 4488c2ecf20Sopenharmony_ci u32 v; 4498c2ecf20Sopenharmony_ci u8 div; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci v = __prci_readl(pd, PRCI_CLKMUXSTATUSREG_OFFSET); 4528c2ecf20Sopenharmony_ci v &= PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK; 4538c2ecf20Sopenharmony_ci div = v ? 1 : 2; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci return div_u64(parent_rate, div); 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic const struct clk_ops sifive_fu540_prci_tlclksel_clk_ops = { 4598c2ecf20Sopenharmony_ci .recalc_rate = sifive_fu540_prci_tlclksel_recalc_rate, 4608c2ecf20Sopenharmony_ci}; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci/* 4638c2ecf20Sopenharmony_ci * PRCI integration data for each WRPLL instance 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic struct __prci_wrpll_data __prci_corepll_data = { 4678c2ecf20Sopenharmony_ci .cfg0_offs = PRCI_COREPLLCFG0_OFFSET, 4688c2ecf20Sopenharmony_ci .enable_bypass = __prci_coreclksel_use_hfclk, 4698c2ecf20Sopenharmony_ci .disable_bypass = __prci_coreclksel_use_corepll, 4708c2ecf20Sopenharmony_ci}; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic struct __prci_wrpll_data __prci_ddrpll_data = { 4738c2ecf20Sopenharmony_ci .cfg0_offs = PRCI_DDRPLLCFG0_OFFSET, 4748c2ecf20Sopenharmony_ci}; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic struct __prci_wrpll_data __prci_gemgxlpll_data = { 4778c2ecf20Sopenharmony_ci .cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET, 4788c2ecf20Sopenharmony_ci}; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci/* 4818c2ecf20Sopenharmony_ci * List of clock controls provided by the PRCI 4828c2ecf20Sopenharmony_ci */ 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic struct __prci_clock __prci_init_clocks[] = { 4858c2ecf20Sopenharmony_ci [PRCI_CLK_COREPLL] = { 4868c2ecf20Sopenharmony_ci .name = "corepll", 4878c2ecf20Sopenharmony_ci .parent_name = "hfclk", 4888c2ecf20Sopenharmony_ci .ops = &sifive_fu540_prci_wrpll_clk_ops, 4898c2ecf20Sopenharmony_ci .pwd = &__prci_corepll_data, 4908c2ecf20Sopenharmony_ci }, 4918c2ecf20Sopenharmony_ci [PRCI_CLK_DDRPLL] = { 4928c2ecf20Sopenharmony_ci .name = "ddrpll", 4938c2ecf20Sopenharmony_ci .parent_name = "hfclk", 4948c2ecf20Sopenharmony_ci .ops = &sifive_fu540_prci_wrpll_ro_clk_ops, 4958c2ecf20Sopenharmony_ci .pwd = &__prci_ddrpll_data, 4968c2ecf20Sopenharmony_ci }, 4978c2ecf20Sopenharmony_ci [PRCI_CLK_GEMGXLPLL] = { 4988c2ecf20Sopenharmony_ci .name = "gemgxlpll", 4998c2ecf20Sopenharmony_ci .parent_name = "hfclk", 5008c2ecf20Sopenharmony_ci .ops = &sifive_fu540_prci_wrpll_clk_ops, 5018c2ecf20Sopenharmony_ci .pwd = &__prci_gemgxlpll_data, 5028c2ecf20Sopenharmony_ci }, 5038c2ecf20Sopenharmony_ci [PRCI_CLK_TLCLK] = { 5048c2ecf20Sopenharmony_ci .name = "tlclk", 5058c2ecf20Sopenharmony_ci .parent_name = "corepll", 5068c2ecf20Sopenharmony_ci .ops = &sifive_fu540_prci_tlclksel_clk_ops, 5078c2ecf20Sopenharmony_ci }, 5088c2ecf20Sopenharmony_ci}; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci/** 5118c2ecf20Sopenharmony_ci * __prci_register_clocks() - register clock controls in the PRCI with Linux 5128c2ecf20Sopenharmony_ci * @dev: Linux struct device * 5138c2ecf20Sopenharmony_ci * 5148c2ecf20Sopenharmony_ci * Register the list of clock controls described in __prci_init_plls[] with 5158c2ecf20Sopenharmony_ci * the Linux clock framework. 5168c2ecf20Sopenharmony_ci * 5178c2ecf20Sopenharmony_ci * Return: 0 upon success or a negative error code upon failure. 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_cistatic int __prci_register_clocks(struct device *dev, struct __prci_data *pd) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci struct clk_init_data init = { }; 5228c2ecf20Sopenharmony_ci struct __prci_clock *pic; 5238c2ecf20Sopenharmony_ci int parent_count, i, r; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci parent_count = of_clk_get_parent_count(dev->of_node); 5268c2ecf20Sopenharmony_ci if (parent_count != EXPECTED_CLK_PARENT_COUNT) { 5278c2ecf20Sopenharmony_ci dev_err(dev, "expected only two parent clocks, found %d\n", 5288c2ecf20Sopenharmony_ci parent_count); 5298c2ecf20Sopenharmony_ci return -EINVAL; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* Register PLLs */ 5338c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(__prci_init_clocks); ++i) { 5348c2ecf20Sopenharmony_ci pic = &__prci_init_clocks[i]; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci init.name = pic->name; 5378c2ecf20Sopenharmony_ci init.parent_names = &pic->parent_name; 5388c2ecf20Sopenharmony_ci init.num_parents = 1; 5398c2ecf20Sopenharmony_ci init.ops = pic->ops; 5408c2ecf20Sopenharmony_ci pic->hw.init = &init; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci pic->pd = pd; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (pic->pwd) 5458c2ecf20Sopenharmony_ci __prci_wrpll_read_cfg(pd, pic->pwd); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci r = devm_clk_hw_register(dev, &pic->hw); 5488c2ecf20Sopenharmony_ci if (r) { 5498c2ecf20Sopenharmony_ci dev_warn(dev, "Failed to register clock %s: %d\n", 5508c2ecf20Sopenharmony_ci init.name, r); 5518c2ecf20Sopenharmony_ci return r; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci r = clk_hw_register_clkdev(&pic->hw, pic->name, dev_name(dev)); 5558c2ecf20Sopenharmony_ci if (r) { 5568c2ecf20Sopenharmony_ci dev_warn(dev, "Failed to register clkdev for %s: %d\n", 5578c2ecf20Sopenharmony_ci init.name, r); 5588c2ecf20Sopenharmony_ci return r; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci pd->hw_clks.hws[i] = &pic->hw; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci pd->hw_clks.num = i; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci r = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, 5678c2ecf20Sopenharmony_ci &pd->hw_clks); 5688c2ecf20Sopenharmony_ci if (r) { 5698c2ecf20Sopenharmony_ci dev_err(dev, "could not add hw_provider: %d\n", r); 5708c2ecf20Sopenharmony_ci return r; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci return 0; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci/* 5778c2ecf20Sopenharmony_ci * Linux device model integration 5788c2ecf20Sopenharmony_ci * 5798c2ecf20Sopenharmony_ci * See the Linux device model documentation for more information about 5808c2ecf20Sopenharmony_ci * these functions. 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_cistatic int sifive_fu540_prci_probe(struct platform_device *pdev) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 5858c2ecf20Sopenharmony_ci struct resource *res; 5868c2ecf20Sopenharmony_ci struct __prci_data *pd; 5878c2ecf20Sopenharmony_ci int r; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci pd = devm_kzalloc(dev, 5908c2ecf20Sopenharmony_ci struct_size(pd, hw_clks.hws, 5918c2ecf20Sopenharmony_ci ARRAY_SIZE(__prci_init_clocks)), 5928c2ecf20Sopenharmony_ci GFP_KERNEL); 5938c2ecf20Sopenharmony_ci if (!pd) 5948c2ecf20Sopenharmony_ci return -ENOMEM; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5978c2ecf20Sopenharmony_ci pd->va = devm_ioremap_resource(dev, res); 5988c2ecf20Sopenharmony_ci if (IS_ERR(pd->va)) 5998c2ecf20Sopenharmony_ci return PTR_ERR(pd->va); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci r = __prci_register_clocks(dev, pd); 6028c2ecf20Sopenharmony_ci if (r) { 6038c2ecf20Sopenharmony_ci dev_err(dev, "could not register clocks: %d\n", r); 6048c2ecf20Sopenharmony_ci return r; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci dev_dbg(dev, "SiFive FU540 PRCI probed\n"); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci return 0; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic const struct of_device_id sifive_fu540_prci_of_match[] = { 6138c2ecf20Sopenharmony_ci { .compatible = "sifive,fu540-c000-prci", }, 6148c2ecf20Sopenharmony_ci {} 6158c2ecf20Sopenharmony_ci}; 6168c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sifive_fu540_prci_of_match); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic struct platform_driver sifive_fu540_prci_driver = { 6198c2ecf20Sopenharmony_ci .driver = { 6208c2ecf20Sopenharmony_ci .name = "sifive-fu540-prci", 6218c2ecf20Sopenharmony_ci .of_match_table = sifive_fu540_prci_of_match, 6228c2ecf20Sopenharmony_ci }, 6238c2ecf20Sopenharmony_ci .probe = sifive_fu540_prci_probe, 6248c2ecf20Sopenharmony_ci}; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic int __init sifive_fu540_prci_init(void) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci return platform_driver_register(&sifive_fu540_prci_driver); 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_cicore_initcall(sifive_fu540_prci_init); 631