diff options
Diffstat (limited to 'drivers/iio/frequency')
| -rw-r--r-- | drivers/iio/frequency/adf4350.c | 186 | 
1 files changed, 176 insertions, 10 deletions
| diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index e76d4ace53ff..a4157cdb314d 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -1,7 +1,7 @@  /*   * ADF4350/ADF4351 SPI Wideband Synthesizer driver   * - * Copyright 2012 Analog Devices Inc. + * Copyright 2012-2013 Analog Devices Inc.   *   * Licensed under the GPL-2.   */ @@ -17,6 +17,9 @@  #include <linux/gcd.h>  #include <linux/gpio.h>  #include <asm/div64.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_gpio.h>  #include <linux/iio/iio.h>  #include <linux/iio/sysfs.h> @@ -33,6 +36,7 @@ struct adf4350_state {  	struct spi_device		*spi;  	struct regulator		*reg;  	struct adf4350_platform_data	*pdata; +	struct clk			*clk;  	unsigned long			clkin;  	unsigned long			chspc; /* Channel Spacing */  	unsigned long			fpfd; /* Phase Frequency Detector */ @@ -43,7 +47,7 @@ struct adf4350_state {  	unsigned			r4_rf_div_sel;  	unsigned long			regs[6];  	unsigned long			regs_hw[6]; - +	unsigned long long		freq_req;  	/*  	 * DMA (thus cache coherency maintenance) requires the  	 * transfer buffers to live in their own cache lines. @@ -52,7 +56,6 @@ struct adf4350_state {  };  static struct adf4350_platform_data default_pdata = { -	.clkin = 122880000,  	.channel_spacing = 10000,  	.r2_user_settings = ADF4350_REG2_PD_POLARITY_POS |  			    ADF4350_REG2_CHARGE_PUMP_CURR_uA(2500), @@ -235,6 +238,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)  		ADF4350_REG4_MUTE_TILL_LOCK_EN));  	st->regs[ADF4350_REG5] = ADF4350_REG5_LD_PIN_MODE_DIGITAL; +	st->freq_req = freq;  	return adf4350_sync_config(st);  } @@ -246,6 +250,7 @@ static ssize_t adf4350_write(struct iio_dev *indio_dev,  {  	struct adf4350_state *st = iio_priv(indio_dev);  	unsigned long long readin; +	unsigned long tmp;  	int ret;  	ret = kstrtoull(buf, 10, &readin); @@ -258,10 +263,23 @@ static ssize_t adf4350_write(struct iio_dev *indio_dev,  		ret = adf4350_set_freq(st, readin);  		break;  	case ADF4350_FREQ_REFIN: -		if (readin > ADF4350_MAX_FREQ_REFIN) +		if (readin > ADF4350_MAX_FREQ_REFIN) {  			ret = -EINVAL; -		else -			st->clkin = readin; +			break; +		} + +		if (st->clk) { +			tmp = clk_round_rate(st->clk, readin); +			if (tmp != readin) { +				ret = -EINVAL; +				break; +			} +			ret = clk_set_rate(st->clk, tmp); +			if (ret < 0) +				break; +		} +		st->clkin = readin; +		ret = adf4350_set_freq(st, st->freq_req);  		break;  	case ADF4350_FREQ_RESOLUTION:  		if (readin == 0) @@ -308,6 +326,9 @@ static ssize_t adf4350_read(struct iio_dev *indio_dev,  			}  		break;  	case ADF4350_FREQ_REFIN: +		if (st->clk) +			st->clkin = clk_get_rate(st->clk); +  		val = st->clkin;  		break;  	case ADF4350_FREQ_RESOLUTION: @@ -318,6 +339,7 @@ static ssize_t adf4350_read(struct iio_dev *indio_dev,  		break;  	default:  		ret = -EINVAL; +		val = 0;  	}  	mutex_unlock(&indio_dev->mlock); @@ -355,19 +377,153 @@ static const struct iio_info adf4350_info = {  	.driver_module = THIS_MODULE,  }; +#ifdef CONFIG_OF +static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev) +{ +	struct device_node *np = dev->of_node; +	struct adf4350_platform_data *pdata; +	unsigned int tmp; +	int ret; + +	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); +	if (!pdata) { +		dev_err(dev, "could not allocate memory for platform data\n"); +		return NULL; +	} + +	strncpy(&pdata->name[0], np->name, SPI_NAME_SIZE - 1); + +	tmp = 10000; +	of_property_read_u32(np, "adi,channel-spacing", &tmp); +	pdata->channel_spacing = tmp; + +	tmp = 0; +	of_property_read_u32(np, "adi,power-up-frequency", &tmp); +	pdata->power_up_frequency = tmp; + +	tmp = 0; +	of_property_read_u32(np, "adi,reference-div-factor", &tmp); +	pdata->ref_div_factor = tmp; + +	ret = of_get_gpio(np, 0); +	if (ret < 0) +		pdata->gpio_lock_detect = -1; +	else +		pdata->gpio_lock_detect = ret; + +	pdata->ref_doubler_en = of_property_read_bool(np, +			"adi,reference-doubler-enable"); +	pdata->ref_div2_en = of_property_read_bool(np, +			"adi,reference-div2-enable"); + +	/* r2_user_settings */ +	pdata->r2_user_settings = of_property_read_bool(np, +			"adi,phase-detector-polarity-positive-enable") ? +			ADF4350_REG2_PD_POLARITY_POS : 0; +	pdata->r2_user_settings |= of_property_read_bool(np, +			"adi,lock-detect-precision-6ns-enable") ? +			ADF4350_REG2_LDP_6ns : 0; +	pdata->r2_user_settings |= of_property_read_bool(np, +			"adi,lock-detect-function-integer-n-enable") ? +			ADF4350_REG2_LDF_INT_N : 0; + +	tmp = 2500; +	of_property_read_u32(np, "adi,charge-pump-current", &tmp); +	pdata->r2_user_settings |= ADF4350_REG2_CHARGE_PUMP_CURR_uA(tmp); + +	tmp = 0; +	of_property_read_u32(np, "adi,muxout-select", &tmp); +	pdata->r2_user_settings |= ADF4350_REG2_MUXOUT(tmp); + +	pdata->r2_user_settings |= of_property_read_bool(np, +			"adi,low-spur-mode-enable") ? +			ADF4350_REG2_NOISE_MODE(0x3) : 0; + +	/* r3_user_settings */ + +	pdata->r3_user_settings = of_property_read_bool(np, +			"adi,cycle-slip-reduction-enable") ? +			ADF4350_REG3_12BIT_CSR_EN : 0; +	pdata->r3_user_settings |= of_property_read_bool(np, +			"adi,charge-cancellation-enable") ? +			ADF4351_REG3_CHARGE_CANCELLATION_EN : 0; + +	pdata->r3_user_settings |= of_property_read_bool(np, +			"adi,anti-backlash-3ns-enable") ? +			ADF4351_REG3_ANTI_BACKLASH_3ns_EN : 0; +	pdata->r3_user_settings |= of_property_read_bool(np, +			"adi,band-select-clock-mode-high-enable") ? +			ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH : 0; + +	tmp = 0; +	of_property_read_u32(np, "adi,12bit-clk-divider", &tmp); +	pdata->r3_user_settings |= ADF4350_REG3_12BIT_CLKDIV(tmp); + +	tmp = 0; +	of_property_read_u32(np, "adi,clk-divider-mode", &tmp); +	pdata->r3_user_settings |= ADF4350_REG3_12BIT_CLKDIV_MODE(tmp); + +	/* r4_user_settings */ + +	pdata->r4_user_settings = of_property_read_bool(np, +			"adi,aux-output-enable") ? +			ADF4350_REG4_AUX_OUTPUT_EN : 0; +	pdata->r4_user_settings |= of_property_read_bool(np, +			"adi,aux-output-fundamental-enable") ? +			ADF4350_REG4_AUX_OUTPUT_FUND : 0; +	pdata->r4_user_settings |= of_property_read_bool(np, +			"adi,mute-till-lock-enable") ? +			ADF4350_REG4_MUTE_TILL_LOCK_EN : 0; + +	tmp = 0; +	of_property_read_u32(np, "adi,output-power", &tmp); +	pdata->r4_user_settings |= ADF4350_REG4_OUTPUT_PWR(tmp); + +	tmp = 0; +	of_property_read_u32(np, "adi,aux-output-power", &tmp); +	pdata->r4_user_settings |= ADF4350_REG4_AUX_OUTPUT_PWR(tmp); + +	return pdata; +} +#else +static +struct adf4350_platform_data *adf4350_parse_dt(struct device *dev) +{ +	return NULL; +} +#endif +  static int adf4350_probe(struct spi_device *spi)  { -	struct adf4350_platform_data *pdata = spi->dev.platform_data; +	struct adf4350_platform_data *pdata;  	struct iio_dev *indio_dev;  	struct adf4350_state *st; +	struct clk *clk = NULL;  	int ret; +	if (spi->dev.of_node) { +		pdata = adf4350_parse_dt(&spi->dev); +		if (pdata == NULL) +			return -EINVAL; +	} else { +		pdata = spi->dev.platform_data; +	} +  	if (!pdata) {  		dev_warn(&spi->dev, "no platform data? using default\n"); -  		pdata = &default_pdata;  	} +	if (!pdata->clkin) { +		clk = clk_get(&spi->dev, "clkin"); +		if (IS_ERR(clk)) +			return -EPROBE_DEFER; + +		ret = clk_prepare_enable(clk); +		if (ret < 0) +			return ret; +	} +  	indio_dev = iio_device_alloc(sizeof(*st));  	if (indio_dev == NULL)  		return -ENOMEM; @@ -395,7 +551,12 @@ static int adf4350_probe(struct spi_device *spi)  	indio_dev->num_channels = 1;  	st->chspc = pdata->channel_spacing; -	st->clkin = pdata->clkin; +	if (clk) { +		st->clk = clk; +		st->clkin = clk_get_rate(clk); +	} else { +		st->clkin = pdata->clkin; +	}  	st->min_out_freq = spi_get_device_id(spi)->driver_data == 4351 ?  		ADF4351_MIN_OUT_FREQ : ADF4350_MIN_OUT_FREQ; @@ -435,6 +596,8 @@ error_put_reg:  	if (!IS_ERR(st->reg))  		regulator_put(st->reg); +	if (clk) +		clk_disable_unprepare(clk);  	iio_device_free(indio_dev);  	return ret; @@ -451,6 +614,9 @@ static int adf4350_remove(struct spi_device *spi)  	iio_device_unregister(indio_dev); +	if (st->clk) +		clk_disable_unprepare(st->clk); +  	if (!IS_ERR(reg)) {  		regulator_disable(reg);  		regulator_put(reg); @@ -481,6 +647,6 @@ static struct spi_driver adf4350_driver = {  };  module_spi_driver(adf4350_driver); -MODULE_AUTHOR("Michael Hennerich <[email protected]>"); +MODULE_AUTHOR("Michael Hennerich <[email protected]>");  MODULE_DESCRIPTION("Analog Devices ADF4350/ADF4351 PLL");  MODULE_LICENSE("GPL v2"); |