| /* Copyright (c) 2015-2017, 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 <linux/gpio.h> |
| #include <linux/of_gpio.h> |
| #include <linux/platform_device.h> |
| #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 (gpioset_info[client].gpioset_state[gp_set] == 1) { |
| 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; |
| } |