diff options
Diffstat (limited to 'drivers/pinctrl/pinmux.c')
| -rw-r--r-- | drivers/pinctrl/pinmux.c | 106 | 
1 files changed, 104 insertions, 2 deletions
| diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 36a11c9e893a..6cdbd9ccf2f0 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -12,6 +12,7 @@   */  #define pr_fmt(fmt) "pinmux core: " fmt +#include <linux/ctype.h>  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/init.h> @@ -673,13 +674,114 @@ void pinmux_show_setting(struct seq_file *s,  DEFINE_SHOW_ATTRIBUTE(pinmux_functions);  DEFINE_SHOW_ATTRIBUTE(pinmux_pins); +#define PINMUX_SELECT_MAX 128 +static ssize_t pinmux_select(struct file *file, const char __user *user_buf, +				   size_t len, loff_t *ppos) +{ +	struct seq_file *sfile = file->private_data; +	struct pinctrl_dev *pctldev = sfile->private; +	const struct pinmux_ops *pmxops = pctldev->desc->pmxops; +	const char *const *groups; +	char *buf, *gname, *fname; +	unsigned int num_groups; +	int fsel, gsel, ret; + +	if (len > PINMUX_SELECT_MAX) +		return -ENOMEM; + +	buf = kzalloc(PINMUX_SELECT_MAX, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	ret = strncpy_from_user(buf, user_buf, PINMUX_SELECT_MAX); +	if (ret < 0) +		goto exit_free_buf; +	buf[len-1] = '\0'; + +	/* remove leading and trailing spaces of input buffer */ +	gname = strstrip(buf); +	if (*gname == '\0') { +		ret = -EINVAL; +		goto exit_free_buf; +	} + +	/* find a separator which is a spacelike character */ +	for (fname = gname; !isspace(*fname); fname++) { +		if (*fname == '\0') { +			ret = -EINVAL; +			goto exit_free_buf; +		} +	} +	*fname = '\0'; + +	/* drop extra spaces between function and group names */ +	fname = skip_spaces(fname + 1); +	if (*fname == '\0') { +		ret = -EINVAL; +		goto exit_free_buf; +	} + +	ret = pinmux_func_name_to_selector(pctldev, fname); +	if (ret < 0) { +		dev_err(pctldev->dev, "invalid function %s in map table\n", fname); +		goto exit_free_buf; +	} +	fsel = ret; + +	ret = pmxops->get_function_groups(pctldev, fsel, &groups, &num_groups); +	if (ret) { +		dev_err(pctldev->dev, "no groups for function %d (%s)", fsel, fname); +		goto exit_free_buf; +	} + +	ret = match_string(groups, num_groups, gname); +	if (ret < 0) { +		dev_err(pctldev->dev, "invalid group %s", gname); +		goto exit_free_buf; +	} + +	ret = pinctrl_get_group_selector(pctldev, gname); +	if (ret < 0) { +		dev_err(pctldev->dev, "failed to get group selector for %s", gname); +		goto exit_free_buf; +	} +	gsel = ret; + +	ret = pmxops->set_mux(pctldev, fsel, gsel); +	if (ret) { +		dev_err(pctldev->dev, "set_mux() failed: %d", ret); +		goto exit_free_buf; +	} +	ret = len; + +exit_free_buf: +	kfree(buf); + +	return ret; +} + +static int pinmux_select_open(struct inode *inode, struct file *file) +{ +	return single_open(file, NULL, inode->i_private); +} + +static const struct file_operations pinmux_select_ops = { +	.owner = THIS_MODULE, +	.open = pinmux_select_open, +	.write = pinmux_select, +	.llseek = no_llseek, +	.release = single_release, +}; +  void pinmux_init_device_debugfs(struct dentry *devroot,  			 struct pinctrl_dev *pctldev)  { -	debugfs_create_file("pinmux-functions", S_IFREG | S_IRUGO, +	debugfs_create_file("pinmux-functions", 0444,  			    devroot, pctldev, &pinmux_functions_fops); -	debugfs_create_file("pinmux-pins", S_IFREG | S_IRUGO, +	debugfs_create_file("pinmux-pins", 0444,  			    devroot, pctldev, &pinmux_pins_fops); +	debugfs_create_file("pinmux-select", 0200, +			    devroot, pctldev, &pinmux_select_ops);  }  #endif /* CONFIG_DEBUG_FS */ | 
