18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for the ICST307 VCO clock found in the ARM Reference designs. 48c2ecf20Sopenharmony_ci * We wrap the custom interface from <asm/hardware/icst.h> into the generic 58c2ecf20Sopenharmony_ci * clock framework. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2012-2015 Linus Walleij 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * TODO: when all ARM reference designs are migrated to generic clocks, the 108c2ecf20Sopenharmony_ci * ICST clock code from the ARM tree should probably be merged into this 118c2ecf20Sopenharmony_ci * file. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/export.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <linux/regmap.h> 208c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "icst.h" 238c2ecf20Sopenharmony_ci#include "clk-icst.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Magic unlocking token used on all Versatile boards */ 268c2ecf20Sopenharmony_ci#define VERSATILE_LOCK_VAL 0xA05F 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define VERSATILE_AUX_OSC_BITS 0x7FFFF 298c2ecf20Sopenharmony_ci#define INTEGRATOR_AP_CM_BITS 0xFF 308c2ecf20Sopenharmony_ci#define INTEGRATOR_AP_SYS_BITS 0xFF 318c2ecf20Sopenharmony_ci#define INTEGRATOR_CP_CM_CORE_BITS 0x7FF 328c2ecf20Sopenharmony_ci#define INTEGRATOR_CP_CM_MEM_BITS 0x7FF000 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define INTEGRATOR_AP_PCI_25_33_MHZ BIT(8) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/** 378c2ecf20Sopenharmony_ci * struct clk_icst - ICST VCO clock wrapper 388c2ecf20Sopenharmony_ci * @hw: corresponding clock hardware entry 398c2ecf20Sopenharmony_ci * @vcoreg: VCO register address 408c2ecf20Sopenharmony_ci * @lockreg: VCO lock register address 418c2ecf20Sopenharmony_ci * @params: parameters for this ICST instance 428c2ecf20Sopenharmony_ci * @rate: current rate 438c2ecf20Sopenharmony_ci * @ctype: the type of control register for the ICST 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistruct clk_icst { 468c2ecf20Sopenharmony_ci struct clk_hw hw; 478c2ecf20Sopenharmony_ci struct regmap *map; 488c2ecf20Sopenharmony_ci u32 vcoreg_off; 498c2ecf20Sopenharmony_ci u32 lockreg_off; 508c2ecf20Sopenharmony_ci struct icst_params *params; 518c2ecf20Sopenharmony_ci unsigned long rate; 528c2ecf20Sopenharmony_ci enum icst_control_type ctype; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define to_icst(_hw) container_of(_hw, struct clk_icst, hw) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/** 588c2ecf20Sopenharmony_ci * vco_get() - get ICST VCO settings from a certain ICST 598c2ecf20Sopenharmony_ci * @icst: the ICST clock to get 608c2ecf20Sopenharmony_ci * @vco: the VCO struct to return the value in 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_cistatic int vco_get(struct clk_icst *icst, struct icst_vco *vco) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci u32 val; 658c2ecf20Sopenharmony_ci int ret; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci ret = regmap_read(icst->map, icst->vcoreg_off, &val); 688c2ecf20Sopenharmony_ci if (ret) 698c2ecf20Sopenharmony_ci return ret; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* 728c2ecf20Sopenharmony_ci * The Integrator/AP core clock can only access the low eight 738c2ecf20Sopenharmony_ci * bits of the v PLL divider. Bit 8 is tied low and always zero, 748c2ecf20Sopenharmony_ci * r is hardwired to 22 and output divider s is hardwired to 1 758c2ecf20Sopenharmony_ci * (divide by 2) according to the document 768c2ecf20Sopenharmony_ci * "Integrator CM926EJ-S, CM946E-S, CM966E-S, CM1026EJ-S and 778c2ecf20Sopenharmony_ci * CM1136JF-S User Guide" ARM DUI 0138E, page 3-13 thru 3-14. 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_AP_CM) { 808c2ecf20Sopenharmony_ci vco->v = val & INTEGRATOR_AP_CM_BITS; 818c2ecf20Sopenharmony_ci vco->r = 22; 828c2ecf20Sopenharmony_ci vco->s = 1; 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* 878c2ecf20Sopenharmony_ci * The Integrator/AP system clock on the base board can only 888c2ecf20Sopenharmony_ci * access the low eight bits of the v PLL divider. Bit 8 is tied low 898c2ecf20Sopenharmony_ci * and always zero, r is hardwired to 46, and the output divider is 908c2ecf20Sopenharmony_ci * hardwired to 3 (divide by 4) according to the document 918c2ecf20Sopenharmony_ci * "Integrator AP ASIC Development Motherboard" ARM DUI 0098B, 928c2ecf20Sopenharmony_ci * page 3-16. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_AP_SYS) { 958c2ecf20Sopenharmony_ci vco->v = val & INTEGRATOR_AP_SYS_BITS; 968c2ecf20Sopenharmony_ci vco->r = 46; 978c2ecf20Sopenharmony_ci vco->s = 3; 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* 1028c2ecf20Sopenharmony_ci * The Integrator/AP PCI clock is using an odd pattern to create 1038c2ecf20Sopenharmony_ci * the child clock, basically a single bit called DIVX/Y is used 1048c2ecf20Sopenharmony_ci * to select between two different hardwired values: setting the 1058c2ecf20Sopenharmony_ci * bit to 0 yields v = 17, r = 22 and OD = 1, whereas setting the 1068c2ecf20Sopenharmony_ci * bit to 1 yields v = 14, r = 14 and OD = 1 giving the frequencies 1078c2ecf20Sopenharmony_ci * 33 or 25 MHz respectively. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_AP_PCI) { 1108c2ecf20Sopenharmony_ci bool divxy = !!(val & INTEGRATOR_AP_PCI_25_33_MHZ); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci vco->v = divxy ? 17 : 14; 1138c2ecf20Sopenharmony_ci vco->r = divxy ? 22 : 14; 1148c2ecf20Sopenharmony_ci vco->s = 1; 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* 1198c2ecf20Sopenharmony_ci * The Integrator/CP core clock can access the low eight bits 1208c2ecf20Sopenharmony_ci * of the v PLL divider. Bit 8 is tied low and always zero, 1218c2ecf20Sopenharmony_ci * r is hardwired to 22 and the output divider s is accessible 1228c2ecf20Sopenharmony_ci * in bits 8 thru 10 according to the document 1238c2ecf20Sopenharmony_ci * "Integrator/CM940T, CM920T, CM740T, and CM720T User Guide" 1248c2ecf20Sopenharmony_ci * ARM DUI 0157A, page 3-20 thru 3-23 and 4-10. 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_CP_CM_CORE) { 1278c2ecf20Sopenharmony_ci vco->v = val & 0xFF; 1288c2ecf20Sopenharmony_ci vco->r = 22; 1298c2ecf20Sopenharmony_ci vco->s = (val >> 8) & 7; 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_CP_CM_MEM) { 1348c2ecf20Sopenharmony_ci vco->v = (val >> 12) & 0xFF; 1358c2ecf20Sopenharmony_ci vco->r = 22; 1368c2ecf20Sopenharmony_ci vco->s = (val >> 20) & 7; 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci vco->v = val & 0x1ff; 1418c2ecf20Sopenharmony_ci vco->r = (val >> 9) & 0x7f; 1428c2ecf20Sopenharmony_ci vco->s = (val >> 16) & 03; 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/** 1478c2ecf20Sopenharmony_ci * vco_set() - commit changes to an ICST VCO 1488c2ecf20Sopenharmony_ci * @icst: the ICST clock to set 1498c2ecf20Sopenharmony_ci * @vco: the VCO struct to set the changes from 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic int vco_set(struct clk_icst *icst, struct icst_vco vco) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci u32 mask; 1548c2ecf20Sopenharmony_ci u32 val; 1558c2ecf20Sopenharmony_ci int ret; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* Mask the bits used by the VCO */ 1588c2ecf20Sopenharmony_ci switch (icst->ctype) { 1598c2ecf20Sopenharmony_ci case ICST_INTEGRATOR_AP_CM: 1608c2ecf20Sopenharmony_ci mask = INTEGRATOR_AP_CM_BITS; 1618c2ecf20Sopenharmony_ci val = vco.v & 0xFF; 1628c2ecf20Sopenharmony_ci if (vco.v & 0x100) 1638c2ecf20Sopenharmony_ci pr_err("ICST error: tried to set bit 8 of VDW\n"); 1648c2ecf20Sopenharmony_ci if (vco.s != 1) 1658c2ecf20Sopenharmony_ci pr_err("ICST error: tried to use VOD != 1\n"); 1668c2ecf20Sopenharmony_ci if (vco.r != 22) 1678c2ecf20Sopenharmony_ci pr_err("ICST error: tried to use RDW != 22\n"); 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci case ICST_INTEGRATOR_AP_SYS: 1708c2ecf20Sopenharmony_ci mask = INTEGRATOR_AP_SYS_BITS; 1718c2ecf20Sopenharmony_ci val = vco.v & 0xFF; 1728c2ecf20Sopenharmony_ci if (vco.v & 0x100) 1738c2ecf20Sopenharmony_ci pr_err("ICST error: tried to set bit 8 of VDW\n"); 1748c2ecf20Sopenharmony_ci if (vco.s != 3) 1758c2ecf20Sopenharmony_ci pr_err("ICST error: tried to use VOD != 1\n"); 1768c2ecf20Sopenharmony_ci if (vco.r != 46) 1778c2ecf20Sopenharmony_ci pr_err("ICST error: tried to use RDW != 22\n"); 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci case ICST_INTEGRATOR_CP_CM_CORE: 1808c2ecf20Sopenharmony_ci mask = INTEGRATOR_CP_CM_CORE_BITS; /* Uses 12 bits */ 1818c2ecf20Sopenharmony_ci val = (vco.v & 0xFF) | vco.s << 8; 1828c2ecf20Sopenharmony_ci if (vco.v & 0x100) 1838c2ecf20Sopenharmony_ci pr_err("ICST error: tried to set bit 8 of VDW\n"); 1848c2ecf20Sopenharmony_ci if (vco.r != 22) 1858c2ecf20Sopenharmony_ci pr_err("ICST error: tried to use RDW != 22\n"); 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci case ICST_INTEGRATOR_CP_CM_MEM: 1888c2ecf20Sopenharmony_ci mask = INTEGRATOR_CP_CM_MEM_BITS; /* Uses 12 bits */ 1898c2ecf20Sopenharmony_ci val = ((vco.v & 0xFF) << 12) | (vco.s << 20); 1908c2ecf20Sopenharmony_ci if (vco.v & 0x100) 1918c2ecf20Sopenharmony_ci pr_err("ICST error: tried to set bit 8 of VDW\n"); 1928c2ecf20Sopenharmony_ci if (vco.r != 22) 1938c2ecf20Sopenharmony_ci pr_err("ICST error: tried to use RDW != 22\n"); 1948c2ecf20Sopenharmony_ci break; 1958c2ecf20Sopenharmony_ci default: 1968c2ecf20Sopenharmony_ci /* Regular auxilary oscillator */ 1978c2ecf20Sopenharmony_ci mask = VERSATILE_AUX_OSC_BITS; 1988c2ecf20Sopenharmony_ci val = vco.v | (vco.r << 9) | (vco.s << 16); 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci pr_debug("ICST: new val = 0x%08x\n", val); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* This magic unlocks the VCO so it can be controlled */ 2058c2ecf20Sopenharmony_ci ret = regmap_write(icst->map, icst->lockreg_off, VERSATILE_LOCK_VAL); 2068c2ecf20Sopenharmony_ci if (ret) 2078c2ecf20Sopenharmony_ci return ret; 2088c2ecf20Sopenharmony_ci ret = regmap_update_bits(icst->map, icst->vcoreg_off, mask, val); 2098c2ecf20Sopenharmony_ci if (ret) 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci /* This locks the VCO again */ 2128c2ecf20Sopenharmony_ci ret = regmap_write(icst->map, icst->lockreg_off, 0); 2138c2ecf20Sopenharmony_ci if (ret) 2148c2ecf20Sopenharmony_ci return ret; 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic unsigned long icst_recalc_rate(struct clk_hw *hw, 2198c2ecf20Sopenharmony_ci unsigned long parent_rate) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct clk_icst *icst = to_icst(hw); 2228c2ecf20Sopenharmony_ci struct icst_vco vco; 2238c2ecf20Sopenharmony_ci int ret; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (parent_rate) 2268c2ecf20Sopenharmony_ci icst->params->ref = parent_rate; 2278c2ecf20Sopenharmony_ci ret = vco_get(icst, &vco); 2288c2ecf20Sopenharmony_ci if (ret) { 2298c2ecf20Sopenharmony_ci pr_err("ICST: could not get VCO setting\n"); 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci icst->rate = icst_hz(icst->params, vco); 2338c2ecf20Sopenharmony_ci return icst->rate; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic long icst_round_rate(struct clk_hw *hw, unsigned long rate, 2378c2ecf20Sopenharmony_ci unsigned long *prate) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct clk_icst *icst = to_icst(hw); 2408c2ecf20Sopenharmony_ci struct icst_vco vco; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_AP_CM || 2438c2ecf20Sopenharmony_ci icst->ctype == ICST_INTEGRATOR_CP_CM_CORE) { 2448c2ecf20Sopenharmony_ci if (rate <= 12000000) 2458c2ecf20Sopenharmony_ci return 12000000; 2468c2ecf20Sopenharmony_ci if (rate >= 160000000) 2478c2ecf20Sopenharmony_ci return 160000000; 2488c2ecf20Sopenharmony_ci /* Slam to closest megahertz */ 2498c2ecf20Sopenharmony_ci return DIV_ROUND_CLOSEST(rate, 1000000) * 1000000; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_CP_CM_MEM) { 2538c2ecf20Sopenharmony_ci if (rate <= 6000000) 2548c2ecf20Sopenharmony_ci return 6000000; 2558c2ecf20Sopenharmony_ci if (rate >= 66000000) 2568c2ecf20Sopenharmony_ci return 66000000; 2578c2ecf20Sopenharmony_ci /* Slam to closest 0.5 megahertz */ 2588c2ecf20Sopenharmony_ci return DIV_ROUND_CLOSEST(rate, 500000) * 500000; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_AP_SYS) { 2628c2ecf20Sopenharmony_ci /* Divides between 3 and 50 MHz in steps of 0.25 MHz */ 2638c2ecf20Sopenharmony_ci if (rate <= 3000000) 2648c2ecf20Sopenharmony_ci return 3000000; 2658c2ecf20Sopenharmony_ci if (rate >= 50000000) 2668c2ecf20Sopenharmony_ci return 5000000; 2678c2ecf20Sopenharmony_ci /* Slam to closest 0.25 MHz */ 2688c2ecf20Sopenharmony_ci return DIV_ROUND_CLOSEST(rate, 250000) * 250000; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_AP_PCI) { 2728c2ecf20Sopenharmony_ci /* 2738c2ecf20Sopenharmony_ci * If we're below or less than halfway from 25 to 33 MHz 2748c2ecf20Sopenharmony_ci * select 25 MHz 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_ci if (rate <= 25000000 || rate < 29000000) 2778c2ecf20Sopenharmony_ci return 25000000; 2788c2ecf20Sopenharmony_ci /* Else just return the default frequency */ 2798c2ecf20Sopenharmony_ci return 33000000; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci vco = icst_hz_to_vco(icst->params, rate); 2838c2ecf20Sopenharmony_ci return icst_hz(icst->params, vco); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int icst_set_rate(struct clk_hw *hw, unsigned long rate, 2878c2ecf20Sopenharmony_ci unsigned long parent_rate) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct clk_icst *icst = to_icst(hw); 2908c2ecf20Sopenharmony_ci struct icst_vco vco; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_AP_PCI) { 2938c2ecf20Sopenharmony_ci /* This clock is especially primitive */ 2948c2ecf20Sopenharmony_ci unsigned int val; 2958c2ecf20Sopenharmony_ci int ret; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (rate == 25000000) { 2988c2ecf20Sopenharmony_ci val = 0; 2998c2ecf20Sopenharmony_ci } else if (rate == 33000000) { 3008c2ecf20Sopenharmony_ci val = INTEGRATOR_AP_PCI_25_33_MHZ; 3018c2ecf20Sopenharmony_ci } else { 3028c2ecf20Sopenharmony_ci pr_err("ICST: cannot set PCI frequency %lu\n", 3038c2ecf20Sopenharmony_ci rate); 3048c2ecf20Sopenharmony_ci return -EINVAL; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci ret = regmap_write(icst->map, icst->lockreg_off, 3078c2ecf20Sopenharmony_ci VERSATILE_LOCK_VAL); 3088c2ecf20Sopenharmony_ci if (ret) 3098c2ecf20Sopenharmony_ci return ret; 3108c2ecf20Sopenharmony_ci ret = regmap_update_bits(icst->map, icst->vcoreg_off, 3118c2ecf20Sopenharmony_ci INTEGRATOR_AP_PCI_25_33_MHZ, 3128c2ecf20Sopenharmony_ci val); 3138c2ecf20Sopenharmony_ci if (ret) 3148c2ecf20Sopenharmony_ci return ret; 3158c2ecf20Sopenharmony_ci /* This locks the VCO again */ 3168c2ecf20Sopenharmony_ci ret = regmap_write(icst->map, icst->lockreg_off, 0); 3178c2ecf20Sopenharmony_ci if (ret) 3188c2ecf20Sopenharmony_ci return ret; 3198c2ecf20Sopenharmony_ci return 0; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (parent_rate) 3238c2ecf20Sopenharmony_ci icst->params->ref = parent_rate; 3248c2ecf20Sopenharmony_ci vco = icst_hz_to_vco(icst->params, rate); 3258c2ecf20Sopenharmony_ci icst->rate = icst_hz(icst->params, vco); 3268c2ecf20Sopenharmony_ci return vco_set(icst, vco); 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic const struct clk_ops icst_ops = { 3308c2ecf20Sopenharmony_ci .recalc_rate = icst_recalc_rate, 3318c2ecf20Sopenharmony_ci .round_rate = icst_round_rate, 3328c2ecf20Sopenharmony_ci .set_rate = icst_set_rate, 3338c2ecf20Sopenharmony_ci}; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistruct clk *icst_clk_setup(struct device *dev, 3368c2ecf20Sopenharmony_ci const struct clk_icst_desc *desc, 3378c2ecf20Sopenharmony_ci const char *name, 3388c2ecf20Sopenharmony_ci const char *parent_name, 3398c2ecf20Sopenharmony_ci struct regmap *map, 3408c2ecf20Sopenharmony_ci enum icst_control_type ctype) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci struct clk *clk; 3438c2ecf20Sopenharmony_ci struct clk_icst *icst; 3448c2ecf20Sopenharmony_ci struct clk_init_data init; 3458c2ecf20Sopenharmony_ci struct icst_params *pclone; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci icst = kzalloc(sizeof(*icst), GFP_KERNEL); 3488c2ecf20Sopenharmony_ci if (!icst) 3498c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci pclone = kmemdup(desc->params, sizeof(*pclone), GFP_KERNEL); 3528c2ecf20Sopenharmony_ci if (!pclone) { 3538c2ecf20Sopenharmony_ci kfree(icst); 3548c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci init.name = name; 3588c2ecf20Sopenharmony_ci init.ops = &icst_ops; 3598c2ecf20Sopenharmony_ci init.flags = 0; 3608c2ecf20Sopenharmony_ci init.parent_names = (parent_name ? &parent_name : NULL); 3618c2ecf20Sopenharmony_ci init.num_parents = (parent_name ? 1 : 0); 3628c2ecf20Sopenharmony_ci icst->map = map; 3638c2ecf20Sopenharmony_ci icst->hw.init = &init; 3648c2ecf20Sopenharmony_ci icst->params = pclone; 3658c2ecf20Sopenharmony_ci icst->vcoreg_off = desc->vco_offset; 3668c2ecf20Sopenharmony_ci icst->lockreg_off = desc->lock_offset; 3678c2ecf20Sopenharmony_ci icst->ctype = ctype; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci clk = clk_register(dev, &icst->hw); 3708c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 3718c2ecf20Sopenharmony_ci kfree(pclone); 3728c2ecf20Sopenharmony_ci kfree(icst); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci return clk; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(icst_clk_setup); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistruct clk *icst_clk_register(struct device *dev, 3808c2ecf20Sopenharmony_ci const struct clk_icst_desc *desc, 3818c2ecf20Sopenharmony_ci const char *name, 3828c2ecf20Sopenharmony_ci const char *parent_name, 3838c2ecf20Sopenharmony_ci void __iomem *base) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct regmap_config icst_regmap_conf = { 3868c2ecf20Sopenharmony_ci .reg_bits = 32, 3878c2ecf20Sopenharmony_ci .val_bits = 32, 3888c2ecf20Sopenharmony_ci .reg_stride = 4, 3898c2ecf20Sopenharmony_ci }; 3908c2ecf20Sopenharmony_ci struct regmap *map; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci map = regmap_init_mmio(dev, base, &icst_regmap_conf); 3938c2ecf20Sopenharmony_ci if (IS_ERR(map)) { 3948c2ecf20Sopenharmony_ci pr_err("could not initialize ICST regmap\n"); 3958c2ecf20Sopenharmony_ci return ERR_CAST(map); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci return icst_clk_setup(dev, desc, name, parent_name, map, 3988c2ecf20Sopenharmony_ci ICST_VERSATILE); 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(icst_clk_register); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 4038c2ecf20Sopenharmony_ci/* 4048c2ecf20Sopenharmony_ci * In a device tree, an memory-mapped ICST clock appear as a child 4058c2ecf20Sopenharmony_ci * of a syscon node. Assume this and probe it only as a child of a 4068c2ecf20Sopenharmony_ci * syscon. 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic const struct icst_params icst525_params = { 4108c2ecf20Sopenharmony_ci .vco_max = ICST525_VCO_MAX_5V, 4118c2ecf20Sopenharmony_ci .vco_min = ICST525_VCO_MIN, 4128c2ecf20Sopenharmony_ci .vd_min = 8, 4138c2ecf20Sopenharmony_ci .vd_max = 263, 4148c2ecf20Sopenharmony_ci .rd_min = 3, 4158c2ecf20Sopenharmony_ci .rd_max = 65, 4168c2ecf20Sopenharmony_ci .s2div = icst525_s2div, 4178c2ecf20Sopenharmony_ci .idx2s = icst525_idx2s, 4188c2ecf20Sopenharmony_ci}; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic const struct icst_params icst307_params = { 4218c2ecf20Sopenharmony_ci .vco_max = ICST307_VCO_MAX, 4228c2ecf20Sopenharmony_ci .vco_min = ICST307_VCO_MIN, 4238c2ecf20Sopenharmony_ci .vd_min = 4 + 8, 4248c2ecf20Sopenharmony_ci .vd_max = 511 + 8, 4258c2ecf20Sopenharmony_ci .rd_min = 1 + 2, 4268c2ecf20Sopenharmony_ci .rd_max = 127 + 2, 4278c2ecf20Sopenharmony_ci .s2div = icst307_s2div, 4288c2ecf20Sopenharmony_ci .idx2s = icst307_idx2s, 4298c2ecf20Sopenharmony_ci}; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci/** 4328c2ecf20Sopenharmony_ci * The core modules on the Integrator/AP and Integrator/CP have 4338c2ecf20Sopenharmony_ci * especially crippled ICST525 control. 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_cistatic const struct icst_params icst525_apcp_cm_params = { 4368c2ecf20Sopenharmony_ci .vco_max = ICST525_VCO_MAX_5V, 4378c2ecf20Sopenharmony_ci .vco_min = ICST525_VCO_MIN, 4388c2ecf20Sopenharmony_ci /* Minimum 12 MHz, VDW = 4 */ 4398c2ecf20Sopenharmony_ci .vd_min = 12, 4408c2ecf20Sopenharmony_ci /* 4418c2ecf20Sopenharmony_ci * Maximum 160 MHz, VDW = 152 for all core modules, but 4428c2ecf20Sopenharmony_ci * CM926EJ-S, CM1026EJ-S and CM1136JF-S can actually 4438c2ecf20Sopenharmony_ci * go to 200 MHz (max VDW = 192). 4448c2ecf20Sopenharmony_ci */ 4458c2ecf20Sopenharmony_ci .vd_max = 192, 4468c2ecf20Sopenharmony_ci /* r is hardcoded to 22 and this is the actual divisor, +2 */ 4478c2ecf20Sopenharmony_ci .rd_min = 24, 4488c2ecf20Sopenharmony_ci .rd_max = 24, 4498c2ecf20Sopenharmony_ci .s2div = icst525_s2div, 4508c2ecf20Sopenharmony_ci .idx2s = icst525_idx2s, 4518c2ecf20Sopenharmony_ci}; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic const struct icst_params icst525_ap_sys_params = { 4548c2ecf20Sopenharmony_ci .vco_max = ICST525_VCO_MAX_5V, 4558c2ecf20Sopenharmony_ci .vco_min = ICST525_VCO_MIN, 4568c2ecf20Sopenharmony_ci /* Minimum 3 MHz, VDW = 4 */ 4578c2ecf20Sopenharmony_ci .vd_min = 3, 4588c2ecf20Sopenharmony_ci /* Maximum 50 MHz, VDW = 192 */ 4598c2ecf20Sopenharmony_ci .vd_max = 50, 4608c2ecf20Sopenharmony_ci /* r is hardcoded to 46 and this is the actual divisor, +2 */ 4618c2ecf20Sopenharmony_ci .rd_min = 48, 4628c2ecf20Sopenharmony_ci .rd_max = 48, 4638c2ecf20Sopenharmony_ci .s2div = icst525_s2div, 4648c2ecf20Sopenharmony_ci .idx2s = icst525_idx2s, 4658c2ecf20Sopenharmony_ci}; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic const struct icst_params icst525_ap_pci_params = { 4688c2ecf20Sopenharmony_ci .vco_max = ICST525_VCO_MAX_5V, 4698c2ecf20Sopenharmony_ci .vco_min = ICST525_VCO_MIN, 4708c2ecf20Sopenharmony_ci /* Minimum 25 MHz */ 4718c2ecf20Sopenharmony_ci .vd_min = 25, 4728c2ecf20Sopenharmony_ci /* Maximum 33 MHz */ 4738c2ecf20Sopenharmony_ci .vd_max = 33, 4748c2ecf20Sopenharmony_ci /* r is hardcoded to 14 or 22 and this is the actual divisors +2 */ 4758c2ecf20Sopenharmony_ci .rd_min = 16, 4768c2ecf20Sopenharmony_ci .rd_max = 24, 4778c2ecf20Sopenharmony_ci .s2div = icst525_s2div, 4788c2ecf20Sopenharmony_ci .idx2s = icst525_idx2s, 4798c2ecf20Sopenharmony_ci}; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic void __init of_syscon_icst_setup(struct device_node *np) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci struct device_node *parent; 4848c2ecf20Sopenharmony_ci struct regmap *map; 4858c2ecf20Sopenharmony_ci struct clk_icst_desc icst_desc; 4868c2ecf20Sopenharmony_ci const char *name = np->name; 4878c2ecf20Sopenharmony_ci const char *parent_name; 4888c2ecf20Sopenharmony_ci struct clk *regclk; 4898c2ecf20Sopenharmony_ci enum icst_control_type ctype; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* We do not release this reference, we are using it perpetually */ 4928c2ecf20Sopenharmony_ci parent = of_get_parent(np); 4938c2ecf20Sopenharmony_ci if (!parent) { 4948c2ecf20Sopenharmony_ci pr_err("no parent node for syscon ICST clock\n"); 4958c2ecf20Sopenharmony_ci return; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci map = syscon_node_to_regmap(parent); 4988c2ecf20Sopenharmony_ci if (IS_ERR(map)) { 4998c2ecf20Sopenharmony_ci pr_err("no regmap for syscon ICST clock parent\n"); 5008c2ecf20Sopenharmony_ci return; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "vco-offset", &icst_desc.vco_offset)) { 5048c2ecf20Sopenharmony_ci pr_err("no VCO register offset for ICST clock\n"); 5058c2ecf20Sopenharmony_ci return; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "lock-offset", &icst_desc.lock_offset)) { 5088c2ecf20Sopenharmony_ci pr_err("no lock register offset for ICST clock\n"); 5098c2ecf20Sopenharmony_ci return; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "arm,syscon-icst525")) { 5138c2ecf20Sopenharmony_ci icst_desc.params = &icst525_params; 5148c2ecf20Sopenharmony_ci ctype = ICST_VERSATILE; 5158c2ecf20Sopenharmony_ci } else if (of_device_is_compatible(np, "arm,syscon-icst307")) { 5168c2ecf20Sopenharmony_ci icst_desc.params = &icst307_params; 5178c2ecf20Sopenharmony_ci ctype = ICST_VERSATILE; 5188c2ecf20Sopenharmony_ci } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorap-cm")) { 5198c2ecf20Sopenharmony_ci icst_desc.params = &icst525_apcp_cm_params; 5208c2ecf20Sopenharmony_ci ctype = ICST_INTEGRATOR_AP_CM; 5218c2ecf20Sopenharmony_ci } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorap-sys")) { 5228c2ecf20Sopenharmony_ci icst_desc.params = &icst525_ap_sys_params; 5238c2ecf20Sopenharmony_ci ctype = ICST_INTEGRATOR_AP_SYS; 5248c2ecf20Sopenharmony_ci } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorap-pci")) { 5258c2ecf20Sopenharmony_ci icst_desc.params = &icst525_ap_pci_params; 5268c2ecf20Sopenharmony_ci ctype = ICST_INTEGRATOR_AP_PCI; 5278c2ecf20Sopenharmony_ci } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorcp-cm-core")) { 5288c2ecf20Sopenharmony_ci icst_desc.params = &icst525_apcp_cm_params; 5298c2ecf20Sopenharmony_ci ctype = ICST_INTEGRATOR_CP_CM_CORE; 5308c2ecf20Sopenharmony_ci } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorcp-cm-mem")) { 5318c2ecf20Sopenharmony_ci icst_desc.params = &icst525_apcp_cm_params; 5328c2ecf20Sopenharmony_ci ctype = ICST_INTEGRATOR_CP_CM_MEM; 5338c2ecf20Sopenharmony_ci } else { 5348c2ecf20Sopenharmony_ci pr_err("unknown ICST clock %s\n", name); 5358c2ecf20Sopenharmony_ci return; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* Parent clock name is not the same as node parent */ 5398c2ecf20Sopenharmony_ci parent_name = of_clk_get_parent_name(np, 0); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci regclk = icst_clk_setup(NULL, &icst_desc, name, parent_name, map, ctype); 5428c2ecf20Sopenharmony_ci if (IS_ERR(regclk)) { 5438c2ecf20Sopenharmony_ci pr_err("error setting up syscon ICST clock %s\n", name); 5448c2ecf20Sopenharmony_ci return; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci of_clk_add_provider(np, of_clk_src_simple_get, regclk); 5478c2ecf20Sopenharmony_ci pr_debug("registered syscon ICST clock %s\n", name); 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ciCLK_OF_DECLARE(arm_syscon_icst525_clk, 5518c2ecf20Sopenharmony_ci "arm,syscon-icst525", of_syscon_icst_setup); 5528c2ecf20Sopenharmony_ciCLK_OF_DECLARE(arm_syscon_icst307_clk, 5538c2ecf20Sopenharmony_ci "arm,syscon-icst307", of_syscon_icst_setup); 5548c2ecf20Sopenharmony_ciCLK_OF_DECLARE(arm_syscon_integratorap_cm_clk, 5558c2ecf20Sopenharmony_ci "arm,syscon-icst525-integratorap-cm", of_syscon_icst_setup); 5568c2ecf20Sopenharmony_ciCLK_OF_DECLARE(arm_syscon_integratorap_sys_clk, 5578c2ecf20Sopenharmony_ci "arm,syscon-icst525-integratorap-sys", of_syscon_icst_setup); 5588c2ecf20Sopenharmony_ciCLK_OF_DECLARE(arm_syscon_integratorap_pci_clk, 5598c2ecf20Sopenharmony_ci "arm,syscon-icst525-integratorap-pci", of_syscon_icst_setup); 5608c2ecf20Sopenharmony_ciCLK_OF_DECLARE(arm_syscon_integratorcp_cm_core_clk, 5618c2ecf20Sopenharmony_ci "arm,syscon-icst525-integratorcp-cm-core", of_syscon_icst_setup); 5628c2ecf20Sopenharmony_ciCLK_OF_DECLARE(arm_syscon_integratorcp_cm_mem_clk, 5638c2ecf20Sopenharmony_ci "arm,syscon-icst525-integratorcp-cm-mem", of_syscon_icst_setup); 5648c2ecf20Sopenharmony_ci#endif 565