tmp_kernel_5.15/drivers/clk/clk-si5324.c

1227 lines
32 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* clk-si5324.c - Si5324 clock driver
*
* Copyright (C) 2017-2018 Xilinx, Inc.
*
* Author: Venkateshwar Rao G <vgannava.xilinx.com>
* Leon Woestenberg <leon@sidebranch.com>
*/
#include <asm/div64.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/rational.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "clk-si5324.h"
#include "si5324.h"
#include "si5324drv.h"
struct si5324_driver_data;
/**
* struct si5324_parameters - si5324 core parameters
*
* @n1_hs_min: Minimum high-speed n1 output divider
* @n1_hs_max: Maximum high-speed n1 output divider
* @n1_hs: n1 high-speed output divider
* @nc1_ls_min: Minimum low-speed clkout1 output divider
* @nc1_ls_max: Maximum low-speed clkout1 output divider
* @nc1_ls: Clkout1 low-speed output divider
* @nc2_ls_min: Minimum low-speed clkout2 output divider
* @nc2_ls_max: Maximum low-speed clkout2 output divider
* @nc2_ls: Clkout2 low-speed output divider
* @n2_hs: High-speed feedback divider
* @n2_ls_min: Minimum low-speed feedback divider
* @n2_ls_max: Maximum low-speed feedback divider
* @n2_ls: Low-speed feedback divider
* @n31_min: Minimum input divider for clk1
* @n31_max: Maximum input divider for clk1
* @n31: Input divider for clk1
* @n32_min: Minimum input divider for clk2
* @n32_max: Maximum input divider for clk2
* @n32: Input divider for clk2
* @fin: Input frequency
* @fout: Output frequency
* @fosc: Osc frequency
* @best_delta_fout: Delta out frequency
* @best_fout: Best output frequency
* @best_n1_hs: Best high speed output divider
* @best_nc1_ls: Best low speed clkout1 divider
* @best_n2_hs: Best high speed feedback divider
* @best_n2_ls: Best low speed feedback divider
* @best_n3: Best input clock divider
* @valid: Validility
*/
struct si5324_parameters {
u32 n1_hs_min;
u32 n1_hs_max;
u32 n1_hs;
u32 nc1_ls_min;
u32 nc1_ls_max;
u32 nc1_ls;
u32 nc2_ls_min;
u32 nc2_ls_max;
u32 nc2_ls;
u32 n2_hs;
u32 n2_ls_min;
u32 n2_ls_max;
u32 n2_ls;
u32 n31_min;
u32 n31_max;
u32 n31;
u32 n32_min;
u32 n32_max;
u32 n32;
u64 fin;
u64 fout;
u64 fosc;
u64 best_delta_fout;
u64 best_fout;
u32 best_n1_hs;
u32 best_nc1_ls;
u32 best_n2_hs;
u32 best_n2_ls;
u32 best_n3;
int valid;
};
/**
* struct si5324_hw_data - Clock parameters
*
* @hw: Handle between common and hardware-specific interfaces
* @drvdata: Driver private data
* @num: Differential pair clock number
*/
struct si5324_hw_data {
struct clk_hw hw;
struct si5324_driver_data *drvdata;
unsigned char num;
};
/**
* struct si5324_driver_data - Driver parameters
* @client: I2C client pointer
* @regmap: Device's regmap
* @onecell: Clock onecell data
* @params: Device parameters
* @pxtal: Clock
* @pxtal_name: Clock name
* @xtal: Reference clock
* @pclkin1: Clock in 1
* @pclkin1_name: Clock in 1 name
* @clkin1: Differential input clock 1
* @pclkin2: Clock in 2
* @pclkin2_name: Clock in 2 name
* @clkin2: Differential input clock 2
* @pll: Pll clock
* @clkout: Output clock
* @rate_clkout0: Clock out 0 rate
* @rate_clkout1: Clock out 1 rate
*/
struct si5324_driver_data {
struct i2c_client *client;
struct regmap *regmap;
struct clk_onecell_data onecell;
struct si5324_parameters params;
struct clk *pxtal;
const char *pxtal_name;
struct clk_hw xtal;
struct clk *pclkin1;
const char *pclkin1_name;
struct clk_hw clkin1;
struct clk *pclkin2;
const char *pclkin2_name;
struct clk_hw clkin2;
struct si5324_hw_data pll;
struct si5324_hw_data *clkout;
unsigned long rate_clkout0;
unsigned long rate_clkout1;
};
static const char * const si5324_input_names[] = {
"xtal", "clkin1", "clkin2"
};
static const char * const si5324_pll_name = "pll";
static const char * const si5324_clkout_names[] = {
"clk0", "clk1"
};
enum si53xx_variant {
si5319,
si5324,
si5328
};
static const char * const si53xx_variant_name[] = {
"si5319", "si5324", "si5328"
};
/**
* si5324_reg_read - Read a single si5324 register.
*
* @drvdata: Device to read from.
* @reg: Register to read.
*
* This function reads data from a single register
*
* Return: Data of the register on success, error number on failure
*/
static inline int
si5324_reg_read(struct si5324_driver_data *drvdata, u8 reg)
{
u32 val;
int ret;
ret = regmap_read(drvdata->regmap, reg, &val);
if (ret < 0) {
dev_err(&drvdata->client->dev,
"unable to read from reg%02x\n", reg);
return ret;
}
return (u8)val;
}
/**
* si5324_bulk_read - Read multiple si5324 registers
*
* @drvdata: Device to read from
* @reg: First register to be read from
* @count: Number of registers
* @buf: Pointer to store read value
*
* This function reads from multiple registers which are in
* sequential order
*
* Return: Number of bytes read
*/
static inline int si5324_bulk_read(struct si5324_driver_data *drvdata,
u8 reg, u8 count, u8 *buf)
{
return regmap_bulk_read(drvdata->regmap, reg, buf, count);
}
/**
* si5324_reg_write - Write a single si5324 register.
*
* @drvdata: Device to write to.
* @reg: Register to write to.
* @val: Value to write.
*
* This function writes into a single register
*
* Return: Zero on success, a negative error number on failure.
*
*/
static inline int si5324_reg_write(struct si5324_driver_data *drvdata,
u8 reg, u8 val)
{
int ret = regmap_write(drvdata->regmap, reg, val);
dev_dbg(&drvdata->client->dev, "%s 0x%02x @%02d\n", __func__,
(int)val, (int)reg);
return ret;
}
/**
* si5324_bulk_write - Write into multiple si5324 registers
*
* @drvdata: Device to write to
* @reg: First register
* @count: Number of registers
* @buf: Block of data to be written
*
* This function writes into multiple registers.
*
* Return: Zero on success, a negative error number on failure.
*/
static inline int si5324_bulk_write(struct si5324_driver_data *drvdata,
u8 reg, u8 count, const u8 *buf)
{
return regmap_raw_write(drvdata->regmap, reg, buf, count);
}
/**
* si5324_set_bits - Set the value of a bitfield in a si5324 register
*
* @drvdata: Device to write to.
* @reg: Register to write to.
* @mask: Mask of bits to set.
* @val: Value to set (unshifted)
*
* This function set particular bits in register
*
* Return: Zero on success, a negative error number on failure.
*/
static inline int si5324_set_bits(struct si5324_driver_data *drvdata,
u8 reg, u8 mask, u8 val)
{
return regmap_update_bits(drvdata->regmap, reg, mask, val);
}
/**
* si5324_bulk_scatter_write - Write into multiple si5324 registers
*
* @drvdata: Device to write to
* @count: Number of registers
* @buf: Register and data to write
*
* This function writes into multiple registers which are need not
* to be in sequential order.
*
* Return: Number of bytes written
*/
static inline int
si5324_bulk_scatter_write(struct si5324_driver_data *drvdata,
u8 count, const u8 *buf)
{
int i;
int ret = 0;
for (i = 0; i < count; i++) {
ret = si5324_reg_write(drvdata, buf[i * 2], buf[i * 2 + 1]);
if (ret)
return ret;
}
return ret;
}
/**
* si5324_initialize - Initializes si5324 device
*
* @drvdata: Device instance
*
* This function initializes si5324 with the following settings
* Keep reset asserted for 20ms
* 1. freerun mode
* 2. Disable output clocks during calibration
* 3. Clock selection mode : default value, manual
* 4. output signal format : LVDS for clkout1, disable clkout2
* 5. CS_CA pin in ignored
* 6. Set lock time to 13.3ms
* 7. Enables the fastlock.
*
* Return: Zero on success, negative number on failure.
*/
static int si5324_initialize(struct si5324_driver_data *drvdata)
{
int ret = 0;
si5324_set_bits(drvdata, SI5324_RESET_CALIB,
SI5324_RST_ALL, SI5324_RST_ALL);
msleep(SI5324_RESET_DELAY_MS);
si5324_set_bits(drvdata, SI5324_RESET_CALIB, SI5324_RST_ALL, 0);
msleep(SI5324_RESET_DELAY_MS);
ret = si5324_reg_read(drvdata, SI5324_CONTROL);
if (ret < 0)
return ret;
si5324_reg_write(drvdata, SI5324_CONTROL,
(ret | SI5324_CONTROL_FREE_RUN));
ret = si5324_reg_read(drvdata, SI5324_CKSEL);
if (ret < 0)
return ret;
si5324_reg_write(drvdata, SI5324_CKSEL, (ret | SI5324_CKSEL_SQL_ICAL));
si5324_reg_write(drvdata, SI3324_AUTOSEL, SI5324_AUTOSEL_DEF);
si5324_reg_write(drvdata, SI5324_OUTPUT_SIGFMT,
SI5324_OUTPUT_SF1_DEFAULT);
ret = si5324_reg_read(drvdata, SI5324_DSBL_CLKOUT);
if (ret < 0)
return ret;
si5324_reg_write(drvdata, SI5324_DSBL_CLKOUT,
(ret | SI5324_DSBL_CLKOUT2));
ret = si5324_reg_read(drvdata, SI5324_POWERDOWN);
if (ret < 0)
return ret;
si5324_reg_write(drvdata, SI5324_POWERDOWN, (ret | SI5324_PD_CK2));
si5324_reg_write(drvdata, SI5324_FOS_LOCKT, SI5324_FOS_DEFAULT);
ret = si5324_reg_read(drvdata, SI5324_CK_ACTV_SEL);
if (ret < 0)
return ret;
si5324_reg_write(drvdata, SI5324_CK_ACTV_SEL, SI5324_CK_DEFAULT);
ret = si5324_reg_read(drvdata, SI5324_FASTLOCK);
if (ret < 0)
return ret;
si5324_reg_write(drvdata, SI5324_FASTLOCK, (ret | SI5324_FASTLOCK_EN));
return 0;
}
/**
* si5324_read_parameters - Reads clock divider parameters
*
* @drvdata: Device to read from
*
* This function reads the clock divider parameters into driver structure.
*
* Following table gives the buffer index, register number and
* register name with bit fields
* 0 25 N1_HS[2:0]
* 6 31 NC1_LS[19:16]
* 7 32 NC1_LS[15:8]
* 8 33 NC1_LS[7:0]
* 9 34 NC2_LS[19:16]
* 10 35 NC2_LS[15:8]
* 11 36 NC2_LS[7:0]
* 15 40 N2_HS[2:0] N2_LS[19:16]
* 16 41 N2_LS[15:8]
* 17 42 N2_LS[7:0]
* 18 43 N31[18:16]
* 19 44 N31[15:8]
* 20 45 N31[7:0]
* 21 46 N32[18:16]
* 22 47 N32[15:8]
* 23 48 N32[7:0]
*/
static void si5324_read_parameters(struct si5324_driver_data *drvdata)
{
u8 buf[SI5324_PARAM_LEN];
si5324_bulk_read(drvdata, SI5324_N1_HS, SI5324_N1_PARAM_LEN, &buf[0]);
si5324_bulk_read(drvdata, SI5324_NC1_LS_H, SI5324_NC_PARAM_LEN,
&buf[6]);
si5324_bulk_read(drvdata, SI5324_N2_HS_LS_H, SI5324_N2_PARAM_LEN,
&buf[15]);
drvdata->params.n1_hs = (buf[0] >> SI5324_N1_HS_VAL_SHIFT);
drvdata->params.n1_hs += 4;
drvdata->params.nc1_ls = ((buf[6] & SI5324_DIV_LS_MASK) <<
SI5324_HSHIFT) | (buf[7] << SI5324_LSHIFT) |
buf[8];
drvdata->params.nc1_ls += 1;
drvdata->params.nc2_ls = ((buf[9] & SI5324_DIV_LS_MASK) <<
SI5324_HSHIFT) | (buf[10] << SI5324_LSHIFT) |
buf[11];
drvdata->params.nc2_ls += 1;
drvdata->params.n2_ls = ((buf[15] & SI5324_DIV_LS_MASK) <<
SI5324_HSHIFT) | (buf[16] << SI5324_LSHIFT) |
buf[17];
drvdata->params.n2_ls += 1;
drvdata->params.n2_hs = buf[15] >> SI5324_N2_HS_LS_H_VAL_SHIFT;
drvdata->params.n2_hs += 4;
drvdata->params.n31 = ((buf[18] & SI5324_DIV_LS_MASK) <<
SI5324_HSHIFT) | (buf[19] << SI5324_LSHIFT) |
buf[20];
drvdata->params.n31 += 1;
drvdata->params.n32 = ((buf[21] & SI5324_DIV_LS_MASK) <<
SI5324_HSHIFT) | (buf[22] << SI5324_LSHIFT) |
buf[23];
drvdata->params.n32 += 1;
drvdata->params.valid = 1;
}
static bool si5324_regmap_is_volatile(struct device *dev, unsigned int reg)
{
return true;
}
/**
* si5324_regmap_is_readable - Checks the register is readable or not
*
* @dev: Registered device
* @reg: Register offset
*
* Checks the register is readable or not.
*
* Return: True if the register is reabdle, False if it is not readable.
*/
static bool si5324_regmap_is_readable(struct device *dev, unsigned int reg)
{
if ((reg > SI5324_POWERDOWN && reg < SI5324_FOS_LOCKT) ||
(reg > SI5324_N1_HS && reg < SI5324_NC1_LS_H) ||
(reg > SI5324_NC2_LS_L && reg < SI5324_N2_HS_LS_H) ||
(reg > SI5324_N32_CLKIN_L && reg < SI5324_FOS_CLKIN_RATE) ||
(reg > SI5324_FOS_CLKIN_RATE && reg < SI5324_PLL_ACTV_CLK) ||
reg > SI5324_SKEW2)
return false;
return true;
}
/**
* si5324_regmap_is_writable - Checks the register is writable or not
*
* @dev: Registered device
* @reg: Register offset
*
* Checks the register is writable or not.
*
* Return: True if the register is writeable, False if it's not writeable.
*/
static bool si5324_regmap_is_writeable(struct device *dev, unsigned int reg)
{
if ((reg > SI5324_POWERDOWN && reg < SI5324_FOS_LOCKT) ||
(reg > SI5324_N1_HS && reg < SI5324_NC1_LS_H) ||
(reg > SI5324_NC2_LS_L && reg < SI5324_N2_HS_LS_H) ||
(reg > SI5324_N32_CLKIN_L && reg < SI5324_FOS_CLKIN_RATE) ||
(reg > SI5324_FOS_CLKIN_RATE && reg < SI5324_PLL_ACTV_CLK) ||
reg > SI5324_SKEW2 ||
(reg >= SI5324_PLL_ACTV_CLK && reg <= SI5324_CLKIN_LOL_STATUS) ||
(reg >= SI5324_PARTNO_H && reg <= SI5324_PARTNO_L))
return false;
return true;
}
static const struct regmap_config si5324_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.cache_type = REGCACHE_RBTREE,
.max_register = 144,
.writeable_reg = si5324_regmap_is_writeable,
.readable_reg = si5324_regmap_is_readable,
.volatile_reg = si5324_regmap_is_volatile,
};
static int si5324_xtal_prepare(struct clk_hw *hw)
{
return 0;
}
static void si5324_xtal_unprepare(struct clk_hw *hw)
{
}
static const struct clk_ops si5324_xtal_ops = {
.prepare = si5324_xtal_prepare,
.unprepare = si5324_xtal_unprepare,
};
/**
* si5324_clkin_prepare - Prepare the clkin
*
* @hw: Handle between common and hardware-specific interfaces
*
* This function enables the particular clk
*
* Return: Zero on success, a negative error number on failure.
*/
static int si5324_clkin_prepare(struct clk_hw *hw)
{
int ret = 0;
struct si5324_driver_data *drvdata;
struct si5324_hw_data *hwdata =
container_of(hw, struct si5324_hw_data, hw);
if (hwdata->num == SI5324_CLKIN1) {
drvdata = container_of(hw, struct si5324_driver_data, clkin1);
ret = si5324_set_bits(drvdata, SI5324_CONTROL,
SI5324_CONTROL_FREE_RUN, 0);
ret = si5324_set_bits(drvdata, SI5324_POWERDOWN, SI5324_PD_CK1 |
SI5324_PD_CK2, SI5324_PD_CK2);
} else if (hwdata->num == SI5324_CLKIN2) {
drvdata = container_of(hw, struct si5324_driver_data, clkin2);
ret = si5324_set_bits(drvdata, SI5324_CONTROL,
SI5324_CONTROL_FREE_RUN, 0);
ret = si5324_set_bits(drvdata, SI5324_POWERDOWN, SI5324_PD_CK1 |
SI5324_PD_CK2, SI5324_PD_CK1);
}
return ret;
}
/**
* si5324_clkin_unprepare - Unprepare the clkin
*
* @hw: Clock hardware
*
* This function enables the particular clk.
*/
static void si5324_clkin_unprepare(struct clk_hw *hw)
{
struct si5324_driver_data *drvdata;
struct si5324_hw_data *hwdata =
container_of(hw, struct si5324_hw_data, hw);
if (hwdata->num == SI5324_CLKIN1) {
drvdata = container_of(hw, struct si5324_driver_data, clkin1);
si5324_set_bits(drvdata, SI5324_POWERDOWN,
SI5324_PD_CK1 | SI5324_PD_CK2, SI5324_PD_CK1);
} else if (hwdata->num == SI5324_CLKIN2) {
drvdata = container_of(hw, struct si5324_driver_data, clkin2);
si5324_set_bits(drvdata, SI5324_POWERDOWN,
SI5324_PD_CK1 | SI5324_PD_CK2, SI5324_PD_CK1);
}
}
static unsigned long si5324_clkin_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
return 0;
}
static const struct clk_ops si5324_clkin_ops = {
.prepare = si5324_clkin_prepare,
.unprepare = si5324_clkin_unprepare,
.recalc_rate = si5324_clkin_recalc_rate,
};
static int si5324_pll_reparent(struct si5324_driver_data *drvdata,
int num, enum si5324_pll_src parent)
{
if (parent == SI5324_PLL_SRC_XTAL) {
si5324_set_bits(drvdata, SI5324_CONTROL,
SI5324_CONTROL_FREE_RUN,
SI5324_CONTROL_FREE_RUN);
si5324_set_bits(drvdata, SI5324_POWERDOWN,
SI5324_PD_CK1 | SI5324_PD_CK2, SI5324_PD_CK1);
si5324_set_bits(drvdata, SI5324_CKSEL,
SI5324_CK_SEL << SI5324_CKSEL_SHIFT,
1 << SI5324_CKSEL_SHIFT);
} else if (parent == SI5324_PLL_SRC_CLKIN1) {
si5324_set_bits(drvdata, SI5324_CONTROL,
SI5324_CONTROL_FREE_RUN, 0);
si5324_set_bits(drvdata, SI5324_POWERDOWN,
SI5324_PD_CK1 | SI5324_PD_CK2, SI5324_PD_CK2);
si5324_set_bits(drvdata, SI5324_CKSEL,
SI5324_CK_SEL << SI5324_CKSEL_SHIFT, 0);
} else if (parent == SI5324_PLL_SRC_CLKIN2) {
si5324_set_bits(drvdata, SI5324_CONTROL,
SI5324_CONTROL_FREE_RUN, 0);
si5324_set_bits(drvdata, SI5324_POWERDOWN,
SI5324_PD_CK1 | SI5324_PD_CK2, SI5324_PD_CK1);
si5324_set_bits(drvdata, SI5324_CKSEL,
SI5324_CK_SEL << SI5324_CKSEL_SHIFT,
1 << SI5324_CKSEL_SHIFT);
}
return 0;
}
static unsigned char si5324_pll_get_parent(struct clk_hw *hw)
{
return 0;
}
/**
* si5324_pll_set_parent - Set parent of clock
*
* @hw: Handle between common and hardware-specific interfaces
* @index: Parent index
*
* This function sets the paraent of clock.
*
* Return: 0 on success, negative error number on failure
*/
static int si5324_pll_set_parent(struct clk_hw *hw, u8 index)
{
struct si5324_hw_data *hwdata =
container_of(hw, struct si5324_hw_data, hw);
enum si5324_pll_src parent;
if (index == SI5324_SRC_XTAL)
parent = SI5324_PLL_SRC_XTAL;
else if (index == SI5324_SRC_CLKIN1)
parent = SI5324_PLL_SRC_CLKIN1;
else if (index == SI5324_SRC_CLKIN2)
parent = SI5324_PLL_SRC_CLKIN2;
else
return -EINVAL;
return si5324_pll_reparent(hwdata->drvdata, hwdata->num, parent);
}
/**
* si5324_pll_recalc_rate - Recalculate clock frequency
*
* @hw: Handle between common and hardware-specific interfaces
* @parent_rate: Clock frequency of parent clock
*
* This function recalculate clock frequency.
*
* Return: Current clock frequency
*/
static unsigned long si5324_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
unsigned long rate;
struct si5324_hw_data *hwdata =
container_of(hw, struct si5324_hw_data, hw);
if (!hwdata->drvdata->params.valid)
si5324_read_parameters(hwdata->drvdata);
WARN_ON(!hwdata->drvdata->params.valid);
rate = parent_rate * hwdata->drvdata->params.n2_ls *
hwdata->drvdata->params.n2_hs;
dev_dbg(&hwdata->drvdata->client->dev,
"%s - %s: n2_ls = %u, n2_hs = %u, parent_rate = %lu, rate = %lu\n",
__func__, clk_hw_get_name(hw),
hwdata->drvdata->params.n2_ls, hwdata->drvdata->params.n2_hs,
parent_rate, (unsigned long)rate);
return rate;
}
static long si5324_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
return rate;
}
static int si5324_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
return 0;
}
static const struct clk_ops si5324_pll_ops = {
.set_parent = si5324_pll_set_parent,
.get_parent = si5324_pll_get_parent,
.recalc_rate = si5324_pll_recalc_rate,
.round_rate = si5324_pll_round_rate,
.set_rate = si5324_pll_set_rate,
};
static int si5324_clkout_set_drive_strength(
struct si5324_driver_data *drvdata, int num,
enum si5324_drive_strength drive)
{
return 0;
}
static int si5324_clkout_prepare(struct clk_hw *hw)
{
return 0;
}
static void si5324_clkout_unprepare(struct clk_hw *hw)
{
}
static unsigned long si5324_clkout_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
unsigned long rate;
struct si5324_hw_data *hwdata =
container_of(hw, struct si5324_hw_data, hw);
rate = hwdata->drvdata->rate_clkout0;
return rate;
}
/**
* si5324_clkout_round_rate - selects the closest value to requested one.
*
* @hw: Handle between common and hardware-specific interfaces
* @rate: Clock rate
* @parent_rate: Parent clock rate
*
* This function selects the rate closest to the requested one.
*
* Return: Clock rate on success, negative error number on failure
*/
static long si5324_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
u32 ncn_ls, n2_ls, n3n, actual_rate;
u8 n1_hs, n2_hs, bwsel;
int ret;
ret = si5324_calcfreqsettings(SI5324_REF_CLOCK, rate, &actual_rate,
&n1_hs, &ncn_ls, &n2_hs, &n2_ls, &n3n,
&bwsel);
if (ret < 0)
return ret;
return actual_rate;
}
static int si5324_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct si5324_hw_data *hwdata =
container_of(hw, struct si5324_hw_data, hw);
u32 ncn_ls, n2_ls, n3n, actual_rate;
u8 n1_hs, n2_hs, bwsel, buf[SI5324_OUT_REGS * 2];
int i, ret, rc;
ret = si5324_calcfreqsettings(SI5324_REF_CLOCK, rate, &actual_rate,
&n1_hs, &ncn_ls, &n2_hs, &n2_ls, &n3n,
&bwsel);
if (ret < 0)
return ret;
hwdata->drvdata->rate_clkout0 = rate;
i = 0;
/* Enable Free running mode */
buf[i] = SI5324_CONTROL;
buf[i + 1] = SI5324_FREE_RUN_EN;
i += 2;
/* Loop bandwidth */
buf[i] = SI5324_BWSEL;
buf[i + 1] = (bwsel << SI5324_BWSEL_SHIFT) | SI5324_BWSEL_DEF_VAL;
i += 2;
/* Enable reference clock 2 in free running mode */
buf[i] = SI5324_POWERDOWN;
/* Enable input clock 2, Disable input clock 1 */
buf[i + 1] = SI5324_PD_CK1_DIS;
i += 2;
/* N1_HS */
buf[i] = SI5324_N1_HS;
buf[i + 1] = n1_hs << SI5324_N1_HS_VAL_SHIFT;
i += 2;
/* NC1_LS */
buf[i] = SI5324_NC1_LS_H;
buf[i + 1] = (u8)((ncn_ls & 0x000F0000) >> 16);
buf[i + 2] = SI5324_NC1_LS_M;
buf[i + 3] = (u8)((ncn_ls & 0x0000FF00) >> 8);
buf[i + 4] = SI5324_NC1_LS_L;
buf[i + 5] = (u8)(ncn_ls & 0x000000FF);
i += 6;
/* N2_HS and N2_LS */
buf[i] = SI5324_N2_HS_LS_H;
buf[i + 1] = (n2_hs << SI5324_N2_HS_LS_H_VAL_SHIFT);
buf[i + 1] |= (u8)((n2_ls & 0x000F0000) >> 16);
buf[i + 2] = SI5324_N2_LS_H;
buf[i + 3] = (u8)((n2_ls & 0x0000FF00) >> 8);
buf[i + 4] = SI5324_N2_LS_L;
buf[i + 5] = (u8)(n2_ls & 0x000000FF);
i += 6;
/* N32 (CLKIN2 or XTAL in FREERUNNING mode) */
buf[i] = SI5324_N32_CLKIN_H;
buf[i + 2] = SI5324_N32_CLKIN_M;
buf[i + 4] = SI5324_N32_CLKIN_L;
buf[i + 1] = (u8)((n3n & 0x00070000) >> 16);
buf[i + 3] = (u8)((n3n & 0x0000FF00) >> 8);
buf[i + 5] = (u8)(n3n & 0x000000FF);
i += 6;
/* Start calibration */
buf[i] = SI5324_RESET_CALIB;
buf[i + 1] = SI5324_CALIB_EN;
i += 2;
hwdata->drvdata->params.valid = 0;
rc = si5324_bulk_scatter_write(hwdata->drvdata, SI5324_OUT_REGS, buf);
return rc;
}
static const struct clk_ops si5324_clkout_ops = {
.prepare = si5324_clkout_prepare,
.unprepare = si5324_clkout_unprepare,
.recalc_rate = si5324_clkout_recalc_rate,
.round_rate = si5324_clkout_round_rate,
.set_rate = si5324_clkout_set_rate,
};
static const struct of_device_id si5324_dt_ids[] = {
{ .compatible = "silabs,si5319" },
{ .compatible = "silabs,si5324" },
{ .compatible = "silabs,si5328" },
{ }
};
MODULE_DEVICE_TABLE(of, si5324_dt_ids);
static int si5324_dt_parse(struct i2c_client *client)
{
struct device_node *child, *np = client->dev.of_node;
struct si5324_platform_data *pdata;
struct property *prop;
const __be32 *p;
int num = 0;
u32 val;
if (!np)
return 0;
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
/*
* property silabs,pll-source : <num src>, [<..>]
* allow to selectively set pll source
*/
of_property_for_each_u32(np, "silabs,pll-source", prop, p, num) {
if (num >= 1) {
dev_err(&client->dev,
"invalid pll %d on pll-source prop\n", num);
return -EINVAL;
}
p = of_prop_next_u32(prop, p, &val);
if (!p) {
dev_err(&client->dev,
"missing pll-source for pll %d\n", num);
return -EINVAL;
}
switch (val) {
case 0:
dev_dbg(&client->dev, "using xtal as parent for pll\n");
pdata->pll_src = SI5324_PLL_SRC_XTAL;
break;
case 1:
dev_dbg(&client->dev,
"using clkin1 as parent for pll\n");
pdata->pll_src = SI5324_PLL_SRC_CLKIN1;
break;
case 2:
dev_dbg(&client->dev,
"using clkin2 as parent for pll\n");
pdata->pll_src = SI5324_PLL_SRC_CLKIN2;
break;
default:
dev_err(&client->dev,
"invalid parent %d for pll %d\n", val, num);
return -EINVAL;
}
}
/* per clkout properties */
for_each_child_of_node(np, child) {
if (of_property_read_u32(child, "reg", &num)) {
dev_err(&client->dev, "missing reg property of %s\n",
child->name);
goto put_child;
}
if (num >= 2) {
dev_err(&client->dev, "invalid clkout %d\n", num);
goto put_child;
}
if (!of_property_read_u32(child, "silabs,drive-strength",
&val)) {
switch (val) {
case SI5324_DRIVE_2MA:
case SI5324_DRIVE_4MA:
case SI5324_DRIVE_6MA:
case SI5324_DRIVE_8MA:
pdata->clkout[num].drive = val;
break;
default:
dev_err(&client->dev,
"invalid drive strength %d for clkout %d\n",
val, num);
goto put_child;
}
}
if (!of_property_read_u32(child, "clock-frequency", &val)) {
dev_dbg(&client->dev, "clock-frequency = %u\n", val);
pdata->clkout[num].rate = val;
} else {
dev_err(&client->dev,
"missing clock-frequency property of %s\n",
child->name);
goto put_child;
}
}
client->dev.platform_data = pdata;
return 0;
put_child:
of_node_put(child);
return -EINVAL;
}
static u8 instance;
static int si5324_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct si5324_platform_data *pdata;
struct si5324_driver_data *drvdata;
struct clk_init_data init;
struct clk *clk;
const char *parent_names[3];
char inst_names[NUM_NAME_IDS][MAX_NAME_LEN];
u8 num_parents, num_clocks;
int ret, n;
enum si53xx_variant variant = id->driver_data;
if (variant > si5328) {
dev_err(&client->dev, "si53xx device not present\n");
return -ENODEV;
}
dev_info(&client->dev, "%s probed\n", si53xx_variant_name[variant]);
ret = si5324_dt_parse(client);
if (ret)
return ret;
pdata = client->dev.platform_data;
if (!pdata)
return -EINVAL;
drvdata = devm_kzalloc(&client->dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->client = client;
drvdata->pxtal = devm_clk_get(&client->dev, "xtal");
drvdata->pclkin1 = devm_clk_get(&client->dev, "clkin1");
drvdata->pclkin2 = devm_clk_get(&client->dev, "clkin2");
if (PTR_ERR(drvdata->pxtal) == -EPROBE_DEFER ||
PTR_ERR(drvdata->pclkin1) == -EPROBE_DEFER ||
PTR_ERR(drvdata->pclkin2) == -EPROBE_DEFER)
return -EPROBE_DEFER;
drvdata->regmap = devm_regmap_init_i2c(client, &si5324_regmap_config);
if (IS_ERR(drvdata->regmap)) {
dev_err(&client->dev, "failed to allocate register map\n");
return PTR_ERR(drvdata->regmap);
}
i2c_set_clientdata(client, drvdata);
si5324_initialize(drvdata);
/* setup input clock configuration */
ret = si5324_pll_reparent(drvdata, 0, pdata->pll_src);
if (ret) {
dev_err(&client->dev,
"failed to reparent pll to %d\n",
pdata->pll_src);
return ret;
}
for (n = 0; n < SI5324_MAX_CLKOUTS; n++) {
ret = si5324_clkout_set_drive_strength(drvdata, n,
pdata->clkout[n].drive);
if (ret) {
dev_err(&client->dev,
"failed set drive strength of clkout%d to %d\n",
n, pdata->clkout[n].drive);
return ret;
}
}
if (!IS_ERR(drvdata->pxtal))
clk_prepare_enable(drvdata->pxtal);
if (!IS_ERR(drvdata->pclkin1))
clk_prepare_enable(drvdata->pclkin1);
if (!IS_ERR(drvdata->pclkin2))
clk_prepare_enable(drvdata->pclkin2);
/* create instance names by appending instance id */
for (n = 0; n < SI5324_SRC_CLKS; n++) {
sprintf(inst_names[n], "%s_%d", si5324_input_names[n],
instance);
}
sprintf(inst_names[3], "%s_%d", si5324_pll_name, instance);
for (n = 0; n < SI5324_MAX_CLKOUTS; n++) {
sprintf(inst_names[n + 4], "%s_%d", si5324_clkout_names[n],
instance);
}
/* register xtal input clock gate */
memset(&init, 0, sizeof(init));
init.name = inst_names[0];
init.ops = &si5324_xtal_ops;
init.flags = 0;
if (!IS_ERR(drvdata->pxtal)) {
drvdata->pxtal_name = __clk_get_name(drvdata->pxtal);
init.parent_names = &drvdata->pxtal_name;
init.num_parents = 1;
}
drvdata->xtal.init = &init;
clk = devm_clk_register(&client->dev, &drvdata->xtal);
if (IS_ERR(clk)) {
dev_err(&client->dev, "unable to register %s\n", init.name);
ret = PTR_ERR(clk);
goto err_clk;
}
/* register clkin1 input clock gate */
memset(&init, 0, sizeof(init));
init.name = inst_names[1];
init.ops = &si5324_clkin_ops;
if (!IS_ERR(drvdata->pclkin1)) {
drvdata->pclkin1_name = __clk_get_name(drvdata->pclkin1);
init.parent_names = &drvdata->pclkin1_name;
init.num_parents = 1;
}
drvdata->clkin1.init = &init;
clk = devm_clk_register(&client->dev, &drvdata->clkin1);
if (IS_ERR(clk)) {
dev_err(&client->dev, "unable to register %s\n",
init.name);
ret = PTR_ERR(clk);
goto err_clk;
}
/* register clkin2 input clock gate */
memset(&init, 0, sizeof(init));
init.name = inst_names[2];
init.ops = &si5324_clkin_ops;
if (!IS_ERR(drvdata->pclkin2)) {
drvdata->pclkin2_name = __clk_get_name(drvdata->pclkin2);
init.parent_names = &drvdata->pclkin2_name;
init.num_parents = 1;
}
drvdata->clkin2.init = &init;
clk = devm_clk_register(&client->dev, &drvdata->clkin2);
if (IS_ERR(clk)) {
dev_err(&client->dev, "unable to register %s\n",
init.name);
ret = PTR_ERR(clk);
goto err_clk;
}
/* Si5324 allows to mux xtal or clkin1 or clkin2 to PLL input */
num_parents = SI5324_SRC_CLKS;
parent_names[0] = inst_names[0];
parent_names[1] = inst_names[1];
parent_names[2] = inst_names[2];
/* register PLL */
drvdata->pll.drvdata = drvdata;
drvdata->pll.hw.init = &init;
memset(&init, 0, sizeof(init));
init.name = inst_names[3];
init.ops = &si5324_pll_ops;
init.flags = 0;
init.flags |= CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE;
init.parent_names = parent_names;
init.num_parents = num_parents;
clk = devm_clk_register(&client->dev, &drvdata->pll.hw);
if (IS_ERR(clk)) {
dev_err(&client->dev, "unable to register %s\n", init.name);
ret = PTR_ERR(clk);
goto err_clk;
}
/* register clk out divider */
num_clocks = 2;
num_parents = 1;
parent_names[0] = inst_names[3];
drvdata->clkout = devm_kzalloc(&client->dev, num_clocks *
sizeof(*drvdata->clkout), GFP_KERNEL);
drvdata->onecell.clk_num = num_clocks;
drvdata->onecell.clks = devm_kzalloc(&client->dev,
num_clocks *
sizeof(*drvdata->onecell.clks),
GFP_KERNEL);
if (WARN_ON(!drvdata->clkout) || !drvdata->onecell.clks) {
ret = -ENOMEM;
goto err_clk;
}
for (n = 0; n < num_clocks; n++) {
drvdata->clkout[n].num = n;
drvdata->clkout[n].drvdata = drvdata;
drvdata->clkout[n].hw.init = &init;
memset(&init, 0, sizeof(init));
init.name = inst_names[4 + n];
init.ops = &si5324_clkout_ops;
init.flags = 0;
init.flags |= CLK_SET_RATE_PARENT;
init.parent_names = parent_names;
init.num_parents = num_parents;
clk = devm_clk_register(&client->dev, &drvdata->clkout[n].hw);
if (IS_ERR(clk)) {
dev_err(&client->dev, "unable to register %s\n",
init.name);
ret = PTR_ERR(clk);
goto err_clk;
}
/* refer to output clock in onecell */
drvdata->onecell.clks[n] = clk;
/* set initial clkout rate */
if (pdata->clkout[n].rate != 0) {
int ret;
ret = clk_set_rate(clk, pdata->clkout[n].rate);
if (ret != 0) {
dev_err(&client->dev, "Cannot set rate : %d\n",
ret);
}
}
}
ret = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get,
&drvdata->onecell);
if (ret) {
dev_err(&client->dev, "unable to add clk provider\n");
goto err_clk;
}
dev_info(&client->dev, "%s probe successful\n",
si53xx_variant_name[variant]);
instance++;
return 0;
err_clk:
if (!IS_ERR(drvdata->pxtal))
clk_disable_unprepare(drvdata->pxtal);
if (!IS_ERR(drvdata->pclkin1))
clk_disable_unprepare(drvdata->pclkin1);
if (!IS_ERR(drvdata->pclkin2))
clk_disable_unprepare(drvdata->pclkin2);
return ret;
}
static int si5324_i2c_remove(struct i2c_client *client)
{
of_clk_del_provider(client->dev.of_node);
return 0;
}
static const struct i2c_device_id si5324_i2c_ids[] = {
{ "si5319", si5319 },
{ "si5324", si5324 },
{ "si5328", si5328 },
{ }
};
MODULE_DEVICE_TABLE(i2c, si5324_i2c_ids);
static struct i2c_driver si5324_driver = {
.driver = {
.name = "si5324",
.of_match_table = of_match_ptr(si5324_dt_ids),
},
.probe = si5324_i2c_probe,
.remove = si5324_i2c_remove,
.id_table = si5324_i2c_ids,
};
module_i2c_driver(si5324_driver);
MODULE_AUTHOR("Venkateshwar Rao G <vgannava@xilinx.com>");
MODULE_DESCRIPTION("Silicon Labs 5319/5324/5328 clock driver");
MODULE_LICENSE("GPL v2");