Skip to content

Commit

Permalink
Merge pull request torvalds#411 from analogdevicesinc/hmc7044-features
Browse files Browse the repository at this point in the history
HMC7044 features
  • Loading branch information
mhennerich authored Jun 6, 2019
2 parents 0a3f31d + d0f2f51 commit 6df008d
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 6 deletions.
38 changes: 35 additions & 3 deletions Documentation/devicetree/bindings/iio/frequency/hmc7044.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ Optional properties:
specifying the Input Buffer Mode (Bits [4:1]) and the
Buffer enable bit ([0]) - see register 0x000E. If this is
not specified, 0 will be used by default.

Adding channels:
Channels can be specified using child nodes.
The following properties are applicable to them:

- reg: The identifier of the channel.
- adi,extended-name: Descriptive channel name.
- adi,divider: Channel divider. The divider supports even divide ratios
from 2 to 4094. The supported odd divide ratios are 1, 3, and 5.
- adi,driver-mode: Output driver mode. Must be one of:
0 - CML mode,
1 - LVPECL mode,
2 - LVDS mode,
3 - CMOS mode.
- adi,high-performance-mode-disable: Disables the high performance mode
- adi,startup-mode-dynamic-enable: Enables pulse generator mode
(default mode is asynchronous)
Expand All @@ -62,9 +76,27 @@ Optional properties:
by up to 17 1/2 cycles of the VCO
- adi,fine-analog-delay: Adjusts the delay of the divider signal
in 24 fine delay steps. Step size = 25 ps.
- adi,output-mux-mode: Configures the output mux selection.
0 = divider channel (default), 1 = analog delay,
2 = other channel of pair, 3 = input VCO clock.
- adi,output-mux-mode: Configures the output mux selection:
0 = divider channel (default)
1 = analog delay
2 = other channel of pair
3 = input VCO clock.
- adi,pll1-ref-prio-ctrl: Refer to register 0x14 description.
- adi,sync-pin-mode: SYNC pin configuration with respect to PLL2.
0 - Disabled
1 - SYNC. A rising edge is carried through PLL2.
Useful for multichip synchronization.
2 - Pulse generator. Request a pulse generator stream from any
channels configured for dynamic startup. This behaves in
the same way as a GPI requested pulse generator.
3 - Causes SYNC if alarm exists, otherwise causes
pulse generator.
- adi,clkin0-rf-sync-enable: CLKIN0 input is used for external RF sync.
- adi,clkin1-vco-in-enable: CLKIN1 input is used for external VCO.
- adi,driver-impedance-mode: Output driver impedance selection.
0 - Internal resistor disable.
1 - Internal 100 Ω resistor enable per output pin.
3 - Internal 50 Ω resistor enable per output pin.

Example:

Expand Down
108 changes: 105 additions & 3 deletions drivers/iio/frequency/hmc7044.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,21 @@
#define HMC7044_REG_EN_CTRL_1 0x0004
#define HMC7044_SEVEN_PAIRS(x) ((x) & 0x7f)

#define HMC7044_REG_GLOB_MODE 0x0005
#define HMC7044_REF_PATH_EN(x) ((x) & 0xf)
#define HMC7044_RFSYNC_EN BIT(4)
#define HMC7044_VCOIN_MODE_EN BIT(5)
#define HMC7044_SYNC_PIN_MODE(x) (((x) & 0x3) << 6)

/* PLL1 */
#define HMC7044_REG_CLKIN0_BUF_CTRL 0x000A
#define HMC7044_REG_CLKIN1_BUF_CTRL 0x000B
#define HMC7044_REG_CLKIN2_BUF_CTRL 0x000C
#define HMC7044_REG_CLKIN3_BUF_CTRL 0x000D
#define HMC7044_REG_OSCIN_BUF_CTRL 0x000E

#define HMC7044_REG_PLL1_REF_PRIO_CTRL 0x0014

#define HMC7044_HIGH_Z_EN BIT(4)
#define HMC7044_LVPECL_EN BIT(3)
#define HMC7044_AC_COUPLING_EN BIT(2)
Expand Down Expand Up @@ -156,6 +164,7 @@

#define HMC7044_REG_CH_OUT_CRTL_8(ch) (0x00D0 + 0xA * (ch))
#define HMC7044_DRIVER_MODE(x) (((x) & 0x3) << 3)
#define HMC7044_DRIVER_Z_MODE(x) (((x) & 0x3) << 0)
#define HMC7044_DYN_DRIVER_EN BIT(5)
#define HMC7044_FORCE_MUTE_EN BIT(7)

Expand Down Expand Up @@ -197,6 +206,7 @@ struct hmc7044_chan_spec {
bool force_mute_enable;
unsigned int divider;
unsigned int driver_mode;
unsigned int driver_impedance;
unsigned int coarse_delay;
unsigned int fine_delay;
unsigned int out_mux_mode;
Expand All @@ -206,10 +216,15 @@ struct hmc7044_chan_spec {
struct hmc7044 {
struct spi_device *spi;
u32 clkin_freq[4];
u32 clkin_freq_ccf[4];
u32 vcxo_freq;
u32 pll2_freq;
unsigned int pll1_loop_bw;
unsigned int sysref_timer_div;
unsigned int pll1_ref_prio_ctrl;
bool clkin0_rfsync_en;
bool clkin1_vcoin_en;
unsigned int sync_pin_mode;
unsigned int pulse_gen_mode;
unsigned int in_buf_mode[5];
unsigned int gpi_ctrl[4];
Expand All @@ -221,9 +236,17 @@ struct hmc7044 {
struct hmc7044_output outputs[HMC7044_NUM_CHAN];
struct clk *clks[HMC7044_NUM_CHAN];
struct clk_onecell_data clk_data;
struct clk *clk_input[4];
struct mutex lock;
};

static const char * const hmc7044_input_clk_names[] = {
[0] = "clkin0",
[1] = "clkin1",
[2] = "clkin2",
[3] = "clkin3",
};

static int hmc7044_write(struct iio_dev *indio_dev,
unsigned int reg,
unsigned int val)
Expand Down Expand Up @@ -488,17 +511,23 @@ static int hmc7044_setup(struct iio_dev *indio_dev)
unsigned long pfd1_freq;
unsigned long vco_limit;
unsigned long n2[2], r2[2];
unsigned int i;
unsigned int i, ref_en = 0;
int ret;

vcxo_freq = hmc->vcxo_freq / 1000;
pll2_freq = hmc->pll2_freq / 1000;

lcm_freq = vcxo_freq;
for (i = 0; i < ARRAY_SIZE(clkin_freq); i++) {
clkin_freq[i] = hmc->clkin_freq[i] / 1000;
if (clkin_freq[i])
if (hmc->clkin_freq_ccf[i])
clkin_freq[i] = hmc->clkin_freq_ccf[i] / 1000;
else
clkin_freq[i] = hmc->clkin_freq[i] / 1000;

if (clkin_freq[i]) {
lcm_freq = gcd(clkin_freq[i], lcm_freq);
ref_en |= BIT(i);
}
}

while (lcm_freq > HMC7044_RECOMM_LCM_MAX)
Expand Down Expand Up @@ -581,6 +610,12 @@ static int hmc7044_setup(struct iio_dev *indio_dev)
hmc7044_write(indio_dev, HMC7044_REG_PLL1_HOLDOVER, 0x06);
hmc7044_write(indio_dev, HMC7044_REG_VTUNE_PRESET, 0x04);

hmc7044_write(indio_dev, HMC7044_REG_GLOB_MODE,
HMC7044_SYNC_PIN_MODE(hmc->sync_pin_mode) |
(hmc->clkin0_rfsync_en ? HMC7044_RFSYNC_EN : 0) |
(hmc->clkin1_vcoin_en ? HMC7044_VCOIN_MODE_EN : 0) |
HMC7044_REF_PATH_EN(ref_en));

/* Program PLL2 */

/* Select the VCO range */
Expand Down Expand Up @@ -630,6 +665,9 @@ static int hmc7044_setup(struct iio_dev *indio_dev)
hmc7044_write(indio_dev, HMC7044_REG_PLL1_N_MSB,
HMC7044_N2_MSB(n1));

hmc7044_write(indio_dev, HMC7044_REG_PLL1_REF_PRIO_CTRL,
hmc->pll1_ref_prio_ctrl);

/* Program the SYSREF timer */

/* Set the divide ratio */
Expand Down Expand Up @@ -680,6 +718,7 @@ static int hmc7044_setup(struct iio_dev *indio_dev)
HMC7044_DIV_MSB(chan->divider));
hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_8(chan->num),
HMC7044_DRIVER_MODE(chan->driver_mode) |
HMC7044_DRIVER_Z_MODE(chan->driver_impedance) |
(chan->start_up_mode_dynamic_enable ?
HMC7044_DYN_DRIVER_EN : 0) |
(chan->force_mute_enable ?
Expand Down Expand Up @@ -787,6 +826,18 @@ static int hmc7044_parse_dt(struct device *dev,
of_property_read_u32(np, "adi,oscin-buffer-mode",
&hmc->in_buf_mode[4]);

hmc->pll1_ref_prio_ctrl = 0xE4;
of_property_read_u32(np, "adi,pll1-ref-prio-ctrl",
&hmc->pll1_ref_prio_ctrl);

hmc->sync_pin_mode = 1;
of_property_read_u32(np, "adi,sync-pin-mode",
&hmc->sync_pin_mode);
hmc->clkin0_rfsync_en =
of_property_read_bool(np, "adi,clkin0-rf-sync-enable");
hmc->clkin1_vcoin_en =
of_property_read_bool(np, "adi,clkin1-vco-in-enable");

ret = of_property_read_u32_array(np, "adi,gpi-controls",
hmc->gpi_ctrl, ARRAY_SIZE(hmc->gpi_ctrl));
if (ret)
Expand Down Expand Up @@ -825,6 +876,10 @@ static int hmc7044_parse_dt(struct device *dev,
hmc->channels[cnt].driver_mode = 0;
of_property_read_u32(chan_np, "adi,driver-mode",
&hmc->channels[cnt].driver_mode);
hmc->channels[cnt].driver_impedance = 1;
of_property_read_u32(chan_np, "adi,driver-impedance-mode",
&hmc->channels[cnt].driver_impedance);

of_property_read_string(chan_np, "adi,extended-name",
&hmc->channels[cnt].extended_name);
hmc->channels[cnt].high_performance_mode_dis =
Expand Down Expand Up @@ -852,6 +907,44 @@ static int hmc7044_parse_dt(struct device *dev,
return 0;
}

static void hmc7044_clk_disable_unprepare(void *data)
{
struct clk *clk = data;

clk_disable_unprepare(clk);
}

static int hmc7044_get_clks(struct device *dev,
struct hmc7044 *hmc)
{
struct clk *clk;
int i, ret;

for (i = 0; i < ARRAY_SIZE(hmc7044_input_clk_names); i++) {
clk = devm_clk_get(dev, hmc7044_input_clk_names[i]);
if (IS_ERR(clk) && PTR_ERR(clk) != -ENOENT)
return PTR_ERR(clk);

if (PTR_ERR(clk) == -ENOENT) {
hmc->clk_input[i] = NULL;
continue;
}

ret = clk_prepare_enable(clk);
if (ret < 0)
return ret;

hmc->clkin_freq_ccf[i] = clk_get_rate(clk);

devm_add_action_or_reset(dev,
hmc7044_clk_disable_unprepare, clk);

hmc->clk_input[i] = clk;
}

return 0;
}

static int hmc7044_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
Expand All @@ -864,6 +957,10 @@ static int hmc7044_probe(struct spi_device *spi)

hmc = iio_priv(indio_dev);

ret = hmc7044_get_clks(&spi->dev, hmc);
if (ret)
return ret;

mutex_init(&hmc->lock);

spi_set_drvdata(spi, indio_dev);
Expand All @@ -881,6 +978,11 @@ static int hmc7044_probe(struct spi_device *spi)
indio_dev->channels = hmc->iio_channels;
indio_dev->num_channels = hmc->num_channels;

if (spi->dev.of_node)
indio_dev->name = spi->dev.of_node->name;
else
indio_dev->name = spi_get_device_id(spi)->name;

ret = hmc7044_setup(indio_dev);
if (ret)
return ret;
Expand Down

0 comments on commit 6df008d

Please sign in to comment.