Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 47be16b6 authored by Ludovic Desroches's avatar Ludovic Desroches Committed by Jonathan Cameron
Browse files

iio: at91_adc: add low and high res support



at91 adc offers the choice between two resolutions: low and high.
The low and high resolution values depends on adc IP version, as many IP
properties have been exposed through device tree, these settings have also
been added to the dt bindings.

Signed-off-by: default avatarLudovic Desroches <ludovic.desroches@atmel.com>
Signed-off-by: default avatarJean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Signed-off-by: default avatarJonathan Cameron <jic23@kernel.org>
parent 2fc72cd8
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -14,9 +14,17 @@ Required properties:
  - atmel,adc-status-register: Offset of the Interrupt Status Register
  - atmel,adc-trigger-register: Offset of the Trigger Register
  - atmel,adc-vref: Reference voltage in millivolts for the conversions
  - atmel,adc-res: List of resolution in bits supported by the ADC. List size
		   must be two at least.
  - atmel,adc-res-names: Contains one identifier string for each resolution
			 in atmel,adc-res property. "lowres" and "highres"
			 identifiers are required.

Optional properties:
  - atmel,adc-use-external: Boolean to enable of external triggers
  - atmel,adc-use-res: String corresponding to an identifier from
		       atmel,adc-res-names property. If not specified, the highest
		       resolution will be used.
 
Optional trigger Nodes:
  - Required properties:
@@ -40,6 +48,9 @@ adc0: adc@fffb0000 {
	atmel,adc-trigger-register = <0x08>;
	atmel,adc-use-external;
	atmel,adc-vref = <3300>;
	atmel,adc-res = <8 10>;
	atmel,adc-res-names = "lowres", "highres";
	atmel,adc-use-res = "lowres";

	trigger@0 {
		trigger-name = "external-rising";
+70 −4
Original line number Diff line number Diff line
@@ -57,6 +57,8 @@ struct at91_adc_state {
	u32			trigger_number;
	bool			use_external;
	u32			vref_mv;
	u32			res;		/* resolution used for convertions */
	bool			low_res;	/* the resolution corresponds to the lowest one */
	wait_queue_head_t	wq_data_avail;
};

@@ -138,7 +140,7 @@ static int at91_adc_channel_init(struct iio_dev *idev)
		chan->channel = bit;
		chan->scan_index = idx;
		chan->scan_type.sign = 'u';
		chan->scan_type.realbits = 10;
		chan->scan_type.realbits = st->res;
		chan->scan_type.storagebits = 16;
		chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
		chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
@@ -372,6 +374,59 @@ static int at91_adc_read_raw(struct iio_dev *idev,
	return -EINVAL;
}

static int at91_adc_of_get_resolution(struct at91_adc_state *st,
				      struct platform_device *pdev)
{
	struct iio_dev *idev = iio_priv_to_dev(st);
	struct device_node *np = pdev->dev.of_node;
	int count, i, ret = 0;
	char *res_name, *s;
	u32 *resolutions;

	count = of_property_count_strings(np, "atmel,adc-res-names");
	if (count < 2) {
		dev_err(&idev->dev, "You must specified at least two resolution names for "
				    "adc-res-names property in the DT\n");
		return count;
	}

	resolutions = kmalloc(count * sizeof(*resolutions), GFP_KERNEL);
	if (!resolutions)
		return -ENOMEM;

	if (of_property_read_u32_array(np, "atmel,adc-res", resolutions, count)) {
		dev_err(&idev->dev, "Missing adc-res property in the DT.\n");
		ret = -ENODEV;
		goto ret;
	}

	if (of_property_read_string(np, "atmel,adc-use-res", (const char **)&res_name))
		res_name = "highres";

	for (i = 0; i < count; i++) {
		if (of_property_read_string_index(np, "atmel,adc-res-names", i, (const char **)&s))
			continue;

		if (strcmp(res_name, s))
			continue;

		st->res = resolutions[i];
		if (!strcmp(res_name, "lowres"))
			st->low_res = true;
		else
			st->low_res = false;

		dev_info(&idev->dev, "Resolution used: %u bits\n", st->res);
		goto ret;
	}

	dev_err(&idev->dev, "There is no resolution for %s\n", res_name);

ret:
	kfree(resolutions);
	return ret;
}

static int at91_adc_probe_dt(struct at91_adc_state *st,
			     struct platform_device *pdev)
{
@@ -415,6 +470,10 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
	}
	st->vref_mv = prop;

	ret = at91_adc_of_get_resolution(st, pdev);
	if (ret)
		goto error_ret;

	st->registers = devm_kzalloc(&idev->dev,
				     sizeof(struct at91_adc_reg_desc),
				     GFP_KERNEL);
@@ -628,6 +687,13 @@ static int at91_adc_probe(struct platform_device *pdev)
	 */
	ticks = round_up((st->startup_time * adc_clk /
			  1000000) - 1, 8) / 8;

	if (st->low_res)
		at91_adc_writel(st, AT91_ADC_MR,
				AT91_ADC_LOWRES |
				(AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
				(AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
	else
		at91_adc_writel(st, AT91_ADC_MR,
				(AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
				(AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));