Commit 6b42b896 authored by Rene Habraken's avatar Rene Habraken
Browse files

modified driver to latest version from link (from mail) Mike, all clk out are read succesful

parent c5da67a9
......@@ -18,11 +18,8 @@
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
......@@ -33,17 +30,17 @@
#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>
/* Standard module information, edit as appropriate */
MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
MODULE_DESCRIPTION("Si5341 driver");
MODULE_LICENSE("GPL");
#define DRIVER_NAME "si5341"
#define SI5341_NUM_INPUTS 4
#define SI5341_MAX_NUM_OUTPUTS 10
#define SI5340_MAX_NUM_OUTPUTS 4
......@@ -84,8 +81,8 @@ struct clk_si5341 {
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;
struct clk *input_clk[SI5341_NUM_INPUTS];
const char *input_clk_name[SI5341_NUM_INPUTS];
const u16 *reg_output_offset;
const u16 *reg_rdiv_offset;
u64 freq_vco; /* 13500–14256 MHz */
......@@ -106,10 +103,25 @@ struct clk_si5341_output_config {
#define SI5341_DEVICE_REV 0x0005
#define SI5341_STATUS 0x000C
#define SI5341_SOFT_RST 0x001C
#define SI5341_IN_SEL 0x0021
#define SI5341_XAXB_CFG 0x090E
#define SI5341_IN_EN 0x0949
#define SI5341_INX_TO_PFD_EN 0x094A
/* Input selection */
#define SI5341_IN_SEL_MASK 0x06
#define SI5341_IN_SEL_SHIFT 1
#define SI5341_IN_SEL_REGCTRL 0x01
#define SI5341_INX_TO_PFD_SHIFT 4
/* XTAL config bits */
#define SI5341_XAXB_CFG_EXTCLK_EN BIT(0)
#define SI5341_XAXB_CFG_PDNB BIT(1)
/* Input dividers (48-bit) */
#define SI5341_IN_PDIV(x) (0x0208 + ((x) * 10))
#define SI5341_IN_PSET(x) (0x020E + ((x) * 10))
#define SI5341_PX_UPD 0x0230
/* PLL configuration */
#define SI5341_PLL_M_NUM 0x0235
......@@ -148,6 +160,10 @@ struct si5341_reg_default {
u8 value;
};
static const char * const si5341_input_clock_names[] = {
"in0", "in1", "in2", "xtal"
};
/* Output configuration registers 0..9 are not quite logically organized */
static const u16 si5341_reg_output_offset[] = {
0x0108,
......@@ -418,7 +434,112 @@ static unsigned long si5341_clk_recalc_rate(struct clk_hw *hw,
return (unsigned long)res;
}
static int si5341_clk_get_selected_input(struct clk_si5341 *data)
{
int err;
u32 val;
err = regmap_read(data->regmap, SI5341_IN_SEL, &val);
if (err < 0)
return err;
return (val & SI5341_IN_SEL_MASK) >> SI5341_IN_SEL_SHIFT;
}
static u8 si5341_clk_get_parent(struct clk_hw *hw)
{
struct clk_si5341 *data = to_clk_si5341(hw);
int res = si5341_clk_get_selected_input(data);
if (res < 0)
return 0; /* Apparently we cannot report errors */
return res;
}
static int si5341_clk_reparent(struct clk_si5341 *data, u8 index)
{
int err;
u8 val;
val = (index << SI5341_IN_SEL_SHIFT) & SI5341_IN_SEL_MASK;
/* Enable register-based input selection */
val |= SI5341_IN_SEL_REGCTRL;
err = regmap_update_bits(data->regmap,
SI5341_IN_SEL, SI5341_IN_SEL_REGCTRL | SI5341_IN_SEL_MASK, val);
if (err < 0)
return err;
if (index < 3) {
/* Enable input buffer for selected input */
err = regmap_update_bits(data->regmap,
SI5341_IN_EN, 0x07, BIT(index));
if (err < 0)
return err;
/* Enables the input to phase detector */
err = regmap_update_bits(data->regmap, SI5341_INX_TO_PFD_EN,
0x7 << SI5341_INX_TO_PFD_SHIFT,
BIT(index + SI5341_INX_TO_PFD_SHIFT));
if (err < 0)
return err;
/* Power down XTAL oscillator and buffer */
err = regmap_update_bits(data->regmap, SI5341_XAXB_CFG,
SI5341_XAXB_CFG_PDNB, 0);
if (err < 0)
return err;
/*
* Set the P divider to "1". There's no explanation in the
* datasheet of these registers, but the clockbuilder software
* programs a "1" when the input is being used.
*/
err = regmap_write(data->regmap, SI5341_IN_PDIV(index), 1);
if (err < 0)
return err;
err = regmap_write(data->regmap, SI5341_IN_PSET(index), 1);
if (err < 0)
return err;
/* Set update PDIV bit */
err = regmap_write(data->regmap, SI5341_PX_UPD, BIT(index));
if (err < 0)
return err;
} else {
/* Disable all input buffers */
err = regmap_update_bits(data->regmap, SI5341_IN_EN, 0x07, 0);
if (err < 0)
return err;
/* Disable input to phase detector */
err = regmap_update_bits(data->regmap, SI5341_INX_TO_PFD_EN,
0x7 << SI5341_INX_TO_PFD_SHIFT, 0);
if (err < 0)
return err;
/* Power up XTAL oscillator and buffer */
err = regmap_update_bits(data->regmap, SI5341_XAXB_CFG,
SI5341_XAXB_CFG_PDNB, SI5341_XAXB_CFG_PDNB);
if (err < 0)
return err;
}
return 0;
}
static int si5341_clk_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_si5341 *data = to_clk_si5341(hw);
return si5341_clk_reparent(data, index);
}
static const struct clk_ops si5341_clk_ops = {
.set_parent = si5341_clk_set_parent,
.get_parent = si5341_clk_get_parent,
.recalc_rate = si5341_clk_recalc_rate,
};
......@@ -669,9 +790,9 @@ static unsigned long si5341_output_clk_recalc_rate(struct clk_hw *hw,
/* Calculate value as 24-bit integer*/
r_divider = r[2] << 16 | r[1] << 8 | r[0];
/* If Rx_REG is zero, the divider is disabled, so return a "0" rate */
if (!r_divider)
return 0;
// /* If Rx_REG is zero, the divider is disabled, so return a "0" rate */
// if (!r_divider)
// return 0;
/* Divider is 2*(Rx_REG+1) */
r_divider += 1;
......@@ -1013,7 +1134,8 @@ static const struct regmap_range si5341_regmap_volatile_range[] = {
regmap_reg_range(0x000C, 0x0012), /* Status */
regmap_reg_range(0x001C, 0x001E), /* reset, finc/fdec */
regmap_reg_range(0x00E2, 0x00FE), /* NVM, interrupts, device ready */
/* Update bits for synth config */
/* Update bits for P divider and synth config */
regmap_reg_range(SI5341_PX_UPD, SI5341_PX_UPD),
regmap_reg_range(SI5341_SYNTH_N_UPD(0), SI5341_SYNTH_N_UPD(0)),
regmap_reg_range(SI5341_SYNTH_N_UPD(1), SI5341_SYNTH_N_UPD(1)),
regmap_reg_range(SI5341_SYNTH_N_UPD(2), SI5341_SYNTH_N_UPD(2)),
......@@ -1150,6 +1272,7 @@ static int si5341_initialize_pll(struct clk_si5341 *data)
struct device_node *np = data->i2c_client->dev.of_node;
u32 m_num = 0;
u32 m_den = 0;
int sel;
if (of_property_read_u32(np, "silabs,pll-m-num", &m_num)) {
dev_err(&data->i2c_client->dev,
......@@ -1163,7 +1286,11 @@ static int si5341_initialize_pll(struct clk_si5341 *data)
if (!m_num || !m_den) {
dev_err(&data->i2c_client->dev,
"PLL configuration invalid, assume 14GHz\n");
m_den = clk_get_rate(data->pxtal) / 10;
sel = si5341_clk_get_selected_input(data);
if (sel < 0)
return sel;
m_den = clk_get_rate(data->input_clk[sel]) / 10;
m_num = 1400000000;
}
......@@ -1171,11 +1298,52 @@ static int si5341_initialize_pll(struct clk_si5341 *data)
SI5341_PLL_M_NUM, m_num, m_den);
}
static int si5341_clk_select_active_input(struct clk_si5341 *data)
{
int res;
int err;
int i;
res = si5341_clk_get_selected_input(data);
if (res < 0)
return res;
/* If the current register setting is invalid, pick the first input */
if (!data->input_clk[res]) {
dev_dbg(&data->i2c_client->dev,
"Input %d not connected, rerouting\n", res);
res = -ENODEV;
for (i = 0; i < SI5341_NUM_INPUTS; ++i) {
if (data->input_clk[i]) {
res = i;
break;
}
}
if (res < 0) {
dev_err(&data->i2c_client->dev,
"No clock input available\n");
return res;
}
}
/* Make sure the selected clock is also enabled and routed */
err = si5341_clk_reparent(data, res);
if (err < 0)
return err;
err = clk_prepare_enable(data->input_clk[res]);
if (err < 0)
return err;
return res;
}
static int si5341_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct clk_si5341 *data;
struct clk_init_data init;
struct clk *input;
const char *root_clock_name;
const char *synth_clock_names[SI5341_NUM_SYNTH];
int err;
......@@ -1189,12 +1357,16 @@ static int si5341_probe(struct i2c_client *client,
data->i2c_client = client;
data->pxtal = devm_clk_get(&client->dev, "xtal");
if (IS_ERR(data->pxtal)) {
if (PTR_ERR(data->pxtal) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_err(&client->dev, "Missing xtal clock input\n");
for (i = 0; i < SI5341_NUM_INPUTS; ++i) {
input = devm_clk_get(&client->dev, si5341_input_clock_names[i]);
if (IS_ERR(input)) {
if (PTR_ERR(input) == -EPROBE_DEFER)
return -EPROBE_DEFER;
data->input_clk_name[i] = si5341_input_clock_names[i];
} else {
data->input_clk[i] = input;
data->input_clk_name[i] = __clk_get_name(input);
}
}
err = si5341_dt_parse_dt(client, config);
......@@ -1216,9 +1388,6 @@ static int si5341_probe(struct i2c_client *client,
if (err < 0)
return err;
/* "Activate" the xtal (usually a fixed clock) */
clk_prepare_enable(data->pxtal);
if (of_property_read_bool(client->dev.of_node, "silabs,reprogram")) {
initialization_required = true;
} else {
......@@ -1251,7 +1420,14 @@ static int si5341_probe(struct i2c_client *client,
ARRAY_SIZE(si5341_reg_defaults));
if (err < 0)
return err;
}
/* Input must be up and running at this point */
err = si5341_clk_select_active_input(data);
if (err < 0)
return err;
if (initialization_required) {
/* PLL configuration is required */
err = si5341_initialize_pll(data);
if (err < 0)
......@@ -1259,9 +1435,8 @@ static int si5341_probe(struct i2c_client *client,
}
/* Register the PLL */
data->pxtal_name = __clk_get_name(data->pxtal);
init.parent_names = &data->pxtal_name;
init.num_parents = 1; /* For now, only XTAL input supported */
init.parent_names = data->input_clk_name;
init.num_parents = SI5341_NUM_INPUTS;
init.ops = &si5341_clk_ops;
init.flags = 0;
data->hw.init = &init;
......@@ -1367,3 +1542,7 @@ static struct i2c_driver si5341_driver = {
.id_table = si5341_id,
};
module_i2c_driver(si5341_driver);
MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
MODULE_DESCRIPTION("Si5341 driver");
MODULE_LICENSE("GPL");
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment