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

Commit 4e7e8017 authored by Manjunathappa, Prakash's avatar Manjunathappa, Prakash Committed by Linus Walleij
Browse files

pinctrl: pinctrl-single: enhance to configure multiple pins of different modules

Add support to configure multiple pins in each register, existing
implementation added by [1] does not support full fledge multiple pin
configuration in single register, reports a pin clash when different
modules configure different bits of same register. The issue reported
and discussed here
http://www.spinics.net/lists/arm-kernel/msg235213.html



With pinctrl-single,bits-per-mux property specified, use function-mask
property to find out number pins to configure. Allocate and register
pin control functions based sub mask.

Tested on da850/omap-l138 EVM.
does not support variable submask for pins.
does not support pinconf.

[1] "pinctrl: pinctrl-single: Add pinctrl-single,bits type of mux"
(9e605cb6),

Signed-off-by: default avatarManjunathappa, Prakash <prakash.pm@ti.com>
Reported-by: default avatarLad, Prabhakar <prabhakar.csengg@gmail.com>
Tested-by: default avatarLad, Prabhakar <prabhakar.csengg@gmail.com>
Acked-by: default avatarHaojian Zhuang <haojian.zhuang@gmail.com>
Acked-by: default avatarTony Lindgren <tony@atomide.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent ac844b62
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -18,7 +18,8 @@ Optional properties:
  pin functions is ignored
  pin functions is ignored


- pinctrl-single,bit-per-mux : boolean to indicate that one register controls
- pinctrl-single,bit-per-mux : boolean to indicate that one register controls
  more than one pin
  more than one pin, for which "pinctrl-single,function-mask" property specifies
 position mask of pin.


- pinctrl-single,drive-strength : array of value that are used to configure
- pinctrl-single,drive-strength : array of value that are used to configure
  drive strength in the pinmux register. They're value of drive strength
  drive strength in the pinmux register. They're value of drive strength
+165 −33
Original line number Original line Diff line number Diff line
@@ -163,6 +163,7 @@ struct pcs_name {
 * @foff:	value to turn mux off
 * @foff:	value to turn mux off
 * @fmax:	max number of functions in fmask
 * @fmax:	max number of functions in fmask
 * @is_pinconf:	whether supports pinconf
 * @is_pinconf:	whether supports pinconf
 * @bits_per_pin:number of bits per pin
 * @names:	array of register names for pins
 * @names:	array of register names for pins
 * @pins:	physical pins on the SoC
 * @pins:	physical pins on the SoC
 * @pgtree:	pingroup index radix tree
 * @pgtree:	pingroup index radix tree
@@ -190,6 +191,7 @@ struct pcs_device {
	unsigned fmax;
	unsigned fmax;
	bool bits_per_mux;
	bool bits_per_mux;
	bool is_pinconf;
	bool is_pinconf;
	unsigned bits_per_pin;
	struct pcs_name *names;
	struct pcs_name *names;
	struct pcs_data pins;
	struct pcs_data pins;
	struct radix_tree_root pgtree;
	struct radix_tree_root pgtree;
@@ -431,10 +433,11 @@ static int pcs_enable(struct pinctrl_dev *pctldev, unsigned fselector,


		vals = &func->vals[i];
		vals = &func->vals[i];
		val = pcs->read(vals->reg);
		val = pcs->read(vals->reg);
		if (!vals->mask)

			mask = pcs->fmask;
		if (pcs->bits_per_mux)
			mask = vals->mask;
		else
		else
			mask = pcs->fmask & vals->mask;
			mask = pcs->fmask;


		val &= ~mask;
		val &= ~mask;
		val |= (vals->val & mask);
		val |= (vals->val & mask);
@@ -779,7 +782,13 @@ static int pcs_allocate_pin_table(struct pcs_device *pcs)
	int mux_bytes, nr_pins, i;
	int mux_bytes, nr_pins, i;


	mux_bytes = pcs->width / BITS_PER_BYTE;
	mux_bytes = pcs->width / BITS_PER_BYTE;

	if (pcs->bits_per_mux) {
		pcs->bits_per_pin = fls(pcs->fmask);
		nr_pins = (pcs->size * BITS_PER_BYTE) / pcs->bits_per_pin;
	} else {
		nr_pins = pcs->size / mux_bytes;
		nr_pins = pcs->size / mux_bytes;
	}


	dev_dbg(pcs->dev, "allocating %i pins\n", nr_pins);
	dev_dbg(pcs->dev, "allocating %i pins\n", nr_pins);
	pcs->pins.pa = devm_kzalloc(pcs->dev,
	pcs->pins.pa = devm_kzalloc(pcs->dev,
@@ -800,8 +809,14 @@ static int pcs_allocate_pin_table(struct pcs_device *pcs)
	for (i = 0; i < pcs->desc.npins; i++) {
	for (i = 0; i < pcs->desc.npins; i++) {
		unsigned offset;
		unsigned offset;
		int res;
		int res;
		int byte_num;


		if (pcs->bits_per_mux) {
			byte_num = (pcs->bits_per_pin * i) / BITS_PER_BYTE;
			offset = (byte_num / mux_bytes) * mux_bytes;
		} else {
			offset = i * mux_bytes;
			offset = i * mux_bytes;
		}
		res = pcs_add_pin(pcs, offset);
		res = pcs_add_pin(pcs, offset);
		if (res < 0) {
		if (res < 0) {
			dev_err(pcs->dev, "error adding pins: %i\n", res);
			dev_err(pcs->dev, "error adding pins: %i\n", res);
@@ -919,6 +934,9 @@ static int pcs_get_pin_by_offset(struct pcs_device *pcs, unsigned offset)
		return -EINVAL;
		return -EINVAL;
	}
	}


	if (pcs->bits_per_mux)
		index = (offset * BITS_PER_BYTE) / pcs->bits_per_pin;
	else
		index = offset / (pcs->width / BITS_PER_BYTE);
		index = offset / (pcs->width / BITS_PER_BYTE);


	return index;
	return index;
@@ -1097,29 +1115,18 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
{
{
	struct pcs_func_vals *vals;
	struct pcs_func_vals *vals;
	const __be32 *mux;
	const __be32 *mux;
	int size, params, rows, *pins, index = 0, found = 0, res = -ENOMEM;
	int size, rows, *pins, index = 0, found = 0, res = -ENOMEM;
	struct pcs_function *function;
	struct pcs_function *function;


	if (pcs->bits_per_mux) {
		params = 3;
		mux = of_get_property(np, PCS_MUX_BITS_NAME, &size);
	} else {
		params = 2;
	mux = of_get_property(np, PCS_MUX_PINS_NAME, &size);
	mux = of_get_property(np, PCS_MUX_PINS_NAME, &size);
	}
	if ((!mux) || (size < sizeof(*mux) * 2)) {

		dev_err(pcs->dev, "bad data for mux %s\n",
	if (!mux) {
			np->name);
		dev_err(pcs->dev, "no valid property for %s\n", np->name);
		return -EINVAL;
	}

	if (size < (sizeof(*mux) * params)) {
		dev_err(pcs->dev, "bad data for %s\n", np->name);
		return -EINVAL;
		return -EINVAL;
	}
	}


	size /= sizeof(*mux);	/* Number of elements in array */
	size /= sizeof(*mux);	/* Number of elements in array */
	rows = size / params;
	rows = size / 2;


	vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows, GFP_KERNEL);
	vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows, GFP_KERNEL);
	if (!vals)
	if (!vals)
@@ -1137,10 +1144,6 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
		val = be32_to_cpup(mux + index++);
		val = be32_to_cpup(mux + index++);
		vals[found].reg = pcs->base + offset;
		vals[found].reg = pcs->base + offset;
		vals[found].val = val;
		vals[found].val = val;
		if (params == 3) {
			val = be32_to_cpup(mux + index++);
			vals[found].mask = val;
		}


		pin = pcs_get_pin_by_offset(pcs, offset);
		pin = pcs_get_pin_by_offset(pcs, offset);
		if (pin < 0) {
		if (pin < 0) {
@@ -1184,6 +1187,125 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
free_pins:
free_pins:
	devm_kfree(pcs->dev, pins);
	devm_kfree(pcs->dev, pins);


free_vals:
	devm_kfree(pcs->dev, vals);

	return res;
}

#define PARAMS_FOR_BITS_PER_MUX 3

static int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs,
						struct device_node *np,
						struct pinctrl_map **map,
						unsigned *num_maps,
						const char **pgnames)
{
	struct pcs_func_vals *vals;
	const __be32 *mux;
	int size, rows, *pins, index = 0, found = 0, res = -ENOMEM;
	int npins_in_row;
	struct pcs_function *function;

	mux = of_get_property(np, PCS_MUX_BITS_NAME, &size);

	if (!mux) {
		dev_err(pcs->dev, "no valid property for %s\n", np->name);
		return -EINVAL;
	}

	if (size < (sizeof(*mux) * PARAMS_FOR_BITS_PER_MUX)) {
		dev_err(pcs->dev, "bad data for %s\n", np->name);
		return -EINVAL;
	}

	/* Number of elements in array */
	size /= sizeof(*mux);

	rows = size / PARAMS_FOR_BITS_PER_MUX;
	npins_in_row = pcs->width / pcs->bits_per_pin;

	vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows * npins_in_row,
			GFP_KERNEL);
	if (!vals)
		return -ENOMEM;

	pins = devm_kzalloc(pcs->dev, sizeof(*pins) * rows * npins_in_row,
			GFP_KERNEL);
	if (!pins)
		goto free_vals;

	while (index < size) {
		unsigned offset, val;
		unsigned mask, bit_pos, val_pos, mask_pos, submask;
		unsigned pin_num_from_lsb;
		int pin;

		offset = be32_to_cpup(mux + index++);
		val = be32_to_cpup(mux + index++);
		mask = be32_to_cpup(mux + index++);

		/* Parse pins in each row from LSB */
		while (mask) {
			bit_pos = ffs(mask);
			pin_num_from_lsb = bit_pos / pcs->bits_per_pin;
			mask_pos = ((pcs->fmask) << (bit_pos - 1));
			val_pos = val & mask_pos;
			submask = mask & mask_pos;
			mask &= ~mask_pos;

			if (submask != mask_pos) {
				dev_warn(pcs->dev,
						"Invalid submask 0x%x for %s at 0x%x\n",
						submask, np->name, offset);
				continue;
			}

			vals[found].mask = submask;
			vals[found].reg = pcs->base + offset;
			vals[found].val = val_pos;

			pin = pcs_get_pin_by_offset(pcs, offset);
			if (pin < 0) {
				dev_err(pcs->dev,
					"could not add functions for %s %ux\n",
					np->name, offset);
				break;
			}
			pins[found++] = pin + pin_num_from_lsb;
		}
	}

	pgnames[0] = np->name;
	function = pcs_add_function(pcs, np, np->name, vals, found, pgnames, 1);
	if (!function)
		goto free_pins;

	res = pcs_add_pingroup(pcs, np, np->name, pins, found);
	if (res < 0)
		goto free_function;

	(*map)->type = PIN_MAP_TYPE_MUX_GROUP;
	(*map)->data.mux.group = np->name;
	(*map)->data.mux.function = np->name;

	if (pcs->is_pinconf) {
		dev_err(pcs->dev, "pinconf not supported\n");
		goto free_pingroups;
	}

	*num_maps = 1;
	return 0;

free_pingroups:
	pcs_free_pingroups(pcs);
	*num_maps = 1;
free_function:
	pcs_remove_function(pcs, function);

free_pins:
	devm_kfree(pcs->dev, pins);

free_vals:
free_vals:
	devm_kfree(pcs->dev, vals);
	devm_kfree(pcs->dev, vals);


@@ -1219,13 +1341,23 @@ static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev,
		goto free_map;
		goto free_map;
	}
	}


	ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, num_maps,
	if (pcs->bits_per_mux) {
					  pgnames);
		ret = pcs_parse_bits_in_pinctrl_entry(pcs, np_config, map,
				num_maps, pgnames);
		if (ret < 0) {
		if (ret < 0) {
			dev_err(pcs->dev, "no pins entries for %s\n",
			dev_err(pcs->dev, "no pins entries for %s\n",
				np_config->name);
				np_config->name);
			goto free_pgnames;
			goto free_pgnames;
		}
		}
	} else {
		ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map,
				num_maps, pgnames);
		if (ret < 0) {
			dev_err(pcs->dev, "no pins entries for %s\n",
				np_config->name);
			goto free_pgnames;
		}
	}


	return 0;
	return 0;