162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the ICST307 VCO clock found in the ARM Reference designs. 462306a36Sopenharmony_ci * We wrap the custom interface from <asm/hardware/icst.h> into the generic 562306a36Sopenharmony_ci * clock framework. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2012-2015 Linus Walleij 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * TODO: when all ARM reference designs are migrated to generic clocks, the 1062306a36Sopenharmony_ci * ICST clock code from the ARM tree should probably be merged into this 1162306a36Sopenharmony_ci * file. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/export.h> 1662306a36Sopenharmony_ci#include <linux/err.h> 1762306a36Sopenharmony_ci#include <linux/clk-provider.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <linux/regmap.h> 2062306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "icst.h" 2362306a36Sopenharmony_ci#include "clk-icst.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* Magic unlocking token used on all Versatile boards */ 2662306a36Sopenharmony_ci#define VERSATILE_LOCK_VAL 0xA05F 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define VERSATILE_AUX_OSC_BITS 0x7FFFF 2962306a36Sopenharmony_ci#define INTEGRATOR_AP_CM_BITS 0xFF 3062306a36Sopenharmony_ci#define INTEGRATOR_AP_SYS_BITS 0xFF 3162306a36Sopenharmony_ci#define INTEGRATOR_CP_CM_CORE_BITS 0x7FF 3262306a36Sopenharmony_ci#define INTEGRATOR_CP_CM_MEM_BITS 0x7FF000 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define INTEGRATOR_AP_PCI_25_33_MHZ BIT(8) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/** 3762306a36Sopenharmony_ci * struct clk_icst - ICST VCO clock wrapper 3862306a36Sopenharmony_ci * @hw: corresponding clock hardware entry 3962306a36Sopenharmony_ci * @map: register map 4062306a36Sopenharmony_ci * @vcoreg_off: VCO register address 4162306a36Sopenharmony_ci * @lockreg_off: VCO lock register address 4262306a36Sopenharmony_ci * @params: parameters for this ICST instance 4362306a36Sopenharmony_ci * @rate: current rate 4462306a36Sopenharmony_ci * @ctype: the type of control register for the ICST 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistruct clk_icst { 4762306a36Sopenharmony_ci struct clk_hw hw; 4862306a36Sopenharmony_ci struct regmap *map; 4962306a36Sopenharmony_ci u32 vcoreg_off; 5062306a36Sopenharmony_ci u32 lockreg_off; 5162306a36Sopenharmony_ci struct icst_params *params; 5262306a36Sopenharmony_ci unsigned long rate; 5362306a36Sopenharmony_ci enum icst_control_type ctype; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define to_icst(_hw) container_of(_hw, struct clk_icst, hw) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/** 5962306a36Sopenharmony_ci * vco_get() - get ICST VCO settings from a certain ICST 6062306a36Sopenharmony_ci * @icst: the ICST clock to get 6162306a36Sopenharmony_ci * @vco: the VCO struct to return the value in 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_cistatic int vco_get(struct clk_icst *icst, struct icst_vco *vco) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci u32 val; 6662306a36Sopenharmony_ci int ret; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci ret = regmap_read(icst->map, icst->vcoreg_off, &val); 6962306a36Sopenharmony_ci if (ret) 7062306a36Sopenharmony_ci return ret; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* 7362306a36Sopenharmony_ci * The Integrator/AP core clock can only access the low eight 7462306a36Sopenharmony_ci * bits of the v PLL divider. Bit 8 is tied low and always zero, 7562306a36Sopenharmony_ci * r is hardwired to 22 and output divider s is hardwired to 1 7662306a36Sopenharmony_ci * (divide by 2) according to the document 7762306a36Sopenharmony_ci * "Integrator CM926EJ-S, CM946E-S, CM966E-S, CM1026EJ-S and 7862306a36Sopenharmony_ci * CM1136JF-S User Guide" ARM DUI 0138E, page 3-13 thru 3-14. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_AP_CM) { 8162306a36Sopenharmony_ci vco->v = val & INTEGRATOR_AP_CM_BITS; 8262306a36Sopenharmony_ci vco->r = 22; 8362306a36Sopenharmony_ci vco->s = 1; 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* 8862306a36Sopenharmony_ci * The Integrator/AP system clock on the base board can only 8962306a36Sopenharmony_ci * access the low eight bits of the v PLL divider. Bit 8 is tied low 9062306a36Sopenharmony_ci * and always zero, r is hardwired to 46, and the output divider is 9162306a36Sopenharmony_ci * hardwired to 3 (divide by 4) according to the document 9262306a36Sopenharmony_ci * "Integrator AP ASIC Development Motherboard" ARM DUI 0098B, 9362306a36Sopenharmony_ci * page 3-16. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_AP_SYS) { 9662306a36Sopenharmony_ci vco->v = val & INTEGRATOR_AP_SYS_BITS; 9762306a36Sopenharmony_ci vco->r = 46; 9862306a36Sopenharmony_ci vco->s = 3; 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* 10362306a36Sopenharmony_ci * The Integrator/AP PCI clock is using an odd pattern to create 10462306a36Sopenharmony_ci * the child clock, basically a single bit called DIVX/Y is used 10562306a36Sopenharmony_ci * to select between two different hardwired values: setting the 10662306a36Sopenharmony_ci * bit to 0 yields v = 17, r = 22 and OD = 1, whereas setting the 10762306a36Sopenharmony_ci * bit to 1 yields v = 14, r = 14 and OD = 1 giving the frequencies 10862306a36Sopenharmony_ci * 33 or 25 MHz respectively. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_AP_PCI) { 11162306a36Sopenharmony_ci bool divxy = !!(val & INTEGRATOR_AP_PCI_25_33_MHZ); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci vco->v = divxy ? 17 : 14; 11462306a36Sopenharmony_ci vco->r = divxy ? 22 : 14; 11562306a36Sopenharmony_ci vco->s = 1; 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* 12062306a36Sopenharmony_ci * The Integrator/CP core clock can access the low eight bits 12162306a36Sopenharmony_ci * of the v PLL divider. Bit 8 is tied low and always zero, 12262306a36Sopenharmony_ci * r is hardwired to 22 and the output divider s is accessible 12362306a36Sopenharmony_ci * in bits 8 thru 10 according to the document 12462306a36Sopenharmony_ci * "Integrator/CM940T, CM920T, CM740T, and CM720T User Guide" 12562306a36Sopenharmony_ci * ARM DUI 0157A, page 3-20 thru 3-23 and 4-10. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_CP_CM_CORE) { 12862306a36Sopenharmony_ci vco->v = val & 0xFF; 12962306a36Sopenharmony_ci vco->r = 22; 13062306a36Sopenharmony_ci vco->s = (val >> 8) & 7; 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_CP_CM_MEM) { 13562306a36Sopenharmony_ci vco->v = (val >> 12) & 0xFF; 13662306a36Sopenharmony_ci vco->r = 22; 13762306a36Sopenharmony_ci vco->s = (val >> 20) & 7; 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci vco->v = val & 0x1ff; 14262306a36Sopenharmony_ci vco->r = (val >> 9) & 0x7f; 14362306a36Sopenharmony_ci vco->s = (val >> 16) & 03; 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/** 14862306a36Sopenharmony_ci * vco_set() - commit changes to an ICST VCO 14962306a36Sopenharmony_ci * @icst: the ICST clock to set 15062306a36Sopenharmony_ci * @vco: the VCO struct to set the changes from 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cistatic int vco_set(struct clk_icst *icst, struct icst_vco vco) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci u32 mask; 15562306a36Sopenharmony_ci u32 val; 15662306a36Sopenharmony_ci int ret; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* Mask the bits used by the VCO */ 15962306a36Sopenharmony_ci switch (icst->ctype) { 16062306a36Sopenharmony_ci case ICST_INTEGRATOR_AP_CM: 16162306a36Sopenharmony_ci mask = INTEGRATOR_AP_CM_BITS; 16262306a36Sopenharmony_ci val = vco.v & 0xFF; 16362306a36Sopenharmony_ci if (vco.v & 0x100) 16462306a36Sopenharmony_ci pr_err("ICST error: tried to set bit 8 of VDW\n"); 16562306a36Sopenharmony_ci if (vco.s != 1) 16662306a36Sopenharmony_ci pr_err("ICST error: tried to use VOD != 1\n"); 16762306a36Sopenharmony_ci if (vco.r != 22) 16862306a36Sopenharmony_ci pr_err("ICST error: tried to use RDW != 22\n"); 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci case ICST_INTEGRATOR_AP_SYS: 17162306a36Sopenharmony_ci mask = INTEGRATOR_AP_SYS_BITS; 17262306a36Sopenharmony_ci val = vco.v & 0xFF; 17362306a36Sopenharmony_ci if (vco.v & 0x100) 17462306a36Sopenharmony_ci pr_err("ICST error: tried to set bit 8 of VDW\n"); 17562306a36Sopenharmony_ci if (vco.s != 3) 17662306a36Sopenharmony_ci pr_err("ICST error: tried to use VOD != 1\n"); 17762306a36Sopenharmony_ci if (vco.r != 46) 17862306a36Sopenharmony_ci pr_err("ICST error: tried to use RDW != 22\n"); 17962306a36Sopenharmony_ci break; 18062306a36Sopenharmony_ci case ICST_INTEGRATOR_CP_CM_CORE: 18162306a36Sopenharmony_ci mask = INTEGRATOR_CP_CM_CORE_BITS; /* Uses 12 bits */ 18262306a36Sopenharmony_ci val = (vco.v & 0xFF) | vco.s << 8; 18362306a36Sopenharmony_ci if (vco.v & 0x100) 18462306a36Sopenharmony_ci pr_err("ICST error: tried to set bit 8 of VDW\n"); 18562306a36Sopenharmony_ci if (vco.r != 22) 18662306a36Sopenharmony_ci pr_err("ICST error: tried to use RDW != 22\n"); 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci case ICST_INTEGRATOR_CP_CM_MEM: 18962306a36Sopenharmony_ci mask = INTEGRATOR_CP_CM_MEM_BITS; /* Uses 12 bits */ 19062306a36Sopenharmony_ci val = ((vco.v & 0xFF) << 12) | (vco.s << 20); 19162306a36Sopenharmony_ci if (vco.v & 0x100) 19262306a36Sopenharmony_ci pr_err("ICST error: tried to set bit 8 of VDW\n"); 19362306a36Sopenharmony_ci if (vco.r != 22) 19462306a36Sopenharmony_ci pr_err("ICST error: tried to use RDW != 22\n"); 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci default: 19762306a36Sopenharmony_ci /* Regular auxilary oscillator */ 19862306a36Sopenharmony_ci mask = VERSATILE_AUX_OSC_BITS; 19962306a36Sopenharmony_ci val = vco.v | (vco.r << 9) | (vco.s << 16); 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci pr_debug("ICST: new val = 0x%08x\n", val); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* This magic unlocks the VCO so it can be controlled */ 20662306a36Sopenharmony_ci ret = regmap_write(icst->map, icst->lockreg_off, VERSATILE_LOCK_VAL); 20762306a36Sopenharmony_ci if (ret) 20862306a36Sopenharmony_ci return ret; 20962306a36Sopenharmony_ci ret = regmap_update_bits(icst->map, icst->vcoreg_off, mask, val); 21062306a36Sopenharmony_ci if (ret) 21162306a36Sopenharmony_ci return ret; 21262306a36Sopenharmony_ci /* This locks the VCO again */ 21362306a36Sopenharmony_ci ret = regmap_write(icst->map, icst->lockreg_off, 0); 21462306a36Sopenharmony_ci if (ret) 21562306a36Sopenharmony_ci return ret; 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic unsigned long icst_recalc_rate(struct clk_hw *hw, 22062306a36Sopenharmony_ci unsigned long parent_rate) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct clk_icst *icst = to_icst(hw); 22362306a36Sopenharmony_ci struct icst_vco vco; 22462306a36Sopenharmony_ci int ret; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (parent_rate) 22762306a36Sopenharmony_ci icst->params->ref = parent_rate; 22862306a36Sopenharmony_ci ret = vco_get(icst, &vco); 22962306a36Sopenharmony_ci if (ret) { 23062306a36Sopenharmony_ci pr_err("ICST: could not get VCO setting\n"); 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci icst->rate = icst_hz(icst->params, vco); 23462306a36Sopenharmony_ci return icst->rate; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic long icst_round_rate(struct clk_hw *hw, unsigned long rate, 23862306a36Sopenharmony_ci unsigned long *prate) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct clk_icst *icst = to_icst(hw); 24162306a36Sopenharmony_ci struct icst_vco vco; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_AP_CM || 24462306a36Sopenharmony_ci icst->ctype == ICST_INTEGRATOR_CP_CM_CORE) { 24562306a36Sopenharmony_ci if (rate <= 12000000) 24662306a36Sopenharmony_ci return 12000000; 24762306a36Sopenharmony_ci if (rate >= 160000000) 24862306a36Sopenharmony_ci return 160000000; 24962306a36Sopenharmony_ci /* Slam to closest megahertz */ 25062306a36Sopenharmony_ci return DIV_ROUND_CLOSEST(rate, 1000000) * 1000000; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_CP_CM_MEM) { 25462306a36Sopenharmony_ci if (rate <= 6000000) 25562306a36Sopenharmony_ci return 6000000; 25662306a36Sopenharmony_ci if (rate >= 66000000) 25762306a36Sopenharmony_ci return 66000000; 25862306a36Sopenharmony_ci /* Slam to closest 0.5 megahertz */ 25962306a36Sopenharmony_ci return DIV_ROUND_CLOSEST(rate, 500000) * 500000; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_AP_SYS) { 26362306a36Sopenharmony_ci /* Divides between 3 and 50 MHz in steps of 0.25 MHz */ 26462306a36Sopenharmony_ci if (rate <= 3000000) 26562306a36Sopenharmony_ci return 3000000; 26662306a36Sopenharmony_ci if (rate >= 50000000) 26762306a36Sopenharmony_ci return 5000000; 26862306a36Sopenharmony_ci /* Slam to closest 0.25 MHz */ 26962306a36Sopenharmony_ci return DIV_ROUND_CLOSEST(rate, 250000) * 250000; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_AP_PCI) { 27362306a36Sopenharmony_ci /* 27462306a36Sopenharmony_ci * If we're below or less than halfway from 25 to 33 MHz 27562306a36Sopenharmony_ci * select 25 MHz 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci if (rate <= 25000000 || rate < 29000000) 27862306a36Sopenharmony_ci return 25000000; 27962306a36Sopenharmony_ci /* Else just return the default frequency */ 28062306a36Sopenharmony_ci return 33000000; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci vco = icst_hz_to_vco(icst->params, rate); 28462306a36Sopenharmony_ci return icst_hz(icst->params, vco); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int icst_set_rate(struct clk_hw *hw, unsigned long rate, 28862306a36Sopenharmony_ci unsigned long parent_rate) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct clk_icst *icst = to_icst(hw); 29162306a36Sopenharmony_ci struct icst_vco vco; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (icst->ctype == ICST_INTEGRATOR_AP_PCI) { 29462306a36Sopenharmony_ci /* This clock is especially primitive */ 29562306a36Sopenharmony_ci unsigned int val; 29662306a36Sopenharmony_ci int ret; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (rate == 25000000) { 29962306a36Sopenharmony_ci val = 0; 30062306a36Sopenharmony_ci } else if (rate == 33000000) { 30162306a36Sopenharmony_ci val = INTEGRATOR_AP_PCI_25_33_MHZ; 30262306a36Sopenharmony_ci } else { 30362306a36Sopenharmony_ci pr_err("ICST: cannot set PCI frequency %lu\n", 30462306a36Sopenharmony_ci rate); 30562306a36Sopenharmony_ci return -EINVAL; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci ret = regmap_write(icst->map, icst->lockreg_off, 30862306a36Sopenharmony_ci VERSATILE_LOCK_VAL); 30962306a36Sopenharmony_ci if (ret) 31062306a36Sopenharmony_ci return ret; 31162306a36Sopenharmony_ci ret = regmap_update_bits(icst->map, icst->vcoreg_off, 31262306a36Sopenharmony_ci INTEGRATOR_AP_PCI_25_33_MHZ, 31362306a36Sopenharmony_ci val); 31462306a36Sopenharmony_ci if (ret) 31562306a36Sopenharmony_ci return ret; 31662306a36Sopenharmony_ci /* This locks the VCO again */ 31762306a36Sopenharmony_ci ret = regmap_write(icst->map, icst->lockreg_off, 0); 31862306a36Sopenharmony_ci if (ret) 31962306a36Sopenharmony_ci return ret; 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (parent_rate) 32462306a36Sopenharmony_ci icst->params->ref = parent_rate; 32562306a36Sopenharmony_ci vco = icst_hz_to_vco(icst->params, rate); 32662306a36Sopenharmony_ci icst->rate = icst_hz(icst->params, vco); 32762306a36Sopenharmony_ci return vco_set(icst, vco); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic const struct clk_ops icst_ops = { 33162306a36Sopenharmony_ci .recalc_rate = icst_recalc_rate, 33262306a36Sopenharmony_ci .round_rate = icst_round_rate, 33362306a36Sopenharmony_ci .set_rate = icst_set_rate, 33462306a36Sopenharmony_ci}; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistruct clk *icst_clk_setup(struct device *dev, 33762306a36Sopenharmony_ci const struct clk_icst_desc *desc, 33862306a36Sopenharmony_ci const char *name, 33962306a36Sopenharmony_ci const char *parent_name, 34062306a36Sopenharmony_ci struct regmap *map, 34162306a36Sopenharmony_ci enum icst_control_type ctype) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct clk *clk; 34462306a36Sopenharmony_ci struct clk_icst *icst; 34562306a36Sopenharmony_ci struct clk_init_data init; 34662306a36Sopenharmony_ci struct icst_params *pclone; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci icst = kzalloc(sizeof(*icst), GFP_KERNEL); 34962306a36Sopenharmony_ci if (!icst) 35062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci pclone = kmemdup(desc->params, sizeof(*pclone), GFP_KERNEL); 35362306a36Sopenharmony_ci if (!pclone) { 35462306a36Sopenharmony_ci kfree(icst); 35562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci init.name = name; 35962306a36Sopenharmony_ci init.ops = &icst_ops; 36062306a36Sopenharmony_ci init.flags = 0; 36162306a36Sopenharmony_ci init.parent_names = (parent_name ? &parent_name : NULL); 36262306a36Sopenharmony_ci init.num_parents = (parent_name ? 1 : 0); 36362306a36Sopenharmony_ci icst->map = map; 36462306a36Sopenharmony_ci icst->hw.init = &init; 36562306a36Sopenharmony_ci icst->params = pclone; 36662306a36Sopenharmony_ci icst->vcoreg_off = desc->vco_offset; 36762306a36Sopenharmony_ci icst->lockreg_off = desc->lock_offset; 36862306a36Sopenharmony_ci icst->ctype = ctype; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci clk = clk_register(dev, &icst->hw); 37162306a36Sopenharmony_ci if (IS_ERR(clk)) { 37262306a36Sopenharmony_ci kfree(pclone); 37362306a36Sopenharmony_ci kfree(icst); 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return clk; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(icst_clk_setup); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistruct clk *icst_clk_register(struct device *dev, 38162306a36Sopenharmony_ci const struct clk_icst_desc *desc, 38262306a36Sopenharmony_ci const char *name, 38362306a36Sopenharmony_ci const char *parent_name, 38462306a36Sopenharmony_ci void __iomem *base) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct regmap_config icst_regmap_conf = { 38762306a36Sopenharmony_ci .reg_bits = 32, 38862306a36Sopenharmony_ci .val_bits = 32, 38962306a36Sopenharmony_ci .reg_stride = 4, 39062306a36Sopenharmony_ci }; 39162306a36Sopenharmony_ci struct regmap *map; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci map = regmap_init_mmio(dev, base, &icst_regmap_conf); 39462306a36Sopenharmony_ci if (IS_ERR(map)) { 39562306a36Sopenharmony_ci pr_err("could not initialize ICST regmap\n"); 39662306a36Sopenharmony_ci return ERR_CAST(map); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci return icst_clk_setup(dev, desc, name, parent_name, map, 39962306a36Sopenharmony_ci ICST_VERSATILE); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(icst_clk_register); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci#ifdef CONFIG_OF 40462306a36Sopenharmony_ci/* 40562306a36Sopenharmony_ci * In a device tree, an memory-mapped ICST clock appear as a child 40662306a36Sopenharmony_ci * of a syscon node. Assume this and probe it only as a child of a 40762306a36Sopenharmony_ci * syscon. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic const struct icst_params icst525_params = { 41162306a36Sopenharmony_ci .vco_max = ICST525_VCO_MAX_5V, 41262306a36Sopenharmony_ci .vco_min = ICST525_VCO_MIN, 41362306a36Sopenharmony_ci .vd_min = 8, 41462306a36Sopenharmony_ci .vd_max = 263, 41562306a36Sopenharmony_ci .rd_min = 3, 41662306a36Sopenharmony_ci .rd_max = 65, 41762306a36Sopenharmony_ci .s2div = icst525_s2div, 41862306a36Sopenharmony_ci .idx2s = icst525_idx2s, 41962306a36Sopenharmony_ci}; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic const struct icst_params icst307_params = { 42262306a36Sopenharmony_ci .vco_max = ICST307_VCO_MAX, 42362306a36Sopenharmony_ci .vco_min = ICST307_VCO_MIN, 42462306a36Sopenharmony_ci .vd_min = 4 + 8, 42562306a36Sopenharmony_ci .vd_max = 511 + 8, 42662306a36Sopenharmony_ci .rd_min = 1 + 2, 42762306a36Sopenharmony_ci .rd_max = 127 + 2, 42862306a36Sopenharmony_ci .s2div = icst307_s2div, 42962306a36Sopenharmony_ci .idx2s = icst307_idx2s, 43062306a36Sopenharmony_ci}; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci/* 43362306a36Sopenharmony_ci * The core modules on the Integrator/AP and Integrator/CP have 43462306a36Sopenharmony_ci * especially crippled ICST525 control. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_cistatic const struct icst_params icst525_apcp_cm_params = { 43762306a36Sopenharmony_ci .vco_max = ICST525_VCO_MAX_5V, 43862306a36Sopenharmony_ci .vco_min = ICST525_VCO_MIN, 43962306a36Sopenharmony_ci /* Minimum 12 MHz, VDW = 4 */ 44062306a36Sopenharmony_ci .vd_min = 12, 44162306a36Sopenharmony_ci /* 44262306a36Sopenharmony_ci * Maximum 160 MHz, VDW = 152 for all core modules, but 44362306a36Sopenharmony_ci * CM926EJ-S, CM1026EJ-S and CM1136JF-S can actually 44462306a36Sopenharmony_ci * go to 200 MHz (max VDW = 192). 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_ci .vd_max = 192, 44762306a36Sopenharmony_ci /* r is hardcoded to 22 and this is the actual divisor, +2 */ 44862306a36Sopenharmony_ci .rd_min = 24, 44962306a36Sopenharmony_ci .rd_max = 24, 45062306a36Sopenharmony_ci .s2div = icst525_s2div, 45162306a36Sopenharmony_ci .idx2s = icst525_idx2s, 45262306a36Sopenharmony_ci}; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic const struct icst_params icst525_ap_sys_params = { 45562306a36Sopenharmony_ci .vco_max = ICST525_VCO_MAX_5V, 45662306a36Sopenharmony_ci .vco_min = ICST525_VCO_MIN, 45762306a36Sopenharmony_ci /* Minimum 3 MHz, VDW = 4 */ 45862306a36Sopenharmony_ci .vd_min = 3, 45962306a36Sopenharmony_ci /* Maximum 50 MHz, VDW = 192 */ 46062306a36Sopenharmony_ci .vd_max = 50, 46162306a36Sopenharmony_ci /* r is hardcoded to 46 and this is the actual divisor, +2 */ 46262306a36Sopenharmony_ci .rd_min = 48, 46362306a36Sopenharmony_ci .rd_max = 48, 46462306a36Sopenharmony_ci .s2div = icst525_s2div, 46562306a36Sopenharmony_ci .idx2s = icst525_idx2s, 46662306a36Sopenharmony_ci}; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic const struct icst_params icst525_ap_pci_params = { 46962306a36Sopenharmony_ci .vco_max = ICST525_VCO_MAX_5V, 47062306a36Sopenharmony_ci .vco_min = ICST525_VCO_MIN, 47162306a36Sopenharmony_ci /* Minimum 25 MHz */ 47262306a36Sopenharmony_ci .vd_min = 25, 47362306a36Sopenharmony_ci /* Maximum 33 MHz */ 47462306a36Sopenharmony_ci .vd_max = 33, 47562306a36Sopenharmony_ci /* r is hardcoded to 14 or 22 and this is the actual divisors +2 */ 47662306a36Sopenharmony_ci .rd_min = 16, 47762306a36Sopenharmony_ci .rd_max = 24, 47862306a36Sopenharmony_ci .s2div = icst525_s2div, 47962306a36Sopenharmony_ci .idx2s = icst525_idx2s, 48062306a36Sopenharmony_ci}; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic void __init of_syscon_icst_setup(struct device_node *np) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct device_node *parent; 48562306a36Sopenharmony_ci struct regmap *map; 48662306a36Sopenharmony_ci struct clk_icst_desc icst_desc; 48762306a36Sopenharmony_ci const char *name; 48862306a36Sopenharmony_ci const char *parent_name; 48962306a36Sopenharmony_ci struct clk *regclk; 49062306a36Sopenharmony_ci enum icst_control_type ctype; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* We do not release this reference, we are using it perpetually */ 49362306a36Sopenharmony_ci parent = of_get_parent(np); 49462306a36Sopenharmony_ci if (!parent) { 49562306a36Sopenharmony_ci pr_err("no parent node for syscon ICST clock\n"); 49662306a36Sopenharmony_ci return; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci map = syscon_node_to_regmap(parent); 49962306a36Sopenharmony_ci if (IS_ERR(map)) { 50062306a36Sopenharmony_ci pr_err("no regmap for syscon ICST clock parent\n"); 50162306a36Sopenharmony_ci return; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (of_property_read_u32(np, "reg", &icst_desc.vco_offset) && 50562306a36Sopenharmony_ci of_property_read_u32(np, "vco-offset", &icst_desc.vco_offset)) { 50662306a36Sopenharmony_ci pr_err("no VCO register offset for ICST clock\n"); 50762306a36Sopenharmony_ci return; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci if (of_property_read_u32(np, "lock-offset", &icst_desc.lock_offset)) { 51062306a36Sopenharmony_ci pr_err("no lock register offset for ICST clock\n"); 51162306a36Sopenharmony_ci return; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (of_device_is_compatible(np, "arm,syscon-icst525")) { 51562306a36Sopenharmony_ci icst_desc.params = &icst525_params; 51662306a36Sopenharmony_ci ctype = ICST_VERSATILE; 51762306a36Sopenharmony_ci } else if (of_device_is_compatible(np, "arm,syscon-icst307")) { 51862306a36Sopenharmony_ci icst_desc.params = &icst307_params; 51962306a36Sopenharmony_ci ctype = ICST_VERSATILE; 52062306a36Sopenharmony_ci } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorap-cm")) { 52162306a36Sopenharmony_ci icst_desc.params = &icst525_apcp_cm_params; 52262306a36Sopenharmony_ci ctype = ICST_INTEGRATOR_AP_CM; 52362306a36Sopenharmony_ci } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorap-sys")) { 52462306a36Sopenharmony_ci icst_desc.params = &icst525_ap_sys_params; 52562306a36Sopenharmony_ci ctype = ICST_INTEGRATOR_AP_SYS; 52662306a36Sopenharmony_ci } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorap-pci")) { 52762306a36Sopenharmony_ci icst_desc.params = &icst525_ap_pci_params; 52862306a36Sopenharmony_ci ctype = ICST_INTEGRATOR_AP_PCI; 52962306a36Sopenharmony_ci } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorcp-cm-core")) { 53062306a36Sopenharmony_ci icst_desc.params = &icst525_apcp_cm_params; 53162306a36Sopenharmony_ci ctype = ICST_INTEGRATOR_CP_CM_CORE; 53262306a36Sopenharmony_ci } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorcp-cm-mem")) { 53362306a36Sopenharmony_ci icst_desc.params = &icst525_apcp_cm_params; 53462306a36Sopenharmony_ci ctype = ICST_INTEGRATOR_CP_CM_MEM; 53562306a36Sopenharmony_ci } else { 53662306a36Sopenharmony_ci pr_err("unknown ICST clock %pOF\n", np); 53762306a36Sopenharmony_ci return; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* Parent clock name is not the same as node parent */ 54162306a36Sopenharmony_ci parent_name = of_clk_get_parent_name(np, 0); 54262306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%pOFP", np); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci regclk = icst_clk_setup(NULL, &icst_desc, name, parent_name, map, ctype); 54562306a36Sopenharmony_ci if (IS_ERR(regclk)) { 54662306a36Sopenharmony_ci pr_err("error setting up syscon ICST clock %s\n", name); 54762306a36Sopenharmony_ci kfree(name); 54862306a36Sopenharmony_ci return; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci of_clk_add_provider(np, of_clk_src_simple_get, regclk); 55162306a36Sopenharmony_ci pr_debug("registered syscon ICST clock %s\n", name); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ciCLK_OF_DECLARE(arm_syscon_icst525_clk, 55562306a36Sopenharmony_ci "arm,syscon-icst525", of_syscon_icst_setup); 55662306a36Sopenharmony_ciCLK_OF_DECLARE(arm_syscon_icst307_clk, 55762306a36Sopenharmony_ci "arm,syscon-icst307", of_syscon_icst_setup); 55862306a36Sopenharmony_ciCLK_OF_DECLARE(arm_syscon_integratorap_cm_clk, 55962306a36Sopenharmony_ci "arm,syscon-icst525-integratorap-cm", of_syscon_icst_setup); 56062306a36Sopenharmony_ciCLK_OF_DECLARE(arm_syscon_integratorap_sys_clk, 56162306a36Sopenharmony_ci "arm,syscon-icst525-integratorap-sys", of_syscon_icst_setup); 56262306a36Sopenharmony_ciCLK_OF_DECLARE(arm_syscon_integratorap_pci_clk, 56362306a36Sopenharmony_ci "arm,syscon-icst525-integratorap-pci", of_syscon_icst_setup); 56462306a36Sopenharmony_ciCLK_OF_DECLARE(arm_syscon_integratorcp_cm_core_clk, 56562306a36Sopenharmony_ci "arm,syscon-icst525-integratorcp-cm-core", of_syscon_icst_setup); 56662306a36Sopenharmony_ciCLK_OF_DECLARE(arm_syscon_integratorcp_cm_mem_clk, 56762306a36Sopenharmony_ci "arm,syscon-icst525-integratorcp-cm-mem", of_syscon_icst_setup); 56862306a36Sopenharmony_ci#endif 569