// SPDX-License-Identifier: GPL-2.0
/*
* Common clock framework driver for the Versaclock7 family of timing devices.
*
* Copyright (c) 2022 Renesas Electronics Corporation
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/i2c.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/swab.h>
/*
* 16-bit register address: the lower 8 bits of the register address come
* from the offset addr byte and the upper 8 bits come from the page register.
*/
#define VC7_PAGE_ADDR 0xFD
#define VC7_PAGE_WINDOW 256
#define VC7_MAX_REG 0x364
/* Maximum number of banks supported by VC7 */
#define VC7_NUM_BANKS 7
/* Maximum number of FODs supported by VC7 */
#define VC7_NUM_FOD 3
/* Maximum number of IODs supported by VC7 */
#define VC7_NUM_IOD 4
/* Maximum number of outputs supported by VC7 */
#define VC7_NUM_OUT 12
/* VCO valid range is 9.5 GHz to 10.7 GHz */
#define VC7_APLL_VCO_MIN 9500000000UL
#define VC7_APLL_VCO_MAX 10700000000UL
/* APLL denominator is fixed at 2^27 */
#define VC7_APLL_DENOMINATOR_BITS 27
/* FOD 1st stage denominator is fixed 2^34 */
#define VC7_FOD_DENOMINATOR_BITS 34
/* IOD can operate between 1kHz and 650MHz */
#define VC7_IOD_RATE_MIN 1000UL
#define VC7_IOD_RATE_MAX 650000000UL
#define VC7_IOD_MIN_DIVISOR 14
#define VC7_IOD_MAX_DIVISOR 0x1ffffff /* 25-bit */
#define VC7_FOD_RATE_MIN 1000UL
#define VC7_FOD_RATE_MAX 650000000UL
#define VC7_FOD_1ST_STAGE_RATE_MIN 33000000UL /* 33 MHz */
#define VC7_FOD_1ST_STAGE_RATE_MAX 650000000UL /* 650 MHz */
#define VC7_FOD_1ST_INT_MAX 324
#define VC7_FOD_2ND_INT_MIN 2
#define VC7_FOD_2ND_INT_MAX 0x1ffff /* 17-bit */
/* VC7 Registers */
#define VC7_REG_XO_CNFG 0x2C
#define VC7_REG_XO_CNFG_COUNT 4
#define VC7_REG_XO_IB_H_DIV_SHIFT 24
#define VC7_REG_XO_IB_H_DIV_MASK GENMASK(28, VC7_REG_XO_IB_H_DIV_SHIFT)
#define VC7_REG_APLL_FB_DIV_FRAC 0x120
#define VC7_REG_APLL_FB_DIV_FRAC_COUNT 4
#define VC7_REG_APLL_FB_DIV_FRAC_MASK GENMASK(26, 0)
#define VC7_REG_APLL_FB_DIV_INT 0x124
#define VC7_REG_APLL_FB_DIV_INT_COUNT 2
#define VC7_REG_APLL_FB_DIV_INT_MASK GENMASK(9, 0)
#define VC7_REG_APLL_CNFG 0x127
#define VC7_REG_APLL_EN_DOUBLER BIT(0)
#define VC7_REG_OUT_BANK_CNFG(idx) (0x280 + (0x4 * (idx)))
#define VC7_REG_OUTPUT_BANK_SRC_MASK GENMASK(2, 0)
#define VC7_REG_FOD_INT_CNFG(idx) (0x1E0 + (0x10 * (idx)))
#define VC7_REG_FOD_INT_CNFG_COUNT 8
#define VC7_REG_FOD_1ST_INT_MASK GENMASK(8, 0)
#define VC7_REG_FOD_2ND_INT_SHIFT 9
#define VC7_REG_FOD_2ND_INT_MASK GENMASK(25, VC7_REG_FOD_2ND_INT_SHIFT)
#define VC7_REG_FOD_FRAC_SHIFT 26
#define VC7_REG_FOD_FRAC_MASK GENMASK_ULL(59, VC7_REG_FOD_FRAC_SHIFT)
#define VC7_REG_IOD_INT_CNFG(idx) (0x1C0 + (0x8 * (idx)))
#define VC7_REG_IOD_INT_CNFG_COUNT 4
#define VC7_REG_IOD_INT_MASK GENMASK(24, 0)
#define VC7_REG_ODRV_EN(idx) (0x240 + (0x4 * (idx)))
#define VC7_REG_OUT_DIS BIT(0)
struct vc7_driver_data;
static const struct regmap_config vc7_regmap_config;
/* Supported Renesas VC7 models */
enum vc7_model {
VC7_RC21008A,
};
struct vc7_chip_info {
const enum vc7_model model;
const unsigned int banks[VC7_NUM_BANKS];
const unsigned int num_banks;
const unsigned int outputs[VC7_NUM_OUT];
const unsigned int num_outputs;
};
/*
* Changing the APLL frequency is currently not supported.
* The APLL will consist of an opaque block between the XO and FOD/IODs and
* its frequency will be computed based on the current state of the device.
*/
struct vc7_apll_data {
struct clk *clk;
struct vc7_driver_data *vc7;
u8 xo_ib_h_div;
u8 en_doubler;
u16 apll_fb_div_int;
u32 apll_fb_div_frac;
};
struct vc7_fod_data {
struct clk_hw hw;
struct vc7_driver_data *vc7;
unsigned int num;
u32 fod_1st_int;
u32 fod_2nd_int;
u64 fod_frac;
};
struct vc7_iod_data {
struct clk_hw hw;
struct vc7_driver_data *vc7;
unsigned int num;
u32 iod_int;
};
struct vc7_out_data {
struct clk_hw hw;
struct vc7_driver_data *vc7;
unsigned int num;
unsigned int out_dis;
};
struct vc7_driver_data {
struct i2c_client *client;
struct regmap *regmap;
const struct vc7_chip_info *chip_info;
struct clk *pin_xin;
struct vc7_apll_data clk_apll;
struct vc7_fod_data clk_fod[VC7_NUM_FOD];
struct vc7_iod_data clk_iod[VC7_NUM_IOD