// SPDX-License-Identifier: GPL-2.0
/*
* Driver for Silicon Labs Si5341/Si5340 Clock generator
* Copyright (C) 2019 Topic Embedded Products
* Author: Mike Looijmans <mike.looijmans@topic.nl>
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/gcd.h>
#include <linux/math64.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#define SI5341_MAX_NUM_OUTPUTS 10
#define SI5340_MAX_NUM_OUTPUTS 4
#define SI5341_NUM_SYNTH 5
#define SI5340_NUM_SYNTH 4
/* Range of the synthesizer fractional divider */
#define SI5341_SYNTH_N_MIN 10
#define SI5341_SYNTH_N_MAX 4095
/* The chip can get its input clock from 3 input pins or an XTAL */
/* There is one PLL running at 13500–14256 MHz */
#define SI5341_PLL_VCO_MIN 13500000000ull
#define SI5341_PLL_VCO_MAX 14256000000ull
/* The 5 frequency synthesizers obtain their input from the PLL */
struct clk_si5341_synth {
struct clk_hw hw;
struct clk_si5341 *data;
u8 index;
};
#define to_clk_si5341_synth(_hw) \
container_of(_hw, struct clk_si5341_synth, hw)
/* The output stages can be connected to any synth (full mux) */
struct clk_si5341_output {
struct clk_hw hw;
struct clk_si5341 *data;
u8 index;
};
#define to_clk_si5341_output(_hw) \
container_of(_hw, struct clk_si5341_output, hw)
struct clk_si5341 {
struct clk_hw hw;
struct regmap *regmap;
struct i2c_client *i2c_client;
struct clk_si5341_synth synth[SI5341_NUM_SYNTH];
struct clk_si5341_output clk[SI5341_MAX_NUM_OUTPUTS];
struct clk *pxtal;
const char *pxtal_name;
const u16 *reg_output_offset;
const u16 *reg_rdiv_offset;
u64 freq_vco; /* 13500–14256 MHz */
u8 num_outputs;
u8 num_synth;
};
#define to_clk_si5341(_hw) container_of(_hw, struct clk_si5341, hw)
struct clk_si5341_output_config {
u8 out_format_drv_bits;
u8 out_cm_ampl_bits;
bool synth_master;
bool always_on;
};
#define SI5341_PAGE 0x0001
#define SI5341_PN_BASE 0x0002
#define SI5341_DEVICE_REV 0x0005
#define SI5341_STATUS 0x000C
#define SI5341_SOFT_RST 0x001C
/* Input dividers (48-bit) */
#define SI5341_IN_PDIV(x) (0x0208 + ((x) * 10))
#define SI5341_IN_PSET(x) (0x020E + ((x) * 10))
/* PLL configuration */
#define SI5341_PLL_M_NUM 0x0235
#define SI5341_PLL_M_DEN 0x023B
/* Output configuration */
#define SI5341_OUT_CONFIG(output) \
((output)->data->reg_output_offset[(output)->index])
#define SI5341_OUT_FORMAT(output) (SI5341_OUT_CONFIG(output) + 1)
#define SI5341_OUT_CM(output) (SI5341_OUT_CONFIG(output) + 2)
#define SI5341_OUT_MUX_SEL(output) (SI5341_OUT_CONFIG(output) + 3)
#define SI5341_OUT_R_REG(output) \
((output)->data->reg_rdiv_offset[(output)->index])
/* Synthesize N divider */
#define SI5341_SYNTH_N_NUM(x) (0x0302 + ((x) * 11))
#define SI5341_SYNTH_N_DEN(x) (0x0308 + ((x) * 11))
#define SI5341_SYNTH_N_UPD(x) (0x030C + ((x) * 11))
/* Synthesizer output enable, phase bypass, power mode */
#define SI5341_SYNTH_N_CLK_TO_OUTX_EN 0x0A03
#define SI5341_SYNTH_N_PIBYP 0x0A04
#define SI5341_SYNTH_N_PDNB 0x0A05
#define SI5341_SYNTH_N_CLK_DIS 0x0B4A
#define SI5341_REGISTER_MAX 0xBFF
/* SI5341_OUT_CONFIG bits */
#define SI5341_OUT_CFG_PDN BIT(0)
#define SI5341_OUT_CFG_OE BIT(1)
#define SI5341_OUT_CFG_RDIV_FORCE2 BIT(2)
/* Static configuration (to be moved to firmware) */
struct si5341_reg_default {
u16 address;
u8 value;
};
/* Output configuration registers 0..9 are not quite logically organized */
static const u16 si5341_reg_output_offset[] = {
0x0108,
0x010D,
0x0112,
0x0117,
0x011C,
0x0121,
0x0126,
0x012B,
0x0130,
0x013A,
};
static const u16 si5340_reg_output_offset[] = {
0x0112,
0x0117,
0x0126,
0x012B,
};
/* The location of the R divider registers */
static const u16 <