/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include "msm-audio-pinctrl.h" /* * pinctrl -- handle to query pinctrl apis * cdc lines -- stores pinctrl handles for pinctrl states * active_set -- maintain the overall pinctrl state */ struct cdc_pinctrl_info { struct pinctrl *pinctrl; struct pinctrl_state **cdc_lines; int active_set; }; /* * gpiosets -- stores all gpiosets mentioned in dtsi file * gpiosets_comb_names -- stores all possible gpioset combinations * gpioset_state -- maintains counter for each gpioset * gpiosets_max -- maintain the total supported gpiosets * gpiosets_comb_max -- maintain the total gpiosets combinations */ struct cdc_gpioset_info { char **gpiosets; char **gpiosets_comb_names; uint8_t *gpioset_state; int gpiosets_max; int gpiosets_comb_max; }; static struct cdc_pinctrl_info pinctrl_info[MAX_PINCTRL_CLIENT]; static struct cdc_gpioset_info gpioset_info[MAX_PINCTRL_CLIENT]; /* Finds the index for the gpio set in the dtsi file */ int msm_get_gpioset_index(enum pinctrl_client client, char *keyword) { int i; for (i = 0; i < gpioset_info[client].gpiosets_max; i++) { if (!(strcmp(gpioset_info[client].gpiosets[i], keyword))) break; } /* Checking if the keyword is present in dtsi or not */ if (i != gpioset_info[client].gpiosets_max) return i; else return -EINVAL; } /* * This function reads the following from dtsi file * 1. All gpio sets * 2. All combinations of gpio sets * 3. Pinctrl handles to gpio sets * * Returns error if there is * 1. Problem reading from dtsi file * 2. Memory allocation failure */ int msm_gpioset_initialize(enum pinctrl_client client, struct device *dev) { struct pinctrl *pinctrl; const char *gpioset_names = "qcom,msm-gpios"; const char *gpioset_combinations = "qcom,pinctrl-names"; const char *gpioset_names_str = NULL; const char *gpioset_comb_str = NULL; int num_strings = 0; int ret = 0; int i = 0; pr_debug("%s\n", __func__); pinctrl = devm_pinctrl_get(dev); if (IS_ERR(pinctrl)) { pr_err("%s: Unable to get pinctrl handle\n", __func__); return -EINVAL; } pinctrl_info[client].pinctrl = pinctrl; /* Reading of gpio sets */ num_strings = of_property_count_strings(dev->of_node, gpioset_names); if (num_strings < 0) { dev_err(dev, "%s: missing %s in dt node or length is incorrect\n", __func__, gpioset_names); goto err; } gpioset_info[client].gpiosets_max = num_strings; gpioset_info[client].gpiosets = devm_kzalloc(dev, gpioset_info[client].gpiosets_max * sizeof(char *), GFP_KERNEL); if (!gpioset_info[client].gpiosets) { dev_err(dev, "Can't allocate memory for gpio set names\n"); ret = -ENOMEM; goto err; } for (i = 0; i < num_strings; i++) { ret = of_property_read_string_index(dev->of_node, gpioset_names, i, &gpioset_names_str); gpioset_info[client].gpiosets[i] = devm_kzalloc(dev, (strlen(gpioset_names_str) + 1), GFP_KERNEL); if (!gpioset_info[client].gpiosets[i]) { dev_err(dev, "%s: Can't allocate gpiosets[%d] data\n", __func__, i); ret = -ENOMEM; goto err; } strlcpy(gpioset_info[client].gpiosets[i], gpioset_names_str, strlen(gpioset_names_str)+1); gpioset_names_str = NULL; } num_strings = 0; /* Allocating memory for gpio set counter */ gpioset_info[client].gpioset_state = devm_kzalloc(dev, gpioset_info[client].gpiosets_max * sizeof(uint8_t), GFP_KERNEL); if (!gpioset_info[client].gpioset_state) { dev_err(dev, "Can't allocate memory for gpio set counter\n"); ret = -ENOMEM; goto err; } /* Reading of all combinations of gpio sets */ num_strings = of_property_count_strings(dev->of_node, gpioset_combinations); if (num_strings < 0) { dev_err(dev, "%s: missing %s in dt node or length is incorrect\n", __func__, gpioset_combinations); goto err; } gpioset_info[client].gpiosets_comb_max = num_strings; gpioset_info[client].gpiosets_comb_names = devm_kzalloc(dev, num_strings * sizeof(char *), GFP_KERNEL); if (!gpioset_info[client].gpiosets_comb_names) { ret = -ENOMEM; goto err; } for (i = 0; i < gpioset_info[client].gpiosets_comb_max; i++) { ret = of_property_read_string_index(dev->of_node, gpioset_combinations, i, &gpioset_comb_str); gpioset_info[client].gpiosets_comb_names[i] = devm_kzalloc(dev, (strlen(gpioset_comb_str) + 1), GFP_KERNEL); if (!gpioset_info[client].gpiosets_comb_names[i]) { ret = -ENOMEM; goto err; } strlcpy(gpioset_info[client].gpiosets_comb_names[i], gpioset_comb_str, strlen(gpioset_comb_str)+1); pr_debug("%s: GPIO configuration %s\n", __func__, gpioset_info[client].gpiosets_comb_names[i]); gpioset_comb_str = NULL; } /* Allocating memory for handles to pinctrl states */ pinctrl_info[client].cdc_lines = devm_kzalloc(dev, num_strings * sizeof(char *), GFP_KERNEL); if (!pinctrl_info[client].cdc_lines) { ret = -ENOMEM; goto err; } /* Get pinctrl handles for gpio sets in dtsi file */ for (i = 0; i < num_strings; i++) { pinctrl_info[client].cdc_lines[i] = pinctrl_lookup_state( pinctrl, (const char *)gpioset_info[client]. gpiosets_comb_names[i]); if (IS_ERR(pinctrl_info[client].cdc_lines[i])) pr_err("%s: Unable to get pinctrl handle for %s\n", __func__, gpioset_info[client]. gpiosets_comb_names[i]); } goto success; err: /* Free up memory allocated for gpio set combinations */ for (i = 0; i < gpioset_info[client].gpiosets_max; i++) { if (gpioset_info[client].gpiosets[i] != NULL) { devm_kfree(dev, gpioset_info[client].gpiosets[i]); gpioset_info[client].gpiosets[i] = NULL; } } if (gpioset_info[client].gpiosets != NULL) { devm_kfree(dev, gpioset_info[client].gpiosets); gpioset_info[client].gpiosets = NULL; } /* Free up memory allocated for gpio set combinations */ for (i = 0; i < gpioset_info[client].gpiosets_comb_max; i++) { if (gpioset_info[client].gpiosets_comb_names[i] != NULL) { devm_kfree(dev, gpioset_info[client].gpiosets_comb_names[i]); gpioset_info[client].gpiosets_comb_names[i] = NULL; } } if (gpioset_info[client].gpiosets_comb_names != NULL) { devm_kfree(dev, gpioset_info[client].gpiosets_comb_names); gpioset_info[client].gpiosets_comb_names = NULL; } /* Free up memory allocated for handles to pinctrl states */ if (pinctrl_info[client].cdc_lines != NULL) { devm_kfree(dev, pinctrl_info[client].cdc_lines); pinctrl_info[client].cdc_lines = NULL; } /* Free up memory allocated for counter of gpio sets */ if (gpioset_info[client].gpioset_state != NULL) { devm_kfree(dev, gpioset_info[client].gpioset_state); gpioset_info[client].gpioset_state = NULL; } success: return ret; } int msm_gpioset_activate(enum pinctrl_client client, char *keyword) { int ret = 0; int gp_set = 0; int active_set = 0; gp_set = msm_get_gpioset_index(client, keyword); if (gp_set < 0) { pr_err("%s: gpio set name does not exist\n", __func__); return gp_set; } if (!gpioset_info[client].gpioset_state[gp_set]) { /* * If pinctrl pointer is not valid, * no need to proceed further */ active_set = pinctrl_info[client].active_set; if (IS_ERR(pinctrl_info[client].cdc_lines[active_set])) return 0; pinctrl_info[client].active_set |= (1 << gp_set); active_set = pinctrl_info[client].active_set; pr_debug("%s: pinctrl.active_set: %d\n", __func__, active_set); /* Select the appropriate pinctrl state */ ret = pinctrl_select_state(pinctrl_info[client].pinctrl, pinctrl_info[client].cdc_lines[active_set]); } gpioset_info[client].gpioset_state[gp_set]++; return ret; } int msm_gpioset_suspend(enum pinctrl_client client, char *keyword) { int ret = 0; int gp_set = 0; int active_set = 0; gp_set = msm_get_gpioset_index(client, keyword); if (gp_set < 0) { pr_err("%s: gpio set name does not exist\n", __func__); return gp_set; } if (1 == gpioset_info[client].gpioset_state[gp_set]) { pinctrl_info[client].active_set &= ~(1 << gp_set); /* * If pinctrl pointer is not valid, * no need to proceed further */ active_set = pinctrl_info[client].active_set; if (IS_ERR(pinctrl_info[client].cdc_lines[active_set])) return -EINVAL; pr_debug("%s: pinctrl.active_set: %d\n", __func__, pinctrl_info[client].active_set); /* Select the appropriate pinctrl state */ ret = pinctrl_select_state(pinctrl_info[client].pinctrl, pinctrl_info[client].cdc_lines[pinctrl_info[client]. active_set]); } if (!(gpioset_info[client].gpioset_state[gp_set])) { pr_err("%s: Invalid call to de activate gpios: %d\n", __func__, gpioset_info[client].gpioset_state[gp_set]); return -EINVAL; } gpioset_info[client].gpioset_state[gp_set]--; return ret; }