/*
* clk-si5351.c: Silicon Laboratories Si5351A/B/C I2C Clock Generator
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
* Rabeeh Khoury <rabeeh@solid-run.com>
*
* References:
* [1] "Si5351A/B/C Data Sheet"
* http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf
* [2] "Manually Generating an Si5351 Register Map"
* http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/rational.h>
#include <linux/i2c.h>
#include <linux/of_platform.h>
#include <linux/platform_data/si5351.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <asm/div64.h>
#include "clk-si5351.h"
struct si5351_driver_data;
struct si5351_parameters {
unsigned long p1;
unsigned long p2;
unsigned long p3;
int valid;
};
struct si5351_hw_data {
struct clk_hw hw;
struct si5351_driver_data *drvdata;
struct si5351_parameters params;
unsigned char num;
};
struct si5351_driver_data {
enum si5351_variant variant;
struct i2c_client *client;
struct regmap *regmap;
struct clk_onecell_data onecell;
struct clk *pxtal;
const char *pxtal_name;
struct clk_hw xtal;
struct clk *pclkin;
const char *pclkin_name;
struct clk_hw clkin;
struct si5351_hw_data pll[2];
struct si5351_hw_data *msynth;
struct si5351_hw_data *clkout;
};
static const char const *si5351_input_names[] = {
"xtal", "clkin"
};
static const char const *si5351_pll_names[] = {
"plla", "pllb", "vxco"
};
static const char const *si5351_msynth_names[] = {
"ms0", "ms1", "ms2", "ms3", "ms4", "ms5", "ms6", "ms7"
};
static const char const *si5351_clkout_names[] = {
"clk0", "clk1", "clk2", "clk3", "clk4", "clk5", "clk6", "clk7"
};
/*
* Si5351 i2c regmap
*/
static inline u8 si5351_reg_read(struct si5351_driver_data *drvdata, u8 reg)
{
u32 val;
int ret;
ret = regmap_read(drvdata->regmap, reg, &val);
if (ret) {
dev_err(&drvdata->client->dev,
"unable to read from reg%02x\n", reg);
return 0;
}
return (u8)val;
}
static inline int si5351_bulk_read(struct si5351_driver_data *drvdata,
u8 reg, u8 count, u8 *buf)
{
return regmap_bulk_read(drvdata->regmap, reg, buf, count);
}
static inline int si5351_reg_write(struct si5351_driver_data *drvdata,
u8 reg, u8 val)
{
return regmap_write(drvdata->regmap, reg, val);
}
static inline int si5351_bulk_write(struct si5351_driver_data *drvdata,
u8 reg, u8 count, const u8 *buf)
{
return regmap_raw_write(drvdata->regmap, reg, buf, count);
}
static inline int si5351_set_bits(struct si5351_driver_data *drvdata,
u8 reg, u8 mask, u8 val)
{
return regmap_update_bits(drvdata->regmap, reg, mask, val);
}
static inline u8 si5351_msynth_params_address(int num)
{
if (num > 5)
return SI5351_CLK6_PARAMETERS + (num - 6);
return SI5351_CLK0_PARAMETERS + (SI5351_PARAMETERS_LENGTH * num);
}
static void si5351_read_parameters(struct si5351_driver_data *drvdata,
u8 reg, struct si5351_parameters *params)
{
u8 buf[SI5351_PARAMETERS_LENGTH];
switch (reg) {
case SI5351_CLK6_PARAMETERS:
case SI5351_CLK7_PARAMETERS:
buf[0] = si5351_reg_read(drvdata, reg);
params->p1 = buf[0];
params->p2 = 0;
params->p3 = 1;
break;
default:
si5351_bulk_read(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf);
params->p1 = ((buf[2] & 0x03) << 16) | (buf[3] << 8) | buf[4];
params->p2 = ((buf[5] & 0x0f) << 16) | (buf[6] << 8) | buf[7];
params->p3 = ((buf[5] & 0xf0) << 12) | (buf[0] << 8) | buf[1];
}
params->valid = 1;
}
static void si5351_write_parameters(struct si5351_driver_data *drvdata,
u8 reg, struct si5351_parameters *params)
{
u8 buf[SI5351_PARAMETERS_LENGTH];
switch (reg) {
case SI5351_CLK6_PARAMETERS:
case SI5351_CLK7_PARAMETERS:
buf[0] = params->p1 & 0xff;
si5351_reg_write(drvdata, reg, buf[0]);
break;
default:
buf[0] = ((params->p3 & 0x0ff00) >> 8) & 0xff;
buf[1] = params->p3 & 0xff;
/* save rdiv and divby4 */
buf[2] = si5351_reg_read(drvdata, reg + 2) & ~0x03;
buf[2] |= ((params->p1 & 0x30000) >> 16) & 0x03;
buf[3] = ((params->p1 & 0x0ff00) >> 8) & 0xff;
buf[4] = params->p1 & 0xff;
buf[5] = ((params->p3 & 0xf0000) >> 12) |
((params->p2 & 0xf0000) >> 16);
buf[6] = ((params->p2 & 0x0ff00) >> 8) & 0xff;
buf[7] = params->p2 & 0xff;
si5351_bulk_write(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf);
}
}
static bool si5351_regmap_is_volatile(struct device *dev, unsigned int reg)
{
switch (reg) {
case SI5351_DEVICE_STATUS:
case SI5351_INTERRUPT_STATUS:
case SI5351_PLL_RESET:
return true;
}
return false;
}
static bool si5351_regmap_is_writeable(struct device *dev, unsigned int reg)
{
/* reserved registers */
if (reg >= 4 && reg <= 8)
return false;
if (reg >= 10 && reg <= 14)
return false;
if (reg >= 173 && reg <= 176)
return false;
if (reg >= 178 && reg <= 182)
return false;
/* read-only */
if (reg == SI5351_DEVICE_STATUS)
return false;
return true;
}
static struct regmap_config si5351_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.cache_type = REGCACHE_RBTREE,
.max_register = 187,
.writeable_reg = si5351_regmap_is_writeable,
.volatile_reg = si5351_regmap_is_volatile,
};
/*
* Si5351 xtal clock input
*/
static int si5351_xtal_prepare(struct clk_hw *hw)
{
struct si5351_driver_data *drvdata =
container_of(hw, struct si5351_driver_data, xtal);
si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
SI5351_XTAL_ENABLE, SI5351_XTAL_ENABLE);
return 0;
}
static void si5351_xtal_unprepare(struct clk_hw *hw)
{
struct si5351_driver_data *drvdata =
container_of(hw, struct si5351_driver_data, xtal);
si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
SI5351_XTAL_ENABLE, 0);
}
static const struct clk_ops si5351_xtal_ops = {
.prepare = si5351_xtal_prepare,
.unprepare = si5351_xtal_unprepare,
};
/*
* Si5351 clkin clock input (Si5351C only)
*/
static int si5351_clkin_prepare(struct clk_hw *hw)
{
struct si5351_driver_data *drvdata =
container_of(hw, struct si5351_driver_data, clkin);
si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
SI5351_CLKIN_ENABLE, SI5351_CLKIN_ENABLE);
return 0;
}
static void si5351_clkin_unprepare(struct clk_hw *hw)
{
struct si5351_driver_data *drvdata =
container_of(hw, struct si5351_driver_data, clkin);
si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE,
SI5351_CLKIN_ENABLE, 0);
}
/*
* CMOS clock source constraints:
* The input frequency range of the PLL is 10Mhz to 40MHz.
* If CLKIN is >40MHz, the input divider must be used.
*/
static unsigned long si5351_clkin_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct si5351_driver_data *drvdata =
container_of(hw, struct si5351_driver_data, clkin);
unsigned long rate;
unsigned char idiv;
rate = parent_rate;
if (parent_rate > 160000000) {
idiv = SI5351_CLKIN_DIV_8;
rate /= 8;
} else if (parent_rate > 80000000) {
idiv = SI5351_CLKIN_DIV_4;
rate /= 4;
} else if (parent_rate > 40000000) {
idiv = SI5351_CLKIN_DIV_2;
rate /= 2;
} else {
idiv = SI5351_CLKIN_DIV_1;
}
si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE,
SI5351_CLKIN_DIV_MASK, idiv);
dev_dbg(&drvdata->client->dev, "%s - clkin div = %d, rate = %lu\n",
__func__, (1 << (idiv >> 6)), rate);
return rate;
}
static const struct clk_ops si5351_clkin_ops = {
.prepare = si5351_clkin_prepare,
.unprepare = si5351_clkin_unprepare,
.recalc_rate = si5351_clkin_recalc_rate,
};
/*
* Si5351 vxco clock input (Si5351B only)
*/
static int si5351_vxco_prepare(struct clk_hw *hw)
{
struct si5351_hw_data *hwdata =
container_of(hw, struct si53
评论0