162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Common clock framework driver for the Versaclock7 family of timing devices. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2022 Renesas Electronics Corporation 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/bitfield.h> 1162306a36Sopenharmony_ci#include <linux/clk.h> 1262306a36Sopenharmony_ci#include <linux/clk-provider.h> 1362306a36Sopenharmony_ci#include <linux/i2c.h> 1462306a36Sopenharmony_ci#include <linux/math64.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/property.h> 1862306a36Sopenharmony_ci#include <linux/regmap.h> 1962306a36Sopenharmony_ci#include <linux/swab.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * 16-bit register address: the lower 8 bits of the register address come 2362306a36Sopenharmony_ci * from the offset addr byte and the upper 8 bits come from the page register. 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci#define VC7_PAGE_ADDR 0xFD 2662306a36Sopenharmony_ci#define VC7_PAGE_WINDOW 256 2762306a36Sopenharmony_ci#define VC7_MAX_REG 0x364 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Maximum number of banks supported by VC7 */ 3062306a36Sopenharmony_ci#define VC7_NUM_BANKS 7 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Maximum number of FODs supported by VC7 */ 3362306a36Sopenharmony_ci#define VC7_NUM_FOD 3 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Maximum number of IODs supported by VC7 */ 3662306a36Sopenharmony_ci#define VC7_NUM_IOD 4 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Maximum number of outputs supported by VC7 */ 3962306a36Sopenharmony_ci#define VC7_NUM_OUT 12 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* VCO valid range is 9.5 GHz to 10.7 GHz */ 4262306a36Sopenharmony_ci#define VC7_APLL_VCO_MIN 9500000000UL 4362306a36Sopenharmony_ci#define VC7_APLL_VCO_MAX 10700000000UL 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* APLL denominator is fixed at 2^27 */ 4662306a36Sopenharmony_ci#define VC7_APLL_DENOMINATOR_BITS 27 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* FOD 1st stage denominator is fixed 2^34 */ 4962306a36Sopenharmony_ci#define VC7_FOD_DENOMINATOR_BITS 34 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* IOD can operate between 1kHz and 650MHz */ 5262306a36Sopenharmony_ci#define VC7_IOD_RATE_MIN 1000UL 5362306a36Sopenharmony_ci#define VC7_IOD_RATE_MAX 650000000UL 5462306a36Sopenharmony_ci#define VC7_IOD_MIN_DIVISOR 14 5562306a36Sopenharmony_ci#define VC7_IOD_MAX_DIVISOR 0x1ffffff /* 25-bit */ 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define VC7_FOD_RATE_MIN 1000UL 5862306a36Sopenharmony_ci#define VC7_FOD_RATE_MAX 650000000UL 5962306a36Sopenharmony_ci#define VC7_FOD_1ST_STAGE_RATE_MIN 33000000UL /* 33 MHz */ 6062306a36Sopenharmony_ci#define VC7_FOD_1ST_STAGE_RATE_MAX 650000000UL /* 650 MHz */ 6162306a36Sopenharmony_ci#define VC7_FOD_1ST_INT_MAX 324 6262306a36Sopenharmony_ci#define VC7_FOD_2ND_INT_MIN 2 6362306a36Sopenharmony_ci#define VC7_FOD_2ND_INT_MAX 0x1ffff /* 17-bit */ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* VC7 Registers */ 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define VC7_REG_XO_CNFG 0x2C 6862306a36Sopenharmony_ci#define VC7_REG_XO_CNFG_COUNT 4 6962306a36Sopenharmony_ci#define VC7_REG_XO_IB_H_DIV_SHIFT 24 7062306a36Sopenharmony_ci#define VC7_REG_XO_IB_H_DIV_MASK GENMASK(28, VC7_REG_XO_IB_H_DIV_SHIFT) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define VC7_REG_APLL_FB_DIV_FRAC 0x120 7362306a36Sopenharmony_ci#define VC7_REG_APLL_FB_DIV_FRAC_COUNT 4 7462306a36Sopenharmony_ci#define VC7_REG_APLL_FB_DIV_FRAC_MASK GENMASK(26, 0) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define VC7_REG_APLL_FB_DIV_INT 0x124 7762306a36Sopenharmony_ci#define VC7_REG_APLL_FB_DIV_INT_COUNT 2 7862306a36Sopenharmony_ci#define VC7_REG_APLL_FB_DIV_INT_MASK GENMASK(9, 0) 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define VC7_REG_APLL_CNFG 0x127 8162306a36Sopenharmony_ci#define VC7_REG_APLL_EN_DOUBLER BIT(0) 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define VC7_REG_OUT_BANK_CNFG(idx) (0x280 + (0x4 * (idx))) 8462306a36Sopenharmony_ci#define VC7_REG_OUTPUT_BANK_SRC_MASK GENMASK(2, 0) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define VC7_REG_FOD_INT_CNFG(idx) (0x1E0 + (0x10 * (idx))) 8762306a36Sopenharmony_ci#define VC7_REG_FOD_INT_CNFG_COUNT 8 8862306a36Sopenharmony_ci#define VC7_REG_FOD_1ST_INT_MASK GENMASK(8, 0) 8962306a36Sopenharmony_ci#define VC7_REG_FOD_2ND_INT_SHIFT 9 9062306a36Sopenharmony_ci#define VC7_REG_FOD_2ND_INT_MASK GENMASK(25, VC7_REG_FOD_2ND_INT_SHIFT) 9162306a36Sopenharmony_ci#define VC7_REG_FOD_FRAC_SHIFT 26 9262306a36Sopenharmony_ci#define VC7_REG_FOD_FRAC_MASK GENMASK_ULL(59, VC7_REG_FOD_FRAC_SHIFT) 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define VC7_REG_IOD_INT_CNFG(idx) (0x1C0 + (0x8 * (idx))) 9562306a36Sopenharmony_ci#define VC7_REG_IOD_INT_CNFG_COUNT 4 9662306a36Sopenharmony_ci#define VC7_REG_IOD_INT_MASK GENMASK(24, 0) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define VC7_REG_ODRV_EN(idx) (0x240 + (0x4 * (idx))) 9962306a36Sopenharmony_ci#define VC7_REG_OUT_DIS BIT(0) 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistruct vc7_driver_data; 10262306a36Sopenharmony_cistatic const struct regmap_config vc7_regmap_config; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* Supported Renesas VC7 models */ 10562306a36Sopenharmony_cienum vc7_model { 10662306a36Sopenharmony_ci VC7_RC21008A, 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistruct vc7_chip_info { 11062306a36Sopenharmony_ci const enum vc7_model model; 11162306a36Sopenharmony_ci const unsigned int banks[VC7_NUM_BANKS]; 11262306a36Sopenharmony_ci const unsigned int num_banks; 11362306a36Sopenharmony_ci const unsigned int outputs[VC7_NUM_OUT]; 11462306a36Sopenharmony_ci const unsigned int num_outputs; 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* 11862306a36Sopenharmony_ci * Changing the APLL frequency is currently not supported. 11962306a36Sopenharmony_ci * The APLL will consist of an opaque block between the XO and FOD/IODs and 12062306a36Sopenharmony_ci * its frequency will be computed based on the current state of the device. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistruct vc7_apll_data { 12362306a36Sopenharmony_ci struct clk *clk; 12462306a36Sopenharmony_ci struct vc7_driver_data *vc7; 12562306a36Sopenharmony_ci u8 xo_ib_h_div; 12662306a36Sopenharmony_ci u8 en_doubler; 12762306a36Sopenharmony_ci u16 apll_fb_div_int; 12862306a36Sopenharmony_ci u32 apll_fb_div_frac; 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistruct vc7_fod_data { 13262306a36Sopenharmony_ci struct clk_hw hw; 13362306a36Sopenharmony_ci struct vc7_driver_data *vc7; 13462306a36Sopenharmony_ci unsigned int num; 13562306a36Sopenharmony_ci u32 fod_1st_int; 13662306a36Sopenharmony_ci u32 fod_2nd_int; 13762306a36Sopenharmony_ci u64 fod_frac; 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistruct vc7_iod_data { 14162306a36Sopenharmony_ci struct clk_hw hw; 14262306a36Sopenharmony_ci struct vc7_driver_data *vc7; 14362306a36Sopenharmony_ci unsigned int num; 14462306a36Sopenharmony_ci u32 iod_int; 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistruct vc7_out_data { 14862306a36Sopenharmony_ci struct clk_hw hw; 14962306a36Sopenharmony_ci struct vc7_driver_data *vc7; 15062306a36Sopenharmony_ci unsigned int num; 15162306a36Sopenharmony_ci unsigned int out_dis; 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistruct vc7_driver_data { 15562306a36Sopenharmony_ci struct i2c_client *client; 15662306a36Sopenharmony_ci struct regmap *regmap; 15762306a36Sopenharmony_ci const struct vc7_chip_info *chip_info; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci struct clk *pin_xin; 16062306a36Sopenharmony_ci struct vc7_apll_data clk_apll; 16162306a36Sopenharmony_ci struct vc7_fod_data clk_fod[VC7_NUM_FOD]; 16262306a36Sopenharmony_ci struct vc7_iod_data clk_iod[VC7_NUM_IOD]; 16362306a36Sopenharmony_ci struct vc7_out_data clk_out[VC7_NUM_OUT]; 16462306a36Sopenharmony_ci}; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistruct vc7_bank_src_map { 16762306a36Sopenharmony_ci enum vc7_bank_src_type { 16862306a36Sopenharmony_ci VC7_FOD, 16962306a36Sopenharmony_ci VC7_IOD, 17062306a36Sopenharmony_ci } type; 17162306a36Sopenharmony_ci union _divider { 17262306a36Sopenharmony_ci struct vc7_iod_data *iod; 17362306a36Sopenharmony_ci struct vc7_fod_data *fod; 17462306a36Sopenharmony_ci } src; 17562306a36Sopenharmony_ci}; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic struct clk_hw *vc7_of_clk_get(struct of_phandle_args *clkspec, 17862306a36Sopenharmony_ci void *data) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct vc7_driver_data *vc7 = data; 18162306a36Sopenharmony_ci unsigned int idx = clkspec->args[0]; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (idx >= vc7->chip_info->num_outputs) 18462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return &vc7->clk_out[idx].hw; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic const unsigned int RC21008A_index_to_output_mapping[] = { 19062306a36Sopenharmony_ci 1, 2, 3, 6, 7, 8, 10, 11 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int vc7_map_index_to_output(const enum vc7_model model, const unsigned int i) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci switch (model) { 19662306a36Sopenharmony_ci case VC7_RC21008A: 19762306a36Sopenharmony_ci return RC21008A_index_to_output_mapping[i]; 19862306a36Sopenharmony_ci default: 19962306a36Sopenharmony_ci return i; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci/* bank to output mapping, same across all variants */ 20462306a36Sopenharmony_cistatic const unsigned int output_bank_mapping[] = { 20562306a36Sopenharmony_ci 0, /* Output 0 */ 20662306a36Sopenharmony_ci 1, /* Output 1 */ 20762306a36Sopenharmony_ci 2, /* Output 2 */ 20862306a36Sopenharmony_ci 2, /* Output 3 */ 20962306a36Sopenharmony_ci 3, /* Output 4 */ 21062306a36Sopenharmony_ci 3, /* Output 5 */ 21162306a36Sopenharmony_ci 3, /* Output 6 */ 21262306a36Sopenharmony_ci 3, /* Output 7 */ 21362306a36Sopenharmony_ci 4, /* Output 8 */ 21462306a36Sopenharmony_ci 4, /* Output 9 */ 21562306a36Sopenharmony_ci 5, /* Output 10 */ 21662306a36Sopenharmony_ci 6 /* Output 11 */ 21762306a36Sopenharmony_ci}; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/** 22062306a36Sopenharmony_ci * vc7_64_mul_64_to_128() - Multiply two u64 and return an unsigned 128-bit integer 22162306a36Sopenharmony_ci * as an upper and lower part. 22262306a36Sopenharmony_ci * 22362306a36Sopenharmony_ci * @left: The left argument. 22462306a36Sopenharmony_ci * @right: The right argument. 22562306a36Sopenharmony_ci * @hi: The upper 64-bits of the 128-bit product. 22662306a36Sopenharmony_ci * @lo: The lower 64-bits of the 128-bit product. 22762306a36Sopenharmony_ci * 22862306a36Sopenharmony_ci * From mul_64_64 in crypto/ecc.c:350 in the linux kernel, accessed in v5.17.2. 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_cistatic void vc7_64_mul_64_to_128(u64 left, u64 right, u64 *hi, u64 *lo) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci u64 a0 = left & 0xffffffffull; 23362306a36Sopenharmony_ci u64 a1 = left >> 32; 23462306a36Sopenharmony_ci u64 b0 = right & 0xffffffffull; 23562306a36Sopenharmony_ci u64 b1 = right >> 32; 23662306a36Sopenharmony_ci u64 m0 = a0 * b0; 23762306a36Sopenharmony_ci u64 m1 = a0 * b1; 23862306a36Sopenharmony_ci u64 m2 = a1 * b0; 23962306a36Sopenharmony_ci u64 m3 = a1 * b1; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci m2 += (m0 >> 32); 24262306a36Sopenharmony_ci m2 += m1; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Overflow */ 24562306a36Sopenharmony_ci if (m2 < m1) 24662306a36Sopenharmony_ci m3 += 0x100000000ull; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci *lo = (m0 & 0xffffffffull) | (m2 << 32); 24962306a36Sopenharmony_ci *hi = m3 + (m2 >> 32); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/** 25362306a36Sopenharmony_ci * vc7_128_div_64_to_64() - Divides a 128-bit uint by a 64-bit divisor, return a 64-bit quotient. 25462306a36Sopenharmony_ci * 25562306a36Sopenharmony_ci * @numhi: The uppper 64-bits of the dividend. 25662306a36Sopenharmony_ci * @numlo: The lower 64-bits of the dividend. 25762306a36Sopenharmony_ci * @den: The denominator (divisor). 25862306a36Sopenharmony_ci * @r: The remainder, pass NULL if the remainder is not needed. 25962306a36Sopenharmony_ci * 26062306a36Sopenharmony_ci * Originally from libdivide, modified to use kernel u64/u32 types. 26162306a36Sopenharmony_ci * 26262306a36Sopenharmony_ci * See https://github.com/ridiculousfish/libdivide/blob/master/libdivide.h#L471. 26362306a36Sopenharmony_ci * 26462306a36Sopenharmony_ci * Return: The 64-bit quotient of the division. 26562306a36Sopenharmony_ci * 26662306a36Sopenharmony_ci * In case of overflow of division by zero, max(u64) is returned. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_cistatic u64 vc7_128_div_64_to_64(u64 numhi, u64 numlo, u64 den, u64 *r) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci /* 27162306a36Sopenharmony_ci * We work in base 2**32. 27262306a36Sopenharmony_ci * A uint32 holds a single digit. A uint64 holds two digits. 27362306a36Sopenharmony_ci * Our numerator is conceptually [num3, num2, num1, num0]. 27462306a36Sopenharmony_ci * Our denominator is [den1, den0]. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_ci const u64 b = ((u64)1 << 32); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* The high and low digits of our computed quotient. */ 27962306a36Sopenharmony_ci u32 q1, q0; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* The normalization shift factor */ 28262306a36Sopenharmony_ci int shift; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* 28562306a36Sopenharmony_ci * The high and low digits of our denominator (after normalizing). 28662306a36Sopenharmony_ci * Also the low 2 digits of our numerator (after normalizing). 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci u32 den1, den0, num1, num0; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* A partial remainder; */ 29162306a36Sopenharmony_ci u64 rem; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* 29462306a36Sopenharmony_ci * The estimated quotient, and its corresponding remainder (unrelated 29562306a36Sopenharmony_ci * to true remainder). 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci u64 qhat, rhat; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* Variables used to correct the estimated quotient. */ 30062306a36Sopenharmony_ci u64 c1, c2; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Check for overflow and divide by 0. */ 30362306a36Sopenharmony_ci if (numhi >= den) { 30462306a36Sopenharmony_ci if (r) 30562306a36Sopenharmony_ci *r = ~0ull; 30662306a36Sopenharmony_ci return ~0ull; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* 31062306a36Sopenharmony_ci * Determine the normalization factor. We multiply den by this, so that 31162306a36Sopenharmony_ci * its leading digit is at least half b. In binary this means just 31262306a36Sopenharmony_ci * shifting left by the number of leading zeros, so that there's a 1 in 31362306a36Sopenharmony_ci * the MSB. 31462306a36Sopenharmony_ci * 31562306a36Sopenharmony_ci * We also shift numer by the same amount. This cannot overflow because 31662306a36Sopenharmony_ci * numhi < den. The expression (-shift & 63) is the same as (64 - 31762306a36Sopenharmony_ci * shift), except it avoids the UB of shifting by 64. The funny bitwise 31862306a36Sopenharmony_ci * 'and' ensures that numlo does not get shifted into numhi if shift is 31962306a36Sopenharmony_ci * 0. clang 11 has an x86 codegen bug here: see LLVM bug 50118. The 32062306a36Sopenharmony_ci * sequence below avoids it. 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_ci shift = __builtin_clzll(den); 32362306a36Sopenharmony_ci den <<= shift; 32462306a36Sopenharmony_ci numhi <<= shift; 32562306a36Sopenharmony_ci numhi |= (numlo >> (-shift & 63)) & (-(s64)shift >> 63); 32662306a36Sopenharmony_ci numlo <<= shift; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* 32962306a36Sopenharmony_ci * Extract the low digits of the numerator and both digits of the 33062306a36Sopenharmony_ci * denominator. 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ci num1 = (u32)(numlo >> 32); 33362306a36Sopenharmony_ci num0 = (u32)(numlo & 0xFFFFFFFFu); 33462306a36Sopenharmony_ci den1 = (u32)(den >> 32); 33562306a36Sopenharmony_ci den0 = (u32)(den & 0xFFFFFFFFu); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* 33862306a36Sopenharmony_ci * We wish to compute q1 = [n3 n2 n1] / [d1 d0]. 33962306a36Sopenharmony_ci * Estimate q1 as [n3 n2] / [d1], and then correct it. 34062306a36Sopenharmony_ci * Note while qhat may be 2 digits, q1 is always 1 digit. 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_ci qhat = div64_u64_rem(numhi, den1, &rhat); 34362306a36Sopenharmony_ci c1 = qhat * den0; 34462306a36Sopenharmony_ci c2 = rhat * b + num1; 34562306a36Sopenharmony_ci if (c1 > c2) 34662306a36Sopenharmony_ci qhat -= (c1 - c2 > den) ? 2 : 1; 34762306a36Sopenharmony_ci q1 = (u32)qhat; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* Compute the true (partial) remainder. */ 35062306a36Sopenharmony_ci rem = numhi * b + num1 - q1 * den; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* 35362306a36Sopenharmony_ci * We wish to compute q0 = [rem1 rem0 n0] / [d1 d0]. 35462306a36Sopenharmony_ci * Estimate q0 as [rem1 rem0] / [d1] and correct it. 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_ci qhat = div64_u64_rem(rem, den1, &rhat); 35762306a36Sopenharmony_ci c1 = qhat * den0; 35862306a36Sopenharmony_ci c2 = rhat * b + num0; 35962306a36Sopenharmony_ci if (c1 > c2) 36062306a36Sopenharmony_ci qhat -= (c1 - c2 > den) ? 2 : 1; 36162306a36Sopenharmony_ci q0 = (u32)qhat; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* Return remainder if requested. */ 36462306a36Sopenharmony_ci if (r) 36562306a36Sopenharmony_ci *r = (rem * b + num0 - q0 * den) >> shift; 36662306a36Sopenharmony_ci return ((u64)q1 << 32) | q0; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int vc7_get_bank_clk(struct vc7_driver_data *vc7, 37062306a36Sopenharmony_ci unsigned int bank_idx, 37162306a36Sopenharmony_ci unsigned int output_bank_src, 37262306a36Sopenharmony_ci struct vc7_bank_src_map *map) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci /* Mapping from Table 38 in datasheet */ 37562306a36Sopenharmony_ci if (bank_idx == 0 || bank_idx == 1) { 37662306a36Sopenharmony_ci switch (output_bank_src) { 37762306a36Sopenharmony_ci case 0: 37862306a36Sopenharmony_ci map->type = VC7_IOD, 37962306a36Sopenharmony_ci map->src.iod = &vc7->clk_iod[0]; 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci case 1: 38262306a36Sopenharmony_ci map->type = VC7_IOD, 38362306a36Sopenharmony_ci map->src.iod = &vc7->clk_iod[1]; 38462306a36Sopenharmony_ci return 0; 38562306a36Sopenharmony_ci case 4: 38662306a36Sopenharmony_ci map->type = VC7_FOD, 38762306a36Sopenharmony_ci map->src.fod = &vc7->clk_fod[0]; 38862306a36Sopenharmony_ci return 0; 38962306a36Sopenharmony_ci case 5: 39062306a36Sopenharmony_ci map->type = VC7_FOD, 39162306a36Sopenharmony_ci map->src.fod = &vc7->clk_fod[1]; 39262306a36Sopenharmony_ci return 0; 39362306a36Sopenharmony_ci default: 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci } else if (bank_idx == 2) { 39762306a36Sopenharmony_ci switch (output_bank_src) { 39862306a36Sopenharmony_ci case 1: 39962306a36Sopenharmony_ci map->type = VC7_IOD, 40062306a36Sopenharmony_ci map->src.iod = &vc7->clk_iod[1]; 40162306a36Sopenharmony_ci return 0; 40262306a36Sopenharmony_ci case 4: 40362306a36Sopenharmony_ci map->type = VC7_FOD, 40462306a36Sopenharmony_ci map->src.fod = &vc7->clk_fod[0]; 40562306a36Sopenharmony_ci return 0; 40662306a36Sopenharmony_ci case 5: 40762306a36Sopenharmony_ci map->type = VC7_FOD, 40862306a36Sopenharmony_ci map->src.fod = &vc7->clk_fod[1]; 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci default: 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci } else if (bank_idx == 3) { 41462306a36Sopenharmony_ci switch (output_bank_src) { 41562306a36Sopenharmony_ci case 4: 41662306a36Sopenharmony_ci map->type = VC7_FOD, 41762306a36Sopenharmony_ci map->src.fod = &vc7->clk_fod[0]; 41862306a36Sopenharmony_ci return 0; 41962306a36Sopenharmony_ci case 5: 42062306a36Sopenharmony_ci map->type = VC7_FOD, 42162306a36Sopenharmony_ci map->src.fod = &vc7->clk_fod[1]; 42262306a36Sopenharmony_ci return 0; 42362306a36Sopenharmony_ci case 6: 42462306a36Sopenharmony_ci map->type = VC7_FOD, 42562306a36Sopenharmony_ci map->src.fod = &vc7->clk_fod[2]; 42662306a36Sopenharmony_ci return 0; 42762306a36Sopenharmony_ci default: 42862306a36Sopenharmony_ci break; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci } else if (bank_idx == 4) { 43162306a36Sopenharmony_ci switch (output_bank_src) { 43262306a36Sopenharmony_ci case 0: 43362306a36Sopenharmony_ci /* CLKIN1 not supported in this driver */ 43462306a36Sopenharmony_ci break; 43562306a36Sopenharmony_ci case 2: 43662306a36Sopenharmony_ci map->type = VC7_IOD, 43762306a36Sopenharmony_ci map->src.iod = &vc7->clk_iod[2]; 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci case 5: 44062306a36Sopenharmony_ci map->type = VC7_FOD, 44162306a36Sopenharmony_ci map->src.fod = &vc7->clk_fod[1]; 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci case 6: 44462306a36Sopenharmony_ci map->type = VC7_FOD, 44562306a36Sopenharmony_ci map->src.fod = &vc7->clk_fod[2]; 44662306a36Sopenharmony_ci return 0; 44762306a36Sopenharmony_ci case 7: 44862306a36Sopenharmony_ci /* CLKIN0 not supported in this driver */ 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci default: 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci } else if (bank_idx == 5) { 45462306a36Sopenharmony_ci switch (output_bank_src) { 45562306a36Sopenharmony_ci case 0: 45662306a36Sopenharmony_ci /* CLKIN1 not supported in this driver */ 45762306a36Sopenharmony_ci break; 45862306a36Sopenharmony_ci case 1: 45962306a36Sopenharmony_ci /* XIN_REFIN not supported in this driver */ 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci case 2: 46262306a36Sopenharmony_ci map->type = VC7_IOD, 46362306a36Sopenharmony_ci map->src.iod = &vc7->clk_iod[2]; 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci case 3: 46662306a36Sopenharmony_ci map->type = VC7_IOD, 46762306a36Sopenharmony_ci map->src.iod = &vc7->clk_iod[3]; 46862306a36Sopenharmony_ci return 0; 46962306a36Sopenharmony_ci case 5: 47062306a36Sopenharmony_ci map->type = VC7_FOD, 47162306a36Sopenharmony_ci map->src.fod = &vc7->clk_fod[1]; 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_ci case 6: 47462306a36Sopenharmony_ci map->type = VC7_FOD, 47562306a36Sopenharmony_ci map->src.fod = &vc7->clk_fod[2]; 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci case 7: 47862306a36Sopenharmony_ci /* CLKIN0 not supported in this driver */ 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci default: 48162306a36Sopenharmony_ci break; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci } else if (bank_idx == 6) { 48462306a36Sopenharmony_ci switch (output_bank_src) { 48562306a36Sopenharmony_ci case 0: 48662306a36Sopenharmony_ci /* CLKIN1 not supported in this driver */ 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci case 2: 48962306a36Sopenharmony_ci map->type = VC7_IOD, 49062306a36Sopenharmony_ci map->src.iod = &vc7->clk_iod[2]; 49162306a36Sopenharmony_ci return 0; 49262306a36Sopenharmony_ci case 3: 49362306a36Sopenharmony_ci map->type = VC7_IOD, 49462306a36Sopenharmony_ci map->src.iod = &vc7->clk_iod[3]; 49562306a36Sopenharmony_ci return 0; 49662306a36Sopenharmony_ci case 5: 49762306a36Sopenharmony_ci map->type = VC7_FOD, 49862306a36Sopenharmony_ci map->src.fod = &vc7->clk_fod[1]; 49962306a36Sopenharmony_ci return 0; 50062306a36Sopenharmony_ci case 6: 50162306a36Sopenharmony_ci map->type = VC7_FOD, 50262306a36Sopenharmony_ci map->src.fod = &vc7->clk_fod[2]; 50362306a36Sopenharmony_ci return 0; 50462306a36Sopenharmony_ci case 7: 50562306a36Sopenharmony_ci /* CLKIN0 not supported in this driver */ 50662306a36Sopenharmony_ci break; 50762306a36Sopenharmony_ci default: 50862306a36Sopenharmony_ci break; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci pr_warn("bank_src%d = %d is not supported\n", bank_idx, output_bank_src); 51362306a36Sopenharmony_ci return -1; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic int vc7_read_apll(struct vc7_driver_data *vc7) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci int err; 51962306a36Sopenharmony_ci u32 val32; 52062306a36Sopenharmony_ci u16 val16; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci err = regmap_bulk_read(vc7->regmap, 52362306a36Sopenharmony_ci VC7_REG_XO_CNFG, 52462306a36Sopenharmony_ci (u32 *)&val32, 52562306a36Sopenharmony_ci VC7_REG_XO_CNFG_COUNT); 52662306a36Sopenharmony_ci if (err) { 52762306a36Sopenharmony_ci dev_err(&vc7->client->dev, "failed to read XO_CNFG\n"); 52862306a36Sopenharmony_ci return err; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci vc7->clk_apll.xo_ib_h_div = (val32 & VC7_REG_XO_IB_H_DIV_MASK) 53262306a36Sopenharmony_ci >> VC7_REG_XO_IB_H_DIV_SHIFT; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci err = regmap_read(vc7->regmap, 53562306a36Sopenharmony_ci VC7_REG_APLL_CNFG, 53662306a36Sopenharmony_ci &val32); 53762306a36Sopenharmony_ci if (err) { 53862306a36Sopenharmony_ci dev_err(&vc7->client->dev, "failed to read APLL_CNFG\n"); 53962306a36Sopenharmony_ci return err; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci vc7->clk_apll.en_doubler = val32 & VC7_REG_APLL_EN_DOUBLER; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci err = regmap_bulk_read(vc7->regmap, 54562306a36Sopenharmony_ci VC7_REG_APLL_FB_DIV_FRAC, 54662306a36Sopenharmony_ci (u32 *)&val32, 54762306a36Sopenharmony_ci VC7_REG_APLL_FB_DIV_FRAC_COUNT); 54862306a36Sopenharmony_ci if (err) { 54962306a36Sopenharmony_ci dev_err(&vc7->client->dev, "failed to read APLL_FB_DIV_FRAC\n"); 55062306a36Sopenharmony_ci return err; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci vc7->clk_apll.apll_fb_div_frac = val32 & VC7_REG_APLL_FB_DIV_FRAC_MASK; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci err = regmap_bulk_read(vc7->regmap, 55662306a36Sopenharmony_ci VC7_REG_APLL_FB_DIV_INT, 55762306a36Sopenharmony_ci (u16 *)&val16, 55862306a36Sopenharmony_ci VC7_REG_APLL_FB_DIV_INT_COUNT); 55962306a36Sopenharmony_ci if (err) { 56062306a36Sopenharmony_ci dev_err(&vc7->client->dev, "failed to read APLL_FB_DIV_INT\n"); 56162306a36Sopenharmony_ci return err; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci vc7->clk_apll.apll_fb_div_int = val16 & VC7_REG_APLL_FB_DIV_INT_MASK; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci return 0; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic int vc7_read_fod(struct vc7_driver_data *vc7, unsigned int idx) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci int err; 57262306a36Sopenharmony_ci u64 val; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci err = regmap_bulk_read(vc7->regmap, 57562306a36Sopenharmony_ci VC7_REG_FOD_INT_CNFG(idx), 57662306a36Sopenharmony_ci (u64 *)&val, 57762306a36Sopenharmony_ci VC7_REG_FOD_INT_CNFG_COUNT); 57862306a36Sopenharmony_ci if (err) { 57962306a36Sopenharmony_ci dev_err(&vc7->client->dev, "failed to read FOD%d\n", idx); 58062306a36Sopenharmony_ci return err; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci vc7->clk_fod[idx].fod_1st_int = (val & VC7_REG_FOD_1ST_INT_MASK); 58462306a36Sopenharmony_ci vc7->clk_fod[idx].fod_2nd_int = 58562306a36Sopenharmony_ci (val & VC7_REG_FOD_2ND_INT_MASK) >> VC7_REG_FOD_2ND_INT_SHIFT; 58662306a36Sopenharmony_ci vc7->clk_fod[idx].fod_frac = (val & VC7_REG_FOD_FRAC_MASK) 58762306a36Sopenharmony_ci >> VC7_REG_FOD_FRAC_SHIFT; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic int vc7_write_fod(struct vc7_driver_data *vc7, unsigned int idx) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci int err; 59562306a36Sopenharmony_ci u64 val; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* 59862306a36Sopenharmony_ci * FOD dividers are part of an atomic group where fod_1st_int, 59962306a36Sopenharmony_ci * fod_2nd_int, and fod_frac must be written together. The new divider 60062306a36Sopenharmony_ci * is applied when the MSB of fod_frac is written. 60162306a36Sopenharmony_ci */ 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci err = regmap_bulk_read(vc7->regmap, 60462306a36Sopenharmony_ci VC7_REG_FOD_INT_CNFG(idx), 60562306a36Sopenharmony_ci (u64 *)&val, 60662306a36Sopenharmony_ci VC7_REG_FOD_INT_CNFG_COUNT); 60762306a36Sopenharmony_ci if (err) { 60862306a36Sopenharmony_ci dev_err(&vc7->client->dev, "failed to read FOD%d\n", idx); 60962306a36Sopenharmony_ci return err; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci val = u64_replace_bits(val, 61362306a36Sopenharmony_ci vc7->clk_fod[idx].fod_1st_int, 61462306a36Sopenharmony_ci VC7_REG_FOD_1ST_INT_MASK); 61562306a36Sopenharmony_ci val = u64_replace_bits(val, 61662306a36Sopenharmony_ci vc7->clk_fod[idx].fod_2nd_int, 61762306a36Sopenharmony_ci VC7_REG_FOD_2ND_INT_MASK); 61862306a36Sopenharmony_ci val = u64_replace_bits(val, 61962306a36Sopenharmony_ci vc7->clk_fod[idx].fod_frac, 62062306a36Sopenharmony_ci VC7_REG_FOD_FRAC_MASK); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci err = regmap_bulk_write(vc7->regmap, 62362306a36Sopenharmony_ci VC7_REG_FOD_INT_CNFG(idx), 62462306a36Sopenharmony_ci (u64 *)&val, 62562306a36Sopenharmony_ci sizeof(u64)); 62662306a36Sopenharmony_ci if (err) { 62762306a36Sopenharmony_ci dev_err(&vc7->client->dev, "failed to write FOD%d\n", idx); 62862306a36Sopenharmony_ci return err; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci return 0; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic int vc7_read_iod(struct vc7_driver_data *vc7, unsigned int idx) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci int err; 63762306a36Sopenharmony_ci u32 val; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci err = regmap_bulk_read(vc7->regmap, 64062306a36Sopenharmony_ci VC7_REG_IOD_INT_CNFG(idx), 64162306a36Sopenharmony_ci (u32 *)&val, 64262306a36Sopenharmony_ci VC7_REG_IOD_INT_CNFG_COUNT); 64362306a36Sopenharmony_ci if (err) { 64462306a36Sopenharmony_ci dev_err(&vc7->client->dev, "failed to read IOD%d\n", idx); 64562306a36Sopenharmony_ci return err; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci vc7->clk_iod[idx].iod_int = (val & VC7_REG_IOD_INT_MASK); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return 0; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic int vc7_write_iod(struct vc7_driver_data *vc7, unsigned int idx) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci int err; 65662306a36Sopenharmony_ci u32 val; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* 65962306a36Sopenharmony_ci * IOD divider field is atomic and all bits must be written. 66062306a36Sopenharmony_ci * The new divider is applied when the MSB of iod_int is written. 66162306a36Sopenharmony_ci */ 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci err = regmap_bulk_read(vc7->regmap, 66462306a36Sopenharmony_ci VC7_REG_IOD_INT_CNFG(idx), 66562306a36Sopenharmony_ci (u32 *)&val, 66662306a36Sopenharmony_ci VC7_REG_IOD_INT_CNFG_COUNT); 66762306a36Sopenharmony_ci if (err) { 66862306a36Sopenharmony_ci dev_err(&vc7->client->dev, "failed to read IOD%d\n", idx); 66962306a36Sopenharmony_ci return err; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci val = u32_replace_bits(val, 67362306a36Sopenharmony_ci vc7->clk_iod[idx].iod_int, 67462306a36Sopenharmony_ci VC7_REG_IOD_INT_MASK); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci err = regmap_bulk_write(vc7->regmap, 67762306a36Sopenharmony_ci VC7_REG_IOD_INT_CNFG(idx), 67862306a36Sopenharmony_ci (u32 *)&val, 67962306a36Sopenharmony_ci sizeof(u32)); 68062306a36Sopenharmony_ci if (err) { 68162306a36Sopenharmony_ci dev_err(&vc7->client->dev, "failed to write IOD%d\n", idx); 68262306a36Sopenharmony_ci return err; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return 0; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int vc7_read_output(struct vc7_driver_data *vc7, unsigned int idx) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci int err; 69162306a36Sopenharmony_ci unsigned int val, out_num; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci out_num = vc7_map_index_to_output(vc7->chip_info->model, idx); 69462306a36Sopenharmony_ci err = regmap_read(vc7->regmap, 69562306a36Sopenharmony_ci VC7_REG_ODRV_EN(out_num), 69662306a36Sopenharmony_ci &val); 69762306a36Sopenharmony_ci if (err) { 69862306a36Sopenharmony_ci dev_err(&vc7->client->dev, "failed to read ODRV_EN[%d]\n", idx); 69962306a36Sopenharmony_ci return err; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci vc7->clk_out[idx].out_dis = val & VC7_REG_OUT_DIS; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci return 0; 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic int vc7_write_output(struct vc7_driver_data *vc7, unsigned int idx) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci int err; 71062306a36Sopenharmony_ci unsigned int out_num; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci out_num = vc7_map_index_to_output(vc7->chip_info->model, idx); 71362306a36Sopenharmony_ci err = regmap_write_bits(vc7->regmap, 71462306a36Sopenharmony_ci VC7_REG_ODRV_EN(out_num), 71562306a36Sopenharmony_ci VC7_REG_OUT_DIS, 71662306a36Sopenharmony_ci vc7->clk_out[idx].out_dis); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (err) { 71962306a36Sopenharmony_ci dev_err(&vc7->client->dev, "failed to write ODRV_EN[%d]\n", idx); 72062306a36Sopenharmony_ci return err; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci return 0; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic unsigned long vc7_get_apll_rate(struct vc7_driver_data *vc7) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci int err; 72962306a36Sopenharmony_ci unsigned long xtal_rate; 73062306a36Sopenharmony_ci u64 refin_div, apll_rate; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci xtal_rate = clk_get_rate(vc7->pin_xin); 73362306a36Sopenharmony_ci err = vc7_read_apll(vc7); 73462306a36Sopenharmony_ci if (err) { 73562306a36Sopenharmony_ci dev_err(&vc7->client->dev, "unable to read apll\n"); 73662306a36Sopenharmony_ci return err; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* 0 is bypassed, 1 is reserved */ 74062306a36Sopenharmony_ci if (vc7->clk_apll.xo_ib_h_div < 2) 74162306a36Sopenharmony_ci refin_div = xtal_rate; 74262306a36Sopenharmony_ci else 74362306a36Sopenharmony_ci refin_div = div64_u64(xtal_rate, vc7->clk_apll.xo_ib_h_div); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (vc7->clk_apll.en_doubler) 74662306a36Sopenharmony_ci refin_div *= 2; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* divider = int + (frac / 2^27) */ 74962306a36Sopenharmony_ci apll_rate = (refin_div * vc7->clk_apll.apll_fb_div_int) + 75062306a36Sopenharmony_ci ((refin_div * vc7->clk_apll.apll_fb_div_frac) >> VC7_APLL_DENOMINATOR_BITS); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci pr_debug("%s - xo_ib_h_div: %u, apll_fb_div_int: %u, apll_fb_div_frac: %u\n", 75362306a36Sopenharmony_ci __func__, vc7->clk_apll.xo_ib_h_div, vc7->clk_apll.apll_fb_div_int, 75462306a36Sopenharmony_ci vc7->clk_apll.apll_fb_div_frac); 75562306a36Sopenharmony_ci pr_debug("%s - refin_div: %llu, apll rate: %llu\n", 75662306a36Sopenharmony_ci __func__, refin_div, apll_rate); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci return apll_rate; 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic void vc7_calc_iod_divider(unsigned long rate, unsigned long parent_rate, 76262306a36Sopenharmony_ci u32 *divider) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci *divider = DIV_ROUND_UP(parent_rate, rate); 76562306a36Sopenharmony_ci if (*divider < VC7_IOD_MIN_DIVISOR) 76662306a36Sopenharmony_ci *divider = VC7_IOD_MIN_DIVISOR; 76762306a36Sopenharmony_ci if (*divider > VC7_IOD_MAX_DIVISOR) 76862306a36Sopenharmony_ci *divider = VC7_IOD_MAX_DIVISOR; 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic void vc7_calc_fod_1st_stage(unsigned long rate, unsigned long parent_rate, 77262306a36Sopenharmony_ci u32 *div_int, u64 *div_frac) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci u64 rem; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci *div_int = (u32)div64_u64_rem(parent_rate, rate, &rem); 77762306a36Sopenharmony_ci *div_frac = div64_u64(rem << VC7_FOD_DENOMINATOR_BITS, rate); 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic unsigned long vc7_calc_fod_1st_stage_rate(unsigned long parent_rate, 78162306a36Sopenharmony_ci u32 fod_1st_int, u64 fod_frac) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci u64 numer, denom, hi, lo, divisor; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci numer = fod_frac; 78662306a36Sopenharmony_ci denom = BIT_ULL(VC7_FOD_DENOMINATOR_BITS); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (fod_frac) { 78962306a36Sopenharmony_ci vc7_64_mul_64_to_128(parent_rate, denom, &hi, &lo); 79062306a36Sopenharmony_ci divisor = ((u64)fod_1st_int * denom) + numer; 79162306a36Sopenharmony_ci return vc7_128_div_64_to_64(hi, lo, divisor, NULL); 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci return div64_u64(parent_rate, fod_1st_int); 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic unsigned long vc7_calc_fod_2nd_stage_rate(unsigned long parent_rate, 79862306a36Sopenharmony_ci u32 fod_1st_int, u32 fod_2nd_int, u64 fod_frac) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci unsigned long fod_1st_stage_rate; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci fod_1st_stage_rate = vc7_calc_fod_1st_stage_rate(parent_rate, fod_1st_int, fod_frac); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if (fod_2nd_int < 2) 80562306a36Sopenharmony_ci return fod_1st_stage_rate; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* 80862306a36Sopenharmony_ci * There is a div-by-2 preceding the 2nd stage integer divider 80962306a36Sopenharmony_ci * (not shown on block diagram) so the actual 2nd stage integer 81062306a36Sopenharmony_ci * divisor is 2 * N. 81162306a36Sopenharmony_ci */ 81262306a36Sopenharmony_ci return div64_u64(fod_1st_stage_rate >> 1, fod_2nd_int); 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic void vc7_calc_fod_divider(unsigned long rate, unsigned long parent_rate, 81662306a36Sopenharmony_ci u32 *fod_1st_int, u32 *fod_2nd_int, u64 *fod_frac) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci unsigned int allow_frac, i, best_frac_i; 81962306a36Sopenharmony_ci unsigned long first_stage_rate; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci vc7_calc_fod_1st_stage(rate, parent_rate, fod_1st_int, fod_frac); 82262306a36Sopenharmony_ci first_stage_rate = vc7_calc_fod_1st_stage_rate(parent_rate, *fod_1st_int, *fod_frac); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci *fod_2nd_int = 0; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci /* Do we need the second stage integer divider? */ 82762306a36Sopenharmony_ci if (first_stage_rate < VC7_FOD_1ST_STAGE_RATE_MIN) { 82862306a36Sopenharmony_ci allow_frac = 0; 82962306a36Sopenharmony_ci best_frac_i = VC7_FOD_2ND_INT_MIN; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci for (i = VC7_FOD_2ND_INT_MIN; i <= VC7_FOD_2ND_INT_MAX; i++) { 83262306a36Sopenharmony_ci /* 83362306a36Sopenharmony_ci * 1) There is a div-by-2 preceding the 2nd stage integer divider 83462306a36Sopenharmony_ci * (not shown on block diagram) so the actual 2nd stage integer 83562306a36Sopenharmony_ci * divisor is 2 * N. 83662306a36Sopenharmony_ci * 2) Attempt to find an integer solution first. This means stepping 83762306a36Sopenharmony_ci * through each 2nd stage integer and recalculating the 1st stage 83862306a36Sopenharmony_ci * until the 1st stage frequency is out of bounds. If no integer 83962306a36Sopenharmony_ci * solution is found, use the best fractional solution. 84062306a36Sopenharmony_ci */ 84162306a36Sopenharmony_ci vc7_calc_fod_1st_stage(parent_rate, rate * 2 * i, fod_1st_int, fod_frac); 84262306a36Sopenharmony_ci first_stage_rate = vc7_calc_fod_1st_stage_rate(parent_rate, 84362306a36Sopenharmony_ci *fod_1st_int, 84462306a36Sopenharmony_ci *fod_frac); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci /* Remember the first viable fractional solution */ 84762306a36Sopenharmony_ci if (best_frac_i == VC7_FOD_2ND_INT_MIN && 84862306a36Sopenharmony_ci first_stage_rate > VC7_FOD_1ST_STAGE_RATE_MIN) { 84962306a36Sopenharmony_ci best_frac_i = i; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* Is the divider viable? Prefer integer solutions over fractional. */ 85362306a36Sopenharmony_ci if (*fod_1st_int < VC7_FOD_1ST_INT_MAX && 85462306a36Sopenharmony_ci first_stage_rate >= VC7_FOD_1ST_STAGE_RATE_MIN && 85562306a36Sopenharmony_ci (allow_frac || *fod_frac == 0)) { 85662306a36Sopenharmony_ci *fod_2nd_int = i; 85762306a36Sopenharmony_ci break; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci /* Ran out of divisors or the 1st stage frequency is out of range */ 86162306a36Sopenharmony_ci if (i >= VC7_FOD_2ND_INT_MAX || 86262306a36Sopenharmony_ci first_stage_rate > VC7_FOD_1ST_STAGE_RATE_MAX) { 86362306a36Sopenharmony_ci allow_frac = 1; 86462306a36Sopenharmony_ci i = best_frac_i; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci /* Restore the best frac and rerun the loop for the last time */ 86762306a36Sopenharmony_ci if (best_frac_i != VC7_FOD_2ND_INT_MIN) 86862306a36Sopenharmony_ci i--; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci continue; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cistatic unsigned long vc7_fod_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci struct vc7_fod_data *fod = container_of(hw, struct vc7_fod_data, hw); 87962306a36Sopenharmony_ci struct vc7_driver_data *vc7 = fod->vc7; 88062306a36Sopenharmony_ci int err; 88162306a36Sopenharmony_ci unsigned long fod_rate; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci err = vc7_read_fod(vc7, fod->num); 88462306a36Sopenharmony_ci if (err) { 88562306a36Sopenharmony_ci dev_err(&vc7->client->dev, "error reading registers for %s\n", 88662306a36Sopenharmony_ci clk_hw_get_name(hw)); 88762306a36Sopenharmony_ci return err; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci pr_debug("%s - %s: parent_rate: %lu\n", __func__, clk_hw_get_name(hw), parent_rate); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci fod_rate = vc7_calc_fod_2nd_stage_rate(parent_rate, fod->fod_1st_int, 89362306a36Sopenharmony_ci fod->fod_2nd_int, fod->fod_frac); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci pr_debug("%s - %s: fod_1st_int: %u, fod_2nd_int: %u, fod_frac: %llu\n", 89662306a36Sopenharmony_ci __func__, clk_hw_get_name(hw), 89762306a36Sopenharmony_ci fod->fod_1st_int, fod->fod_2nd_int, fod->fod_frac); 89862306a36Sopenharmony_ci pr_debug("%s - %s rate: %lu\n", __func__, clk_hw_get_name(hw), fod_rate); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci return fod_rate; 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic long vc7_fod_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct vc7_fod_data *fod = container_of(hw, struct vc7_fod_data, hw); 90662306a36Sopenharmony_ci unsigned long fod_rate; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci pr_debug("%s - %s: requested rate: %lu, parent_rate: %lu\n", 90962306a36Sopenharmony_ci __func__, clk_hw_get_name(hw), rate, *parent_rate); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci vc7_calc_fod_divider(rate, *parent_rate, 91262306a36Sopenharmony_ci &fod->fod_1st_int, &fod->fod_2nd_int, &fod->fod_frac); 91362306a36Sopenharmony_ci fod_rate = vc7_calc_fod_2nd_stage_rate(*parent_rate, fod->fod_1st_int, 91462306a36Sopenharmony_ci fod->fod_2nd_int, fod->fod_frac); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci pr_debug("%s - %s: fod_1st_int: %u, fod_2nd_int: %u, fod_frac: %llu\n", 91762306a36Sopenharmony_ci __func__, clk_hw_get_name(hw), 91862306a36Sopenharmony_ci fod->fod_1st_int, fod->fod_2nd_int, fod->fod_frac); 91962306a36Sopenharmony_ci pr_debug("%s - %s rate: %lu\n", __func__, clk_hw_get_name(hw), fod_rate); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci return fod_rate; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic int vc7_fod_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci struct vc7_fod_data *fod = container_of(hw, struct vc7_fod_data, hw); 92762306a36Sopenharmony_ci struct vc7_driver_data *vc7 = fod->vc7; 92862306a36Sopenharmony_ci unsigned long fod_rate; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci pr_debug("%s - %s: rate: %lu, parent_rate: %lu\n", 93162306a36Sopenharmony_ci __func__, clk_hw_get_name(hw), rate, parent_rate); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci if (rate < VC7_FOD_RATE_MIN || rate > VC7_FOD_RATE_MAX) { 93462306a36Sopenharmony_ci dev_err(&vc7->client->dev, 93562306a36Sopenharmony_ci "requested frequency %lu Hz for %s is out of range\n", 93662306a36Sopenharmony_ci rate, clk_hw_get_name(hw)); 93762306a36Sopenharmony_ci return -EINVAL; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci vc7_write_fod(vc7, fod->num); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci fod_rate = vc7_calc_fod_2nd_stage_rate(parent_rate, fod->fod_1st_int, 94362306a36Sopenharmony_ci fod->fod_2nd_int, fod->fod_frac); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci pr_debug("%s - %s: fod_1st_int: %u, fod_2nd_int: %u, fod_frac: %llu\n", 94662306a36Sopenharmony_ci __func__, clk_hw_get_name(hw), 94762306a36Sopenharmony_ci fod->fod_1st_int, fod->fod_2nd_int, fod->fod_frac); 94862306a36Sopenharmony_ci pr_debug("%s - %s rate: %lu\n", __func__, clk_hw_get_name(hw), fod_rate); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci return 0; 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic const struct clk_ops vc7_fod_ops = { 95462306a36Sopenharmony_ci .recalc_rate = vc7_fod_recalc_rate, 95562306a36Sopenharmony_ci .round_rate = vc7_fod_round_rate, 95662306a36Sopenharmony_ci .set_rate = vc7_fod_set_rate, 95762306a36Sopenharmony_ci}; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic unsigned long vc7_iod_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci struct vc7_iod_data *iod = container_of(hw, struct vc7_iod_data, hw); 96262306a36Sopenharmony_ci struct vc7_driver_data *vc7 = iod->vc7; 96362306a36Sopenharmony_ci int err; 96462306a36Sopenharmony_ci unsigned long iod_rate; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci err = vc7_read_iod(vc7, iod->num); 96762306a36Sopenharmony_ci if (err) { 96862306a36Sopenharmony_ci dev_err(&vc7->client->dev, "error reading registers for %s\n", 96962306a36Sopenharmony_ci clk_hw_get_name(hw)); 97062306a36Sopenharmony_ci return err; 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci iod_rate = div64_u64(parent_rate, iod->iod_int); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci pr_debug("%s - %s: iod_int: %u\n", __func__, clk_hw_get_name(hw), iod->iod_int); 97662306a36Sopenharmony_ci pr_debug("%s - %s rate: %lu\n", __func__, clk_hw_get_name(hw), iod_rate); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci return iod_rate; 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic long vc7_iod_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci struct vc7_iod_data *iod = container_of(hw, struct vc7_iod_data, hw); 98462306a36Sopenharmony_ci unsigned long iod_rate; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci pr_debug("%s - %s: requested rate: %lu, parent_rate: %lu\n", 98762306a36Sopenharmony_ci __func__, clk_hw_get_name(hw), rate, *parent_rate); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci vc7_calc_iod_divider(rate, *parent_rate, &iod->iod_int); 99062306a36Sopenharmony_ci iod_rate = div64_u64(*parent_rate, iod->iod_int); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci pr_debug("%s - %s: iod_int: %u\n", __func__, clk_hw_get_name(hw), iod->iod_int); 99362306a36Sopenharmony_ci pr_debug("%s - %s rate: %ld\n", __func__, clk_hw_get_name(hw), iod_rate); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci return iod_rate; 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic int vc7_iod_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci struct vc7_iod_data *iod = container_of(hw, struct vc7_iod_data, hw); 100162306a36Sopenharmony_ci struct vc7_driver_data *vc7 = iod->vc7; 100262306a36Sopenharmony_ci unsigned long iod_rate; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci pr_debug("%s - %s: rate: %lu, parent_rate: %lu\n", 100562306a36Sopenharmony_ci __func__, clk_hw_get_name(hw), rate, parent_rate); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci if (rate < VC7_IOD_RATE_MIN || rate > VC7_IOD_RATE_MAX) { 100862306a36Sopenharmony_ci dev_err(&vc7->client->dev, 100962306a36Sopenharmony_ci "requested frequency %lu Hz for %s is out of range\n", 101062306a36Sopenharmony_ci rate, clk_hw_get_name(hw)); 101162306a36Sopenharmony_ci return -EINVAL; 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci vc7_write_iod(vc7, iod->num); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci iod_rate = div64_u64(parent_rate, iod->iod_int); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci pr_debug("%s - %s: iod_int: %u\n", __func__, clk_hw_get_name(hw), iod->iod_int); 101962306a36Sopenharmony_ci pr_debug("%s - %s rate: %ld\n", __func__, clk_hw_get_name(hw), iod_rate); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci return 0; 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cistatic const struct clk_ops vc7_iod_ops = { 102562306a36Sopenharmony_ci .recalc_rate = vc7_iod_recalc_rate, 102662306a36Sopenharmony_ci .round_rate = vc7_iod_round_rate, 102762306a36Sopenharmony_ci .set_rate = vc7_iod_set_rate, 102862306a36Sopenharmony_ci}; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cistatic int vc7_clk_out_prepare(struct clk_hw *hw) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci struct vc7_out_data *out = container_of(hw, struct vc7_out_data, hw); 103362306a36Sopenharmony_ci struct vc7_driver_data *vc7 = out->vc7; 103462306a36Sopenharmony_ci int err; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci out->out_dis = 0; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci err = vc7_write_output(vc7, out->num); 103962306a36Sopenharmony_ci if (err) { 104062306a36Sopenharmony_ci dev_err(&vc7->client->dev, "error writing registers for %s\n", 104162306a36Sopenharmony_ci clk_hw_get_name(hw)); 104262306a36Sopenharmony_ci return err; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci pr_debug("%s - %s: clk prepared\n", __func__, clk_hw_get_name(hw)); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci return 0; 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_cistatic void vc7_clk_out_unprepare(struct clk_hw *hw) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci struct vc7_out_data *out = container_of(hw, struct vc7_out_data, hw); 105362306a36Sopenharmony_ci struct vc7_driver_data *vc7 = out->vc7; 105462306a36Sopenharmony_ci int err; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci out->out_dis = 1; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci err = vc7_write_output(vc7, out->num); 105962306a36Sopenharmony_ci if (err) { 106062306a36Sopenharmony_ci dev_err(&vc7->client->dev, "error writing registers for %s\n", 106162306a36Sopenharmony_ci clk_hw_get_name(hw)); 106262306a36Sopenharmony_ci return; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci pr_debug("%s - %s: clk unprepared\n", __func__, clk_hw_get_name(hw)); 106662306a36Sopenharmony_ci} 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_cistatic int vc7_clk_out_is_enabled(struct clk_hw *hw) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci struct vc7_out_data *out = container_of(hw, struct vc7_out_data, hw); 107162306a36Sopenharmony_ci struct vc7_driver_data *vc7 = out->vc7; 107262306a36Sopenharmony_ci int err, is_enabled; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci err = vc7_read_output(vc7, out->num); 107562306a36Sopenharmony_ci if (err) { 107662306a36Sopenharmony_ci dev_err(&vc7->client->dev, "error reading registers for %s\n", 107762306a36Sopenharmony_ci clk_hw_get_name(hw)); 107862306a36Sopenharmony_ci return err; 107962306a36Sopenharmony_ci } 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci is_enabled = !out->out_dis; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci pr_debug("%s - %s: is_enabled=%d\n", __func__, clk_hw_get_name(hw), is_enabled); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci return is_enabled; 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cistatic const struct clk_ops vc7_clk_out_ops = { 108962306a36Sopenharmony_ci .prepare = vc7_clk_out_prepare, 109062306a36Sopenharmony_ci .unprepare = vc7_clk_out_unprepare, 109162306a36Sopenharmony_ci .is_enabled = vc7_clk_out_is_enabled, 109262306a36Sopenharmony_ci}; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_cistatic int vc7_probe(struct i2c_client *client) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci struct vc7_driver_data *vc7; 109762306a36Sopenharmony_ci struct clk_init_data clk_init; 109862306a36Sopenharmony_ci struct vc7_bank_src_map bank_src_map; 109962306a36Sopenharmony_ci const char *node_name, *apll_name; 110062306a36Sopenharmony_ci const char *parent_names[1]; 110162306a36Sopenharmony_ci unsigned int i, val, bank_idx, out_num; 110262306a36Sopenharmony_ci unsigned long apll_rate; 110362306a36Sopenharmony_ci int ret; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci vc7 = devm_kzalloc(&client->dev, sizeof(*vc7), GFP_KERNEL); 110662306a36Sopenharmony_ci if (!vc7) 110762306a36Sopenharmony_ci return -ENOMEM; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci i2c_set_clientdata(client, vc7); 111062306a36Sopenharmony_ci vc7->client = client; 111162306a36Sopenharmony_ci vc7->chip_info = i2c_get_match_data(client); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci vc7->pin_xin = devm_clk_get(&client->dev, "xin"); 111462306a36Sopenharmony_ci if (PTR_ERR(vc7->pin_xin) == -EPROBE_DEFER) { 111562306a36Sopenharmony_ci return dev_err_probe(&client->dev, -EPROBE_DEFER, 111662306a36Sopenharmony_ci "xin not specified\n"); 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci vc7->regmap = devm_regmap_init_i2c(client, &vc7_regmap_config); 112062306a36Sopenharmony_ci if (IS_ERR(vc7->regmap)) { 112162306a36Sopenharmony_ci return dev_err_probe(&client->dev, PTR_ERR(vc7->regmap), 112262306a36Sopenharmony_ci "failed to allocate register map\n"); 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci if (of_property_read_string(client->dev.of_node, "clock-output-names", 112662306a36Sopenharmony_ci &node_name)) 112762306a36Sopenharmony_ci node_name = client->dev.of_node->name; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci /* Register APLL */ 113062306a36Sopenharmony_ci apll_rate = vc7_get_apll_rate(vc7); 113162306a36Sopenharmony_ci apll_name = kasprintf(GFP_KERNEL, "%s_apll", node_name); 113262306a36Sopenharmony_ci vc7->clk_apll.clk = clk_register_fixed_rate(&client->dev, apll_name, 113362306a36Sopenharmony_ci __clk_get_name(vc7->pin_xin), 113462306a36Sopenharmony_ci 0, apll_rate); 113562306a36Sopenharmony_ci kfree(apll_name); /* ccf made a copy of the name */ 113662306a36Sopenharmony_ci if (IS_ERR(vc7->clk_apll.clk)) { 113762306a36Sopenharmony_ci return dev_err_probe(&client->dev, PTR_ERR(vc7->clk_apll.clk), 113862306a36Sopenharmony_ci "failed to register apll\n"); 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci /* Register FODs */ 114262306a36Sopenharmony_ci for (i = 0; i < VC7_NUM_FOD; i++) { 114362306a36Sopenharmony_ci memset(&clk_init, 0, sizeof(clk_init)); 114462306a36Sopenharmony_ci clk_init.name = kasprintf(GFP_KERNEL, "%s_fod%d", node_name, i); 114562306a36Sopenharmony_ci clk_init.ops = &vc7_fod_ops; 114662306a36Sopenharmony_ci clk_init.parent_names = parent_names; 114762306a36Sopenharmony_ci parent_names[0] = __clk_get_name(vc7->clk_apll.clk); 114862306a36Sopenharmony_ci clk_init.num_parents = 1; 114962306a36Sopenharmony_ci vc7->clk_fod[i].num = i; 115062306a36Sopenharmony_ci vc7->clk_fod[i].vc7 = vc7; 115162306a36Sopenharmony_ci vc7->clk_fod[i].hw.init = &clk_init; 115262306a36Sopenharmony_ci ret = devm_clk_hw_register(&client->dev, &vc7->clk_fod[i].hw); 115362306a36Sopenharmony_ci if (ret) 115462306a36Sopenharmony_ci goto err_clk_register; 115562306a36Sopenharmony_ci kfree(clk_init.name); /* ccf made a copy of the name */ 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci /* Register IODs */ 115962306a36Sopenharmony_ci for (i = 0; i < VC7_NUM_IOD; i++) { 116062306a36Sopenharmony_ci memset(&clk_init, 0, sizeof(clk_init)); 116162306a36Sopenharmony_ci clk_init.name = kasprintf(GFP_KERNEL, "%s_iod%d", node_name, i); 116262306a36Sopenharmony_ci clk_init.ops = &vc7_iod_ops; 116362306a36Sopenharmony_ci clk_init.parent_names = parent_names; 116462306a36Sopenharmony_ci parent_names[0] = __clk_get_name(vc7->clk_apll.clk); 116562306a36Sopenharmony_ci clk_init.num_parents = 1; 116662306a36Sopenharmony_ci vc7->clk_iod[i].num = i; 116762306a36Sopenharmony_ci vc7->clk_iod[i].vc7 = vc7; 116862306a36Sopenharmony_ci vc7->clk_iod[i].hw.init = &clk_init; 116962306a36Sopenharmony_ci ret = devm_clk_hw_register(&client->dev, &vc7->clk_iod[i].hw); 117062306a36Sopenharmony_ci if (ret) 117162306a36Sopenharmony_ci goto err_clk_register; 117262306a36Sopenharmony_ci kfree(clk_init.name); /* ccf made a copy of the name */ 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci /* Register outputs */ 117662306a36Sopenharmony_ci for (i = 0; i < vc7->chip_info->num_outputs; i++) { 117762306a36Sopenharmony_ci out_num = vc7_map_index_to_output(vc7->chip_info->model, i); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci /* 118062306a36Sopenharmony_ci * This driver does not support remapping FOD/IOD to banks. 118162306a36Sopenharmony_ci * The device state is read and the driver is setup to match 118262306a36Sopenharmony_ci * the device's existing mapping. 118362306a36Sopenharmony_ci */ 118462306a36Sopenharmony_ci bank_idx = output_bank_mapping[out_num]; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci regmap_read(vc7->regmap, VC7_REG_OUT_BANK_CNFG(bank_idx), &val); 118762306a36Sopenharmony_ci val &= VC7_REG_OUTPUT_BANK_SRC_MASK; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci memset(&bank_src_map, 0, sizeof(bank_src_map)); 119062306a36Sopenharmony_ci ret = vc7_get_bank_clk(vc7, bank_idx, val, &bank_src_map); 119162306a36Sopenharmony_ci if (ret) { 119262306a36Sopenharmony_ci dev_err_probe(&client->dev, ret, 119362306a36Sopenharmony_ci "unable to register output %d\n", i); 119462306a36Sopenharmony_ci return ret; 119562306a36Sopenharmony_ci } 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci switch (bank_src_map.type) { 119862306a36Sopenharmony_ci case VC7_FOD: 119962306a36Sopenharmony_ci parent_names[0] = clk_hw_get_name(&bank_src_map.src.fod->hw); 120062306a36Sopenharmony_ci break; 120162306a36Sopenharmony_ci case VC7_IOD: 120262306a36Sopenharmony_ci parent_names[0] = clk_hw_get_name(&bank_src_map.src.iod->hw); 120362306a36Sopenharmony_ci break; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci memset(&clk_init, 0, sizeof(clk_init)); 120762306a36Sopenharmony_ci clk_init.name = kasprintf(GFP_KERNEL, "%s_out%d", node_name, i); 120862306a36Sopenharmony_ci clk_init.ops = &vc7_clk_out_ops; 120962306a36Sopenharmony_ci clk_init.flags = CLK_SET_RATE_PARENT; 121062306a36Sopenharmony_ci clk_init.parent_names = parent_names; 121162306a36Sopenharmony_ci clk_init.num_parents = 1; 121262306a36Sopenharmony_ci vc7->clk_out[i].num = i; 121362306a36Sopenharmony_ci vc7->clk_out[i].vc7 = vc7; 121462306a36Sopenharmony_ci vc7->clk_out[i].hw.init = &clk_init; 121562306a36Sopenharmony_ci ret = devm_clk_hw_register(&client->dev, &vc7->clk_out[i].hw); 121662306a36Sopenharmony_ci if (ret) 121762306a36Sopenharmony_ci goto err_clk_register; 121862306a36Sopenharmony_ci kfree(clk_init.name); /* ccf made a copy of the name */ 121962306a36Sopenharmony_ci } 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci ret = of_clk_add_hw_provider(client->dev.of_node, vc7_of_clk_get, vc7); 122262306a36Sopenharmony_ci if (ret) { 122362306a36Sopenharmony_ci dev_err_probe(&client->dev, ret, "unable to add clk provider\n"); 122462306a36Sopenharmony_ci goto err_clk; 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci return ret; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_cierr_clk_register: 123062306a36Sopenharmony_ci dev_err_probe(&client->dev, ret, 123162306a36Sopenharmony_ci "unable to register %s\n", clk_init.name); 123262306a36Sopenharmony_ci kfree(clk_init.name); /* ccf made a copy of the name */ 123362306a36Sopenharmony_cierr_clk: 123462306a36Sopenharmony_ci clk_unregister_fixed_rate(vc7->clk_apll.clk); 123562306a36Sopenharmony_ci return ret; 123662306a36Sopenharmony_ci} 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_cistatic void vc7_remove(struct i2c_client *client) 123962306a36Sopenharmony_ci{ 124062306a36Sopenharmony_ci struct vc7_driver_data *vc7 = i2c_get_clientdata(client); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci of_clk_del_provider(client->dev.of_node); 124362306a36Sopenharmony_ci clk_unregister_fixed_rate(vc7->clk_apll.clk); 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_cistatic bool vc7_volatile_reg(struct device *dev, unsigned int reg) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci if (reg == VC7_PAGE_ADDR) 124962306a36Sopenharmony_ci return false; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci return true; 125262306a36Sopenharmony_ci} 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_cistatic const struct vc7_chip_info vc7_rc21008a_info = { 125562306a36Sopenharmony_ci .model = VC7_RC21008A, 125662306a36Sopenharmony_ci .num_banks = 6, 125762306a36Sopenharmony_ci .num_outputs = 8, 125862306a36Sopenharmony_ci}; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_cistatic struct regmap_range_cfg vc7_range_cfg[] = { 126162306a36Sopenharmony_ci{ 126262306a36Sopenharmony_ci .range_min = 0, 126362306a36Sopenharmony_ci .range_max = VC7_MAX_REG, 126462306a36Sopenharmony_ci .selector_reg = VC7_PAGE_ADDR, 126562306a36Sopenharmony_ci .selector_mask = 0xFF, 126662306a36Sopenharmony_ci .selector_shift = 0, 126762306a36Sopenharmony_ci .window_start = 0, 126862306a36Sopenharmony_ci .window_len = VC7_PAGE_WINDOW, 126962306a36Sopenharmony_ci}}; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_cistatic const struct regmap_config vc7_regmap_config = { 127262306a36Sopenharmony_ci .reg_bits = 8, 127362306a36Sopenharmony_ci .val_bits = 8, 127462306a36Sopenharmony_ci .max_register = VC7_MAX_REG, 127562306a36Sopenharmony_ci .ranges = vc7_range_cfg, 127662306a36Sopenharmony_ci .num_ranges = ARRAY_SIZE(vc7_range_cfg), 127762306a36Sopenharmony_ci .volatile_reg = vc7_volatile_reg, 127862306a36Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 127962306a36Sopenharmony_ci .can_multi_write = true, 128062306a36Sopenharmony_ci .reg_format_endian = REGMAP_ENDIAN_LITTLE, 128162306a36Sopenharmony_ci .val_format_endian = REGMAP_ENDIAN_LITTLE, 128262306a36Sopenharmony_ci}; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_cistatic const struct i2c_device_id vc7_i2c_id[] = { 128562306a36Sopenharmony_ci { "rc21008a", .driver_data = (kernel_ulong_t)&vc7_rc21008a_info }, 128662306a36Sopenharmony_ci {} 128762306a36Sopenharmony_ci}; 128862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, vc7_i2c_id); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_cistatic const struct of_device_id vc7_of_match[] = { 129162306a36Sopenharmony_ci { .compatible = "renesas,rc21008a", .data = &vc7_rc21008a_info }, 129262306a36Sopenharmony_ci {} 129362306a36Sopenharmony_ci}; 129462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, vc7_of_match); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_cistatic struct i2c_driver vc7_i2c_driver = { 129762306a36Sopenharmony_ci .driver = { 129862306a36Sopenharmony_ci .name = "vc7", 129962306a36Sopenharmony_ci .of_match_table = vc7_of_match, 130062306a36Sopenharmony_ci }, 130162306a36Sopenharmony_ci .probe = vc7_probe, 130262306a36Sopenharmony_ci .remove = vc7_remove, 130362306a36Sopenharmony_ci .id_table = vc7_i2c_id, 130462306a36Sopenharmony_ci}; 130562306a36Sopenharmony_cimodule_i2c_driver(vc7_i2c_driver); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 130862306a36Sopenharmony_ciMODULE_AUTHOR("Alex Helms <alexander.helms.jy@renesas.com"); 130962306a36Sopenharmony_ciMODULE_DESCRIPTION("Renesas Versaclock7 common clock framework driver"); 1310