diff options
Diffstat (limited to 'drivers/pinctrl/pinconf.c')
| -rw-r--r-- | drivers/pinctrl/pinconf.c | 335 | 
1 files changed, 181 insertions, 154 deletions
diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index d611ecfcbf7..c67c37e23dd 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -17,6 +17,7 @@  #include <linux/slab.h>  #include <linux/debugfs.h>  #include <linux/seq_file.h> +#include <linux/uaccess.h>  #include <linux/pinctrl/machine.h>  #include <linux/pinctrl/pinctrl.h>  #include <linux/pinctrl/pinconf.h> @@ -88,14 +89,14 @@ int pin_config_get(const char *dev_name, const char *name,  	struct pinctrl_dev *pctldev;  	int pin; -	mutex_lock(&pinctrl_mutex); -  	pctldev = get_pinctrl_dev_from_devname(dev_name);  	if (!pctldev) {  		pin = -EINVAL; -		goto unlock; +		return pin;  	} +	mutex_lock(&pctldev->mutex); +  	pin = pin_get_from_name(pctldev, name);  	if (pin < 0)  		goto unlock; @@ -103,7 +104,7 @@ int pin_config_get(const char *dev_name, const char *name,  	pin = pin_config_get_for_pin(pctldev, pin, config);  unlock: -	mutex_unlock(&pinctrl_mutex); +	mutex_unlock(&pctldev->mutex);  	return pin;  }  EXPORT_SYMBOL(pin_config_get); @@ -144,14 +145,14 @@ int pin_config_set(const char *dev_name, const char *name,  	struct pinctrl_dev *pctldev;  	int pin, ret; -	mutex_lock(&pinctrl_mutex); -  	pctldev = get_pinctrl_dev_from_devname(dev_name);  	if (!pctldev) {  		ret = -EINVAL; -		goto unlock; +		return ret;  	} +	mutex_lock(&pctldev->mutex); +  	pin = pin_get_from_name(pctldev, name);  	if (pin < 0) {  		ret = pin; @@ -161,7 +162,7 @@ int pin_config_set(const char *dev_name, const char *name,  	ret = pin_config_set_for_pin(pctldev, pin, config);  unlock: -	mutex_unlock(&pinctrl_mutex); +	mutex_unlock(&pctldev->mutex);  	return ret;  }  EXPORT_SYMBOL(pin_config_set); @@ -173,13 +174,14 @@ int pin_config_group_get(const char *dev_name, const char *pin_group,  	const struct pinconf_ops *ops;  	int selector, ret; -	mutex_lock(&pinctrl_mutex); -  	pctldev = get_pinctrl_dev_from_devname(dev_name);  	if (!pctldev) {  		ret = -EINVAL; -		goto unlock; +		return ret;  	} + +	mutex_lock(&pctldev->mutex); +  	ops = pctldev->desc->confops;  	if (!ops || !ops->pin_config_group_get) { @@ -199,7 +201,7 @@ int pin_config_group_get(const char *dev_name, const char *pin_group,  	ret = ops->pin_config_group_get(pctldev, selector, config);  unlock: -	mutex_unlock(&pinctrl_mutex); +	mutex_unlock(&pctldev->mutex);  	return ret;  }  EXPORT_SYMBOL(pin_config_group_get); @@ -216,13 +218,14 @@ int pin_config_group_set(const char *dev_name, const char *pin_group,  	int ret;  	int i; -	mutex_lock(&pinctrl_mutex); -  	pctldev = get_pinctrl_dev_from_devname(dev_name);  	if (!pctldev) {  		ret = -EINVAL; -		goto unlock; +		return ret;  	} + +	mutex_lock(&pctldev->mutex); +  	ops = pctldev->desc->confops;  	pctlops = pctldev->desc->pctlops; @@ -278,7 +281,7 @@ int pin_config_group_set(const char *dev_name, const char *pin_group,  	ret = 0;  unlock: -	mutex_unlock(&pinctrl_mutex); +	mutex_unlock(&pctldev->mutex);  	return ret;  } @@ -486,7 +489,7 @@ static int pinconf_pins_show(struct seq_file *s, void *what)  	seq_puts(s, "Pin config settings per pin\n");  	seq_puts(s, "Format: pin (name): configs\n"); -	mutex_lock(&pinctrl_mutex); +	mutex_lock(&pctldev->mutex);  	/* The pin number can be retrived from the pin controller descriptor */  	for (i = 0; i < pctldev->desc->npins; i++) { @@ -506,7 +509,7 @@ static int pinconf_pins_show(struct seq_file *s, void *what)  		seq_printf(s, "\n");  	} -	mutex_unlock(&pinctrl_mutex); +	mutex_unlock(&pctldev->mutex);  	return 0;  } @@ -574,122 +577,58 @@ static const struct file_operations pinconf_groups_ops = {  	.release	= single_release,  }; -/* 32bit read/write ressources */ -#define MAX_NAME_LEN 16 -char dbg_pinname[MAX_NAME_LEN]; /* shared: name of the state of the pin*/ -char dbg_state_name[MAX_NAME_LEN]; /* shared: state of the pin*/ -static u32 dbg_config; /* shared: config to be read/set for the pin & state*/ - -static int pinconf_dbg_pinname_print(struct seq_file *s, void *d) -{ -	if (strlen(dbg_pinname)) -		seq_printf(s, "%s\n", dbg_pinname); -	else -		seq_printf(s, "No pin name set\n"); -	return 0; -} - -static int pinconf_dbg_pinname_open(struct inode *inode, struct file *file) -{ -	return single_open(file, pinconf_dbg_pinname_print, inode->i_private); -} - -static int pinconf_dbg_pinname_write(struct file *file, -	const char __user *user_buf, size_t count, loff_t *ppos) -{ -	int err; - -	if (count > MAX_NAME_LEN) -		return -EINVAL; - -	err = sscanf(user_buf, "%15s", dbg_pinname); - -	if (err != 1) -		return -EINVAL; - -	return count; -} +#define MAX_NAME_LEN 15 -static const struct file_operations pinconf_dbg_pinname_fops = { -	.open = pinconf_dbg_pinname_open, -	.write = pinconf_dbg_pinname_write, -	.read = seq_read, -	.llseek = seq_lseek, -	.release = single_release, -	.owner = THIS_MODULE, +struct dbg_cfg { +	enum pinctrl_map_type map_type; +	char dev_name[MAX_NAME_LEN+1]; +	char state_name[MAX_NAME_LEN+1]; +	char pin_name[MAX_NAME_LEN+1];  }; -static int pinconf_dbg_state_print(struct seq_file *s, void *d) -{ -	if (strlen(dbg_state_name)) -		seq_printf(s, "%s\n", dbg_state_name); -	else -		seq_printf(s, "No pin state set\n"); -	return 0; -} - -static int pinconf_dbg_state_open(struct inode *inode, struct file *file) -{ -	return single_open(file, pinconf_dbg_state_print, inode->i_private); -} - -static int pinconf_dbg_state_write(struct file *file, -	const char __user *user_buf, size_t count, loff_t *ppos) -{ -	int err; - -	if (count > MAX_NAME_LEN) -		return -EINVAL; - -	err = sscanf(user_buf, "%15s", dbg_state_name); - -	if (err != 1) -		return -EINVAL; - -	return count; -} - -static const struct file_operations pinconf_dbg_pinstate_fops = { -	.open = pinconf_dbg_state_open, -	.write = pinconf_dbg_state_write, -	.read = seq_read, -	.llseek = seq_lseek, -	.release = single_release, -	.owner = THIS_MODULE, -}; +/* + * Goal is to keep this structure as global in order to simply read the + * pinconf-config file after a write to check config is as expected + */ +static struct dbg_cfg pinconf_dbg_conf;  /**   * pinconf_dbg_config_print() - display the pinctrl config from the pinctrl - * map, of a pin/state pair based on pinname and state that have been - * selected with the debugfs entries pinconf-name and pinconf-state - * @s: contains the 32bits config to be written + * map, of the dev/pin/state that was last written to pinconf-config file. + * @s: string filled in  with config description   * @d: not used   */  static int pinconf_dbg_config_print(struct seq_file *s, void *d)  {  	struct pinctrl_maps *maps_node; -	struct pinctrl_map const *map; +	const struct pinctrl_map *map;  	struct pinctrl_dev *pctldev = NULL; -	struct pinconf_ops *confops = NULL; +	const struct pinconf_ops *confops = NULL; +	const struct pinctrl_map_configs *configs; +	struct dbg_cfg *dbg = &pinconf_dbg_conf;  	int i, j;  	bool found = false; +	unsigned long config; -	mutex_lock(&pinctrl_mutex); +	mutex_lock(&pctldev->mutex);  	/* Parse the pinctrl map and look for the elected pin/state */  	for_each_maps(maps_node, i, map) { -		if (map->type != PIN_MAP_TYPE_CONFIGS_PIN) +		if (map->type != dbg->map_type)  			continue; - -		if (strncmp(map->name, dbg_state_name, MAX_NAME_LEN) > 0) +		if (strcmp(map->dev_name, dbg->dev_name)) +			continue; +		if (strcmp(map->name, dbg->state_name))  			continue;  		for (j = 0; j < map->data.configs.num_configs; j++) { -			if (0 == strncmp(map->data.configs.group_or_pin, -						dbg_pinname, MAX_NAME_LEN)) { -				/* We found the right pin / state, read the -				 * config and store the pctldev */ -				dbg_config = map->data.configs.configs[j]; +			if (!strcmp(map->data.configs.group_or_pin, +					dbg->pin_name)) { +				/* +				 * We found the right pin / state, read the +				 * config and he pctldev for later use +				 */ +				configs = &map->data.configs;  				pctldev = get_pinctrl_dev_from_devname  					(map->ctrl_dev_name);  				found = true; @@ -698,74 +637,166 @@ static int pinconf_dbg_config_print(struct seq_file *s, void *d)  		}  	} -	mutex_unlock(&pinctrl_mutex); +	if (!found) { +		seq_printf(s, "No config found for dev/state/pin, expected:\n"); +		seq_printf(s, "Searched dev:%s\n", dbg->dev_name); +		seq_printf(s, "Searched state:%s\n", dbg->state_name); +		seq_printf(s, "Searched pin:%s\n", dbg->pin_name); +		seq_printf(s, "Use: modify config_pin <devname> "\ +				"<state> <pinname> <value>\n"); +		goto exit; +	} -	if (found) { -		seq_printf(s, "Config of %s in state %s: 0x%08X\n", dbg_pinname, -				 dbg_state_name, dbg_config); +	config = *(configs->configs); +	seq_printf(s, "Dev %s has config of %s in state %s: 0x%08lX\n", +			dbg->dev_name, dbg->pin_name, +			dbg->state_name, config); -		if (pctldev) -			confops = pctldev->desc->confops; +	if (pctldev) +		confops = pctldev->desc->confops; -		if (confops && confops->pin_config_config_dbg_show) -			confops->pin_config_config_dbg_show(pctldev, -					s, dbg_config); -	} else { -		seq_printf(s, "No pin found for defined name/state\n"); -	} +	if (confops && confops->pin_config_config_dbg_show) +		confops->pin_config_config_dbg_show(pctldev, s, config); -	return 0; -} +exit: +	mutex_unlock(&pctldev->mutex); -static int pinconf_dbg_config_open(struct inode *inode, struct file *file) -{ -	return single_open(file, pinconf_dbg_config_print, inode->i_private); +	return 0;  }  /** - * pinconf_dbg_config_write() - overwrite the pinctrl config in thepinctrl - * map, of a pin/state pair based on pinname and state that have been - * selected with the debugfs entries pinconf-name and pinconf-state + * pinconf_dbg_config_write() - modify the pinctrl config in the pinctrl + * map, of a dev/pin/state entry based on user entries to pinconf-config + * @user_buf: contains the modification request with expected format: + *     modify config_pin <devicename> <state> <pinname> <newvalue> + * modify is literal string, alternatives like add/delete not supported yet + * config_pin is literal, alternatives like config_mux not supported yet + * <devicename> <state> <pinname> are values that should match the pinctrl-maps + * <newvalue> reflects the new config and is driver dependant   */  static int pinconf_dbg_config_write(struct file *file,  	const char __user *user_buf, size_t count, loff_t *ppos)  { -	int err; -	unsigned long config;  	struct pinctrl_maps *maps_node; -	struct pinctrl_map const *map; -	int i, j; +	const struct pinctrl_map *map; +	struct pinctrl_dev *pctldev = NULL; +	const struct pinconf_ops *confops = NULL; +	struct dbg_cfg *dbg = &pinconf_dbg_conf; +	const struct pinctrl_map_configs *configs; +	char config[MAX_NAME_LEN+1]; +	bool found = false; +	char buf[128]; +	char *b = &buf[0]; +	int buf_size; +	char *token; +	int i; + +	/* Get userspace string and assure termination */ +	buf_size = min(count, (sizeof(buf)-1)); +	if (copy_from_user(buf, user_buf, buf_size)) +		return -EFAULT; +	buf[buf_size] = 0; + +	/* +	 * need to parse entry and extract parameters: +	 * modify configs_pin devicename state pinname newvalue +	 */ + +	/* Get arg: 'modify' */ +	token = strsep(&b, " "); +	if (!token) +		return -EINVAL; +	if (strcmp(token, "modify")) +		return -EINVAL; -	err = kstrtoul_from_user(user_buf, count, 0, &config); +	/* Get arg type: "config_pin" type supported so far */ +	token = strsep(&b, " "); +	if (!token) +		return -EINVAL; +	if (strcmp(token, "config_pin")) +		return -EINVAL; +	dbg->map_type = PIN_MAP_TYPE_CONFIGS_PIN; -	if (err) -		return err; +	/* get arg 'device_name' */ +	token = strsep(&b, " "); +	if (token == NULL) +		return -EINVAL; +	if (strlen(token) >= MAX_NAME_LEN) +		return -EINVAL; +	strncpy(dbg->dev_name, token, MAX_NAME_LEN); -	dbg_config = config; +	/* get arg 'state_name' */ +	token = strsep(&b, " "); +	if (token == NULL) +		return -EINVAL; +	if (strlen(token) >= MAX_NAME_LEN) +		return -EINVAL; +	strncpy(dbg->state_name, token, MAX_NAME_LEN); -	mutex_lock(&pinctrl_mutex); +	/* get arg 'pin_name' */ +	token = strsep(&b, " "); +	if (token == NULL) +		return -EINVAL; +	if (strlen(token) >= MAX_NAME_LEN) +		return -EINVAL; +	strncpy(dbg->pin_name, token, MAX_NAME_LEN); -	/* Parse the pinctrl map and look for the selected pin/state */ +	/* get new_value of config' */ +	token = strsep(&b, " "); +	if (token == NULL) +		return -EINVAL; +	if (strlen(token) >= MAX_NAME_LEN) +		return -EINVAL; +	strncpy(config, token, MAX_NAME_LEN); + +	mutex_lock(&pinctrl_maps_mutex); + +	/* Parse the pinctrl map and look for the selected dev/state/pin */  	for_each_maps(maps_node, i, map) { -		if (map->type != PIN_MAP_TYPE_CONFIGS_PIN) +		if (strcmp(map->dev_name, dbg->dev_name))  			continue; - -		if (strncmp(map->name, dbg_state_name, MAX_NAME_LEN) > 0) +		if (map->type != dbg->map_type) +			continue; +		if (strcmp(map->name, dbg->state_name))  			continue;  		/*  we found the right pin / state, so overwrite config */ -		for (j = 0; j < map->data.configs.num_configs; j++) { -			if (strncmp(map->data.configs.group_or_pin, dbg_pinname, -						MAX_NAME_LEN) == 0) -				map->data.configs.configs[j] = dbg_config; +		if (!strcmp(map->data.configs.group_or_pin, dbg->pin_name)) { +			found = true; +			pctldev = get_pinctrl_dev_from_devname( +					map->ctrl_dev_name); +			configs = &map->data.configs; +			break;  		}  	} -	mutex_unlock(&pinctrl_mutex); +	if (!found) { +		count = -EINVAL; +		goto exit; +	} + +	if (pctldev) +		confops = pctldev->desc->confops; + +	if (confops && confops->pin_config_dbg_parse_modify) { +		for (i = 0; i < configs->num_configs; i++) { +			confops->pin_config_dbg_parse_modify(pctldev, +						     config, +						     &configs->configs[i]); +		} +	} + +exit: +	mutex_unlock(&pinctrl_maps_mutex);  	return count;  } +static int pinconf_dbg_config_open(struct inode *inode, struct file *file) +{ +	return single_open(file, pinconf_dbg_config_print, inode->i_private); +} +  static const struct file_operations pinconf_dbg_pinconfig_fops = {  	.open = pinconf_dbg_config_open,  	.write = pinconf_dbg_config_write, @@ -782,10 +813,6 @@ void pinconf_init_device_debugfs(struct dentry *devroot,  			    devroot, pctldev, &pinconf_pins_ops);  	debugfs_create_file("pinconf-groups", S_IFREG | S_IRUGO,  			    devroot, pctldev, &pinconf_groups_ops); -	debugfs_create_file("pinconf-name", (S_IRUGO | S_IWUSR | S_IWGRP), -			    devroot, pctldev, &pinconf_dbg_pinname_fops); -	debugfs_create_file("pinconf-state",  (S_IRUGO | S_IWUSR | S_IWGRP), -			    devroot, pctldev, &pinconf_dbg_pinstate_fops);  	debugfs_create_file("pinconf-config",  (S_IRUGO | S_IWUSR | S_IWGRP),  			    devroot, pctldev, &pinconf_dbg_pinconfig_fops);  }  |