Loading Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt +24 −9 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ PMIC's from Qualcomm. "qcom,pm8941-gpio" "qcom,pma8084-gpio" And must contain either "qcom,spmi-gpio" or "qcom,ssbi-gpio" if the device is on an spmi bus or an ssbi bus respectively - reg: Usage: required Value type: <prop-encoded-array> Loading Loading @@ -93,7 +96,11 @@ to specify in a pin configuration subnode: "dtest1", "dtest2", "dtest3", "dtest4" "dtest4", And following values are supported by LV/MV GPIO subtypes: "func3", "func4", "analog" - bias-disable: Usage: optional Loading Loading @@ -178,10 +185,18 @@ to specify in a pin configuration subnode: Value type: <none> Definition: The specified pins are configured in open-source mode. - qcom,atest: Usage: optional Value type: <u32> Definition: Selects ATEST rail to route to GPIO when it's configured in analog-pass-through mode by specifying "analog" function. Valid values are 0-3 corresponding to PMIC_GPIO_AOUT_ATESTx defined in <dt-bindings/pinctrl/qcom,pmic-gpio.h>. Example: pm8921_gpio: gpio@150 { compatible = "qcom,pm8921-gpio"; compatible = "qcom,pm8921-gpio", "qcom,ssbi-gpio"; reg = <0x150 0x160>; interrupts = <192 1>, <193 1>, <194 1>, <195 1>, <196 1>, <197 1>, Loading Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt +15 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ of PMIC's from Qualcomm. "qcom,pm8941-mpp", "qcom,pma8084-mpp", And must contain either "qcom,spmi-mpp" or "qcom,ssbi-mpp" if the device is on an spmi bus or an ssbi bus respectively. - reg: Usage: required Value type: <prop-encoded-array> Loading Loading @@ -139,7 +142,7 @@ to specify in a pin configuration subnode: - qcom,dtest: Usage: optional Value type: <u32> Definition: Selects which dtest rail to be routed in the various functions. Definition: Selects which dtest rail to be routed for digital output. Valid values are 1-4 - qcom,amux-route: Loading @@ -153,10 +156,20 @@ to specify in a pin configuration subnode: Value type: <none> Definition: Indicates that the pin should be operating in paired mode. - qcom,dtest-buffer: Usage: optional Value type: <u32> Definition: Selects which dtest rail to be routed for digital input. It's also valid when the pin is configured as digital input and output. 4 dtest rails supported in total and more than one rail could be selected simultaneously. Each bit of the 4 LSBs represent one dtest rail, such as [3:0] = 0101 means both dtest1 and dtest3 are selected. Valid values are 1-15. Example: mpps@a000 { compatible = "qcom,pm8841-mpp"; compatible = "qcom,pm8841-mpp", "qcom,spmi-mpp"; reg = <0xa000>; gpio-controller; #gpio-cells = <2>; Loading drivers/base/platform.c +20 −0 Original line number Diff line number Diff line Loading @@ -116,6 +116,26 @@ int platform_get_irq(struct platform_device *dev, unsigned int num) } EXPORT_SYMBOL_GPL(platform_get_irq); /** * platform_irq_count - Count the number of IRQs a platform device uses * @dev: platform device * * Return: Number of IRQs a platform device uses or EPROBE_DEFER */ int platform_irq_count(struct platform_device *dev) { int ret, nr = 0; while ((ret = platform_get_irq(dev, nr)) >= 0) nr++; if (ret == -EPROBE_DEFER) return ret; return nr; } EXPORT_SYMBOL_GPL(platform_irq_count); /** * platform_get_resource_byname - get a resource for a device by name * @dev: platform device Loading drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +245 −41 Original line number Diff line number Diff line /* * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. * Copyright (c) 2012-2014, 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 Loading @@ -14,6 +14,7 @@ #include <linux/gpio.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinmux.h> Loading @@ -39,6 +40,8 @@ #define PMIC_GPIO_SUBTYPE_GPIOC_4CH 0x5 #define PMIC_GPIO_SUBTYPE_GPIO_8CH 0x9 #define PMIC_GPIO_SUBTYPE_GPIOC_8CH 0xd #define PMIC_GPIO_SUBTYPE_GPIO_LV 0x10 #define PMIC_GPIO_SUBTYPE_GPIO_MV 0x11 #define PMIC_MPP_REG_RT_STS 0x10 #define PMIC_MPP_REG_RT_STS_VAL_MASK 0x1 Loading @@ -47,8 +50,10 @@ #define PMIC_GPIO_REG_MODE_CTL 0x40 #define PMIC_GPIO_REG_DIG_VIN_CTL 0x41 #define PMIC_GPIO_REG_DIG_PULL_CTL 0x42 #define PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL 0x44 #define PMIC_GPIO_REG_DIG_OUT_CTL 0x45 #define PMIC_GPIO_REG_EN_CTL 0x46 #define PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL 0x4A /* PMIC_GPIO_REG_MODE_CTL */ #define PMIC_GPIO_REG_MODE_VALUE_SHIFT 0x1 Loading @@ -57,6 +62,13 @@ #define PMIC_GPIO_REG_MODE_DIR_SHIFT 4 #define PMIC_GPIO_REG_MODE_DIR_MASK 0x7 #define PMIC_GPIO_MODE_DIGITAL_INPUT 0 #define PMIC_GPIO_MODE_DIGITAL_OUTPUT 1 #define PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT 2 #define PMIC_GPIO_MODE_ANALOG_PASS_THRU 3 #define PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK 0x3 /* PMIC_GPIO_REG_DIG_VIN_CTL */ #define PMIC_GPIO_REG_VIN_SHIFT 0 #define PMIC_GPIO_REG_VIN_MASK 0x7 Loading @@ -68,6 +80,11 @@ #define PMIC_GPIO_PULL_DOWN 4 #define PMIC_GPIO_PULL_DISABLE 5 /* PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL for LV/MV */ #define PMIC_GPIO_LV_MV_OUTPUT_INVERT 0x80 #define PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT 7 #define PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK 0xF /* PMIC_GPIO_REG_DIG_OUT_CTL */ #define PMIC_GPIO_REG_OUT_STRENGTH_SHIFT 0 #define PMIC_GPIO_REG_OUT_STRENGTH_MASK 0x3 Loading @@ -87,9 +104,28 @@ #define PMIC_GPIO_PHYSICAL_OFFSET 1 /* PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL */ #define PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK 0x3 /* Qualcomm specific pin configurations */ #define PMIC_GPIO_CONF_PULL_UP (PIN_CONFIG_END + 1) #define PMIC_GPIO_CONF_STRENGTH (PIN_CONFIG_END + 2) #define PMIC_GPIO_CONF_ATEST (PIN_CONFIG_END + 3) /* The index of each function in pmic_gpio_functions[] array */ enum pmic_gpio_func_index { PMIC_GPIO_FUNC_INDEX_NORMAL = 0x00, PMIC_GPIO_FUNC_INDEX_PAIRED = 0x01, PMIC_GPIO_FUNC_INDEX_FUNC1 = 0x02, PMIC_GPIO_FUNC_INDEX_FUNC2 = 0x03, PMIC_GPIO_FUNC_INDEX_FUNC3 = 0x04, PMIC_GPIO_FUNC_INDEX_FUNC4 = 0x05, PMIC_GPIO_FUNC_INDEX_DTEST1 = 0x06, PMIC_GPIO_FUNC_INDEX_DTEST2 = 0x07, PMIC_GPIO_FUNC_INDEX_DTEST3 = 0x08, PMIC_GPIO_FUNC_INDEX_DTEST4 = 0x09, PMIC_GPIO_FUNC_INDEX_ANALOG = 0x10, }; /** * struct pmic_gpio_pad - keep current GPIO settings Loading @@ -101,12 +137,14 @@ * open-drain or open-source mode. * @output_enabled: Set to true if GPIO output logic is enabled. * @input_enabled: Set to true if GPIO input buffer logic is enabled. * @lv_mv_type: Set to true if GPIO subtype is GPIO_LV(0x10) or GPIO_MV(0x11). * @num_sources: Number of power-sources supported by this GPIO. * @power_source: Current power-source used. * @buffer_type: Push-pull, open-drain or open-source. * @pullup: Constant current which flow trough GPIO output buffer. * @strength: No, Low, Medium, High * @function: See pmic_gpio_functions[] * @atest: the ATEST selection for GPIO analog-pass-through mode */ struct pmic_gpio_pad { u16 base; Loading @@ -116,12 +154,14 @@ struct pmic_gpio_pad { bool have_buffer; bool output_enabled; bool input_enabled; bool lv_mv_type; unsigned int num_sources; unsigned int power_source; unsigned int buffer_type; unsigned int pullup; unsigned int strength; unsigned int function; unsigned int atest; }; struct pmic_gpio_state { Loading @@ -134,12 +174,14 @@ struct pmic_gpio_state { static const struct pinconf_generic_params pmic_gpio_bindings[] = { {"qcom,pull-up-strength", PMIC_GPIO_CONF_PULL_UP, 0}, {"qcom,drive-strength", PMIC_GPIO_CONF_STRENGTH, 0}, {"qcom,atest", PMIC_GPIO_CONF_ATEST, 0}, }; #ifdef CONFIG_DEBUG_FS static const struct pin_config_item pmic_conf_items[ARRAY_SIZE(pmic_gpio_bindings)] = { PCONFDUMP(PMIC_GPIO_CONF_PULL_UP, "pull up strength", NULL, true), PCONFDUMP(PMIC_GPIO_CONF_STRENGTH, "drive-strength", NULL, true), PCONFDUMP(PMIC_GPIO_CONF_ATEST, "atest", NULL, true), }; #endif Loading @@ -151,11 +193,25 @@ static const char *const pmic_gpio_groups[] = { "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36", }; /* * Treat LV/MV GPIO analog-pass-through mode as a function, add it * to the end of the function list. Add placeholder for the reserved * functions defined in LV/MV OUTPUT_SOURCE_SEL register. */ static const char *const pmic_gpio_functions[] = { PMIC_GPIO_FUNC_NORMAL, PMIC_GPIO_FUNC_PAIRED, PMIC_GPIO_FUNC_FUNC1, PMIC_GPIO_FUNC_FUNC2, PMIC_GPIO_FUNC_DTEST1, PMIC_GPIO_FUNC_DTEST2, PMIC_GPIO_FUNC_DTEST3, PMIC_GPIO_FUNC_DTEST4, [PMIC_GPIO_FUNC_INDEX_NORMAL] = PMIC_GPIO_FUNC_NORMAL, [PMIC_GPIO_FUNC_INDEX_PAIRED] = PMIC_GPIO_FUNC_PAIRED, [PMIC_GPIO_FUNC_INDEX_FUNC1] = PMIC_GPIO_FUNC_FUNC1, [PMIC_GPIO_FUNC_INDEX_FUNC2] = PMIC_GPIO_FUNC_FUNC2, [PMIC_GPIO_FUNC_INDEX_FUNC3] = PMIC_GPIO_FUNC_FUNC3, [PMIC_GPIO_FUNC_INDEX_FUNC4] = PMIC_GPIO_FUNC_FUNC4, [PMIC_GPIO_FUNC_INDEX_DTEST1] = PMIC_GPIO_FUNC_DTEST1, [PMIC_GPIO_FUNC_INDEX_DTEST2] = PMIC_GPIO_FUNC_DTEST2, [PMIC_GPIO_FUNC_INDEX_DTEST3] = PMIC_GPIO_FUNC_DTEST3, [PMIC_GPIO_FUNC_INDEX_DTEST4] = PMIC_GPIO_FUNC_DTEST4, "reserved-a", "reserved-b", "reserved-c", "reserved-d", "reserved-e", "reserved-f", [PMIC_GPIO_FUNC_INDEX_ANALOG] = PMIC_GPIO_FUNC_ANALOG, }; static inline struct pmic_gpio_state *to_gpio_state(struct gpio_chip *chip) Loading Loading @@ -252,12 +308,64 @@ static int pmic_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned function, pad->function = function; val = 0; val = PMIC_GPIO_MODE_DIGITAL_INPUT; if (pad->output_enabled) { if (pad->input_enabled) val = 2; val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT; else val = 1; val = PMIC_GPIO_MODE_DIGITAL_OUTPUT; } if (function > PMIC_GPIO_FUNC_INDEX_DTEST4 && function < PMIC_GPIO_FUNC_INDEX_ANALOG) { pr_err("reserved function: %s hasn't been enabled\n", pmic_gpio_functions[function]); return -EINVAL; } if (pad->lv_mv_type) { if (pad->function == PMIC_GPIO_FUNC_INDEX_ANALOG) { val = PMIC_GPIO_MODE_ANALOG_PASS_THRU; ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); if (ret < 0) return ret; ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, pad->atest); if (ret < 0) return ret; } else { ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); if (ret < 0) return ret; val = pad->out_value << PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT; val |= pad->function & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val); if (ret < 0) return ret; } } else { /* * GPIO not of LV/MV subtype doesn't have "func3", "func4" * "analog" functions, and "dtest1" to "dtest4" functions * have register value 2 bits lower than the function index * in pmic_gpio_functions[]. */ if (function == PMIC_GPIO_FUNC_INDEX_FUNC3 || function == PMIC_GPIO_FUNC_INDEX_FUNC4 || function == PMIC_GPIO_FUNC_INDEX_ANALOG) { return -EINVAL; } else if (function >= PMIC_GPIO_FUNC_INDEX_DTEST1 && function <= PMIC_GPIO_FUNC_INDEX_DTEST4) { pad->function -= (PMIC_GPIO_FUNC_INDEX_DTEST1 - PMIC_GPIO_FUNC_INDEX_FUNC3); } val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT; Loading @@ -267,6 +375,7 @@ static int pmic_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned function, ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); if (ret < 0) return ret; } val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT; Loading Loading @@ -326,6 +435,9 @@ static int pmic_gpio_config_get(struct pinctrl_dev *pctldev, case PMIC_GPIO_CONF_STRENGTH: arg = pad->strength; break; case PMIC_GPIO_CONF_ATEST: arg = pad->atest; break; default: return -EINVAL; } Loading Loading @@ -400,6 +512,11 @@ static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin, return -EINVAL; pad->strength = arg; break; case PMIC_GPIO_CONF_ATEST: if (arg > PMIC_GPIO_AOUT_ATEST4) return -EINVAL; pad->atest = arg; break; default: return -EINVAL; } Loading @@ -424,19 +541,53 @@ static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin, if (ret < 0) return ret; val = 0; val = PMIC_GPIO_MODE_DIGITAL_INPUT; if (pad->output_enabled) { if (pad->input_enabled) val = 2; val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT; else val = 1; val = PMIC_GPIO_MODE_DIGITAL_OUTPUT; } if (pad->lv_mv_type) { if (pad->function == PMIC_GPIO_FUNC_INDEX_ANALOG) { val = PMIC_GPIO_MODE_ANALOG_PASS_THRU; ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); if (ret < 0) return ret; ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, pad->atest); if (ret < 0) return ret; } else { ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); if (ret < 0) return ret; val = pad->out_value << PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT; val |= pad->function & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val); if (ret < 0) return ret; } } else { val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT; val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT; return pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); if (ret < 0) return ret; } return ret; } static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev, Loading @@ -444,7 +595,7 @@ static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev, { struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); struct pmic_gpio_pad *pad; int ret, val; int ret, val, function; static const char *const biases[] = { "pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA", Loading Loading @@ -475,9 +626,21 @@ static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev, ret &= PMIC_MPP_REG_RT_STS_VAL_MASK; pad->out_value = ret; } /* * For GPIO not of LV/MV subtypes, the register value of * the function mapping from "dtest1" to "dtest4" is 2 bits * lower than the function index in pmic_gpio_functions[]. */ if (!pad->lv_mv_type && pad->function >= PMIC_GPIO_FUNC_INDEX_FUNC3) { function = pad->function + (PMIC_GPIO_FUNC_INDEX_DTEST1 - PMIC_GPIO_FUNC_INDEX_FUNC3); } else { function = pad->function; } seq_printf(s, " %-4s", pad->output_enabled ? "out" : "in"); seq_printf(s, " %-7s", pmic_gpio_functions[pad->function]); seq_printf(s, " %-7s", pmic_gpio_functions[function]); seq_printf(s, " vin-%d", pad->power_source); seq_printf(s, " %-27s", biases[pad->pullup]); seq_printf(s, " %-10s", buffer_types[pad->buffer_type]); Loading Loading @@ -622,11 +785,36 @@ static int pmic_gpio_populate(struct pmic_gpio_state *state, case PMIC_GPIO_SUBTYPE_GPIOC_8CH: pad->num_sources = 8; break; case PMIC_GPIO_SUBTYPE_GPIO_LV: pad->num_sources = 1; pad->have_buffer = true; pad->lv_mv_type = true; break; case PMIC_GPIO_SUBTYPE_GPIO_MV: pad->num_sources = 2; pad->have_buffer = true; pad->lv_mv_type = true; break; default: dev_err(state->dev, "unknown GPIO type 0x%x\n", subtype); return -ENODEV; } if (pad->lv_mv_type) { val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL); if (val < 0) return val; pad->out_value = !!(val & PMIC_GPIO_LV_MV_OUTPUT_INVERT); pad->function = val & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL); if (val < 0) return val; dir = val & PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK; } else { val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL); if (val < 0) return val; Loading @@ -635,27 +823,34 @@ static int pmic_gpio_populate(struct pmic_gpio_state *state, dir = val >> PMIC_GPIO_REG_MODE_DIR_SHIFT; dir &= PMIC_GPIO_REG_MODE_DIR_MASK; pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK; } switch (dir) { case 0: case PMIC_GPIO_MODE_DIGITAL_INPUT: pad->input_enabled = true; pad->output_enabled = false; break; case 1: case PMIC_GPIO_MODE_DIGITAL_OUTPUT: pad->input_enabled = false; pad->output_enabled = true; break; case 2: case PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT: pad->input_enabled = true; pad->output_enabled = true; break; case PMIC_GPIO_MODE_ANALOG_PASS_THRU: if (pad->lv_mv_type) pad->function = PMIC_GPIO_FUNC_INDEX_ANALOG; else return -ENODEV; break; default: dev_err(state->dev, "unknown GPIO direction\n"); return -ENODEV; } pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK; val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL); if (val < 0) return val; Loading @@ -680,6 +875,13 @@ static int pmic_gpio_populate(struct pmic_gpio_state *state, pad->buffer_type = val >> PMIC_GPIO_REG_OUT_TYPE_SHIFT; pad->buffer_type &= PMIC_GPIO_REG_OUT_TYPE_MASK; if (pad->function == PMIC_GPIO_FUNC_INDEX_ANALOG) { val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL); if (val < 0) return val; pad->atest = val & PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK; } /* Pin could be disabled with PIN_CONFIG_BIAS_HIGH_IMPEDANCE */ pad->is_enabled = true; return 0; Loading @@ -693,18 +895,19 @@ static int pmic_gpio_probe(struct platform_device *pdev) struct pmic_gpio_pad *pad, *pads; struct pmic_gpio_state *state; int ret, npins, i; u32 res[2]; u32 reg; ret = of_property_read_u32_array(dev->of_node, "reg", res, 2); ret = of_property_read_u32(dev->of_node, "reg", ®); if (ret < 0) { dev_err(dev, "missing base address and/or range"); dev_err(dev, "missing base address"); return ret; } npins = res[1] / PMIC_GPIO_ADDRESS_RANGE; npins = platform_irq_count(pdev); if (!npins) return -EINVAL; if (npins < 0) return npins; BUG_ON(npins > ARRAY_SIZE(pmic_gpio_groups)); Loading Loading @@ -752,7 +955,7 @@ static int pmic_gpio_probe(struct platform_device *pdev) if (pad->irq < 0) return pad->irq; pad->base = res[0] + i * PMIC_GPIO_ADDRESS_RANGE; pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE; ret = pmic_gpio_populate(state, pad); if (ret < 0) Loading Loading @@ -805,6 +1008,7 @@ static const struct of_device_id pmic_gpio_of_match[] = { { .compatible = "qcom,pm8916-gpio" }, /* 4 GPIO's */ { .compatible = "qcom,pm8941-gpio" }, /* 36 GPIO's */ { .compatible = "qcom,pma8084-gpio" }, /* 22 GPIO's */ { .compatible = "qcom,spmi-gpio" }, /* Generic */ { }, }; Loading drivers/pinctrl/qcom/pinctrl-spmi-mpp.c +58 −9 Original line number Diff line number Diff line /* * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. * Copyright (c) 2012-2014, 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 Loading @@ -14,6 +14,7 @@ #include <linux/gpio.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinmux.h> Loading Loading @@ -87,6 +88,10 @@ #define PMIC_MPP_REG_AIN_ROUTE_SHIFT 0 #define PMIC_MPP_REG_AIN_ROUTE_MASK 0x7 /* PMIC_MPP_REG_SINK_CTL */ #define PMIC_MPP_REG_CURRENT_SINK_MASK 0x7 #define MPP_CURRENT_SINK_MA_STEP_SIZE 5 #define PMIC_MPP_MODE_DIGITAL_INPUT 0 #define PMIC_MPP_MODE_DIGITAL_OUTPUT 1 #define PMIC_MPP_MODE_DIGITAL_BIDIR 2 Loading @@ -106,6 +111,7 @@ #define PMIC_MPP_CONF_ANALOG_LEVEL (PIN_CONFIG_END + 2) #define PMIC_MPP_CONF_DTEST_SELECTOR (PIN_CONFIG_END + 3) #define PMIC_MPP_CONF_PAIRED (PIN_CONFIG_END + 4) #define PMIC_MPP_CONF_DTEST_BUFFER (PIN_CONFIG_END + 5) /** * struct pmic_mpp_pad - keep current MPP settings Loading @@ -124,6 +130,7 @@ * @function: See pmic_mpp_functions[]. * @drive_strength: Amount of current in sink mode * @dtest: DTEST route selector * @dtest_buffer: the DTEST buffer selection for digital input mode */ struct pmic_mpp_pad { u16 base; Loading @@ -141,6 +148,7 @@ struct pmic_mpp_pad { unsigned int function; unsigned int drive_strength; unsigned int dtest; unsigned int dtest_buffer; }; struct pmic_mpp_state { Loading @@ -155,6 +163,7 @@ static const struct pinconf_generic_params pmic_mpp_bindings[] = { {"qcom,analog-level", PMIC_MPP_CONF_ANALOG_LEVEL, 0}, {"qcom,dtest", PMIC_MPP_CONF_DTEST_SELECTOR, 0}, {"qcom,paired", PMIC_MPP_CONF_PAIRED, 0}, {"qcom,dtest-buffer", PMIC_MPP_CONF_DTEST_BUFFER, 0}, }; #ifdef CONFIG_DEBUG_FS Loading @@ -163,6 +172,7 @@ static const struct pin_config_item pmic_conf_items[] = { PCONFDUMP(PMIC_MPP_CONF_ANALOG_LEVEL, "analog level", NULL, true), PCONFDUMP(PMIC_MPP_CONF_DTEST_SELECTOR, "dtest", NULL, true), PCONFDUMP(PMIC_MPP_CONF_PAIRED, "paired", NULL, false), PCONFDUMP(PMIC_MPP_CONF_DTEST_BUFFER, "dtest buffer", NULL, true), }; #endif Loading Loading @@ -392,6 +402,9 @@ static int pmic_mpp_config_get(struct pinctrl_dev *pctldev, case PMIC_MPP_CONF_ANALOG_LEVEL: arg = pad->aout_level; break; case PMIC_MPP_CONF_DTEST_BUFFER: arg = pad->dtest_buffer; break; default: return -EINVAL; } Loading Loading @@ -457,7 +470,7 @@ static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin, pad->dtest = arg; break; case PIN_CONFIG_DRIVE_STRENGTH: arg = pad->drive_strength; pad->drive_strength = arg; break; case PMIC_MPP_CONF_AMUX_ROUTE: if (arg >= PMIC_MPP_AMUX_ROUTE_ABUS4) Loading @@ -470,6 +483,15 @@ static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin, case PMIC_MPP_CONF_PAIRED: pad->paired = !!arg; break; case PMIC_MPP_CONF_DTEST_BUFFER: /* * 0xf is the max value which selects * 4 dtest rails simultaneously */ if (arg > 0xf) return -EINVAL; pad->dtest_buffer = arg; break; default: return -EINVAL; } Loading @@ -481,6 +503,11 @@ static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin, if (ret < 0) return ret; val = pad->dtest_buffer; ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_IN_CTL, val); if (ret < 0) return ret; val = pad->pullup << PMIC_MPP_REG_PULL_SHIFT; ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_PULL_CTL, val); Loading @@ -497,6 +524,16 @@ static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin, if (ret < 0) return ret; val = 0; if (pad->drive_strength >= MPP_CURRENT_SINK_MA_STEP_SIZE) val = DIV_ROUND_UP(pad->drive_strength, MPP_CURRENT_SINK_MA_STEP_SIZE) - 1; val &= PMIC_MPP_REG_CURRENT_SINK_MASK; ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_SINK_CTL, val); if (ret < 0) return ret; ret = pmic_mpp_write_mode_ctl(state, pad); if (ret < 0) return ret; Loading Loading @@ -544,6 +581,8 @@ static void pmic_mpp_config_dbg_show(struct pinctrl_dev *pctldev, seq_printf(s, " dtest%d", pad->dtest); if (pad->paired) seq_puts(s, " paired"); if (pad->dtest_buffer) seq_printf(s, " dtest buffer %d", pad->dtest_buffer); } } Loading Loading @@ -741,7 +780,7 @@ static int pmic_mpp_populate(struct pmic_mpp_state *state, sel &= PMIC_MPP_REG_MODE_FUNCTION_MASK; if (sel >= PMIC_MPP_SELECTOR_DTEST_FIRST) pad->dtest = sel + 1; pad->dtest = sel - PMIC_MPP_SELECTOR_DTEST_FIRST + 1; else if (sel == PMIC_MPP_SELECTOR_PAIRED) pad->paired = true; Loading @@ -752,6 +791,12 @@ static int pmic_mpp_populate(struct pmic_mpp_state *state, pad->power_source = val >> PMIC_MPP_REG_VIN_SHIFT; pad->power_source &= PMIC_MPP_REG_VIN_MASK; val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_IN_CTL); if (val < 0) return val; pad->dtest_buffer = val; val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_PULL_CTL); if (val < 0) return val; Loading @@ -770,7 +815,8 @@ static int pmic_mpp_populate(struct pmic_mpp_state *state, if (val < 0) return val; pad->drive_strength = val; val &= PMIC_MPP_REG_CURRENT_SINK_MASK; pad->drive_strength = (val + 1) * MPP_CURRENT_SINK_MA_STEP_SIZE; val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AOUT_CTL); if (val < 0) Loading @@ -795,17 +841,19 @@ static int pmic_mpp_probe(struct platform_device *pdev) struct pmic_mpp_pad *pad, *pads; struct pmic_mpp_state *state; int ret, npins, i; u32 res[2]; u32 reg; ret = of_property_read_u32_array(dev->of_node, "reg", res, 2); ret = of_property_read_u32(dev->of_node, "reg", ®); if (ret < 0) { dev_err(dev, "missing base address and/or range"); dev_err(dev, "missing base address"); return ret; } npins = res[1] / PMIC_MPP_ADDRESS_RANGE; npins = platform_irq_count(pdev); if (!npins) return -EINVAL; if (npins < 0) return npins; BUG_ON(npins > ARRAY_SIZE(pmic_mpp_groups)); Loading Loading @@ -854,7 +902,7 @@ static int pmic_mpp_probe(struct platform_device *pdev) if (pad->irq < 0) return pad->irq; pad->base = res[0] + i * PMIC_MPP_ADDRESS_RANGE; pad->base = reg + i * PMIC_MPP_ADDRESS_RANGE; ret = pmic_mpp_populate(state, pad); if (ret < 0) Loading Loading @@ -908,6 +956,7 @@ static const struct of_device_id pmic_mpp_of_match[] = { { .compatible = "qcom,pm8916-mpp" }, /* 4 MPP's */ { .compatible = "qcom,pm8941-mpp" }, /* 8 MPP's */ { .compatible = "qcom,pma8084-mpp" }, /* 8 MPP's */ { .compatible = "qcom,spmi-mpp" }, /* Generic */ { }, }; Loading Loading
Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt +24 −9 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ PMIC's from Qualcomm. "qcom,pm8941-gpio" "qcom,pma8084-gpio" And must contain either "qcom,spmi-gpio" or "qcom,ssbi-gpio" if the device is on an spmi bus or an ssbi bus respectively - reg: Usage: required Value type: <prop-encoded-array> Loading Loading @@ -93,7 +96,11 @@ to specify in a pin configuration subnode: "dtest1", "dtest2", "dtest3", "dtest4" "dtest4", And following values are supported by LV/MV GPIO subtypes: "func3", "func4", "analog" - bias-disable: Usage: optional Loading Loading @@ -178,10 +185,18 @@ to specify in a pin configuration subnode: Value type: <none> Definition: The specified pins are configured in open-source mode. - qcom,atest: Usage: optional Value type: <u32> Definition: Selects ATEST rail to route to GPIO when it's configured in analog-pass-through mode by specifying "analog" function. Valid values are 0-3 corresponding to PMIC_GPIO_AOUT_ATESTx defined in <dt-bindings/pinctrl/qcom,pmic-gpio.h>. Example: pm8921_gpio: gpio@150 { compatible = "qcom,pm8921-gpio"; compatible = "qcom,pm8921-gpio", "qcom,ssbi-gpio"; reg = <0x150 0x160>; interrupts = <192 1>, <193 1>, <194 1>, <195 1>, <196 1>, <197 1>, Loading
Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt +15 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ of PMIC's from Qualcomm. "qcom,pm8941-mpp", "qcom,pma8084-mpp", And must contain either "qcom,spmi-mpp" or "qcom,ssbi-mpp" if the device is on an spmi bus or an ssbi bus respectively. - reg: Usage: required Value type: <prop-encoded-array> Loading Loading @@ -139,7 +142,7 @@ to specify in a pin configuration subnode: - qcom,dtest: Usage: optional Value type: <u32> Definition: Selects which dtest rail to be routed in the various functions. Definition: Selects which dtest rail to be routed for digital output. Valid values are 1-4 - qcom,amux-route: Loading @@ -153,10 +156,20 @@ to specify in a pin configuration subnode: Value type: <none> Definition: Indicates that the pin should be operating in paired mode. - qcom,dtest-buffer: Usage: optional Value type: <u32> Definition: Selects which dtest rail to be routed for digital input. It's also valid when the pin is configured as digital input and output. 4 dtest rails supported in total and more than one rail could be selected simultaneously. Each bit of the 4 LSBs represent one dtest rail, such as [3:0] = 0101 means both dtest1 and dtest3 are selected. Valid values are 1-15. Example: mpps@a000 { compatible = "qcom,pm8841-mpp"; compatible = "qcom,pm8841-mpp", "qcom,spmi-mpp"; reg = <0xa000>; gpio-controller; #gpio-cells = <2>; Loading
drivers/base/platform.c +20 −0 Original line number Diff line number Diff line Loading @@ -116,6 +116,26 @@ int platform_get_irq(struct platform_device *dev, unsigned int num) } EXPORT_SYMBOL_GPL(platform_get_irq); /** * platform_irq_count - Count the number of IRQs a platform device uses * @dev: platform device * * Return: Number of IRQs a platform device uses or EPROBE_DEFER */ int platform_irq_count(struct platform_device *dev) { int ret, nr = 0; while ((ret = platform_get_irq(dev, nr)) >= 0) nr++; if (ret == -EPROBE_DEFER) return ret; return nr; } EXPORT_SYMBOL_GPL(platform_irq_count); /** * platform_get_resource_byname - get a resource for a device by name * @dev: platform device Loading
drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +245 −41 Original line number Diff line number Diff line /* * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. * Copyright (c) 2012-2014, 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 Loading @@ -14,6 +14,7 @@ #include <linux/gpio.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinmux.h> Loading @@ -39,6 +40,8 @@ #define PMIC_GPIO_SUBTYPE_GPIOC_4CH 0x5 #define PMIC_GPIO_SUBTYPE_GPIO_8CH 0x9 #define PMIC_GPIO_SUBTYPE_GPIOC_8CH 0xd #define PMIC_GPIO_SUBTYPE_GPIO_LV 0x10 #define PMIC_GPIO_SUBTYPE_GPIO_MV 0x11 #define PMIC_MPP_REG_RT_STS 0x10 #define PMIC_MPP_REG_RT_STS_VAL_MASK 0x1 Loading @@ -47,8 +50,10 @@ #define PMIC_GPIO_REG_MODE_CTL 0x40 #define PMIC_GPIO_REG_DIG_VIN_CTL 0x41 #define PMIC_GPIO_REG_DIG_PULL_CTL 0x42 #define PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL 0x44 #define PMIC_GPIO_REG_DIG_OUT_CTL 0x45 #define PMIC_GPIO_REG_EN_CTL 0x46 #define PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL 0x4A /* PMIC_GPIO_REG_MODE_CTL */ #define PMIC_GPIO_REG_MODE_VALUE_SHIFT 0x1 Loading @@ -57,6 +62,13 @@ #define PMIC_GPIO_REG_MODE_DIR_SHIFT 4 #define PMIC_GPIO_REG_MODE_DIR_MASK 0x7 #define PMIC_GPIO_MODE_DIGITAL_INPUT 0 #define PMIC_GPIO_MODE_DIGITAL_OUTPUT 1 #define PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT 2 #define PMIC_GPIO_MODE_ANALOG_PASS_THRU 3 #define PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK 0x3 /* PMIC_GPIO_REG_DIG_VIN_CTL */ #define PMIC_GPIO_REG_VIN_SHIFT 0 #define PMIC_GPIO_REG_VIN_MASK 0x7 Loading @@ -68,6 +80,11 @@ #define PMIC_GPIO_PULL_DOWN 4 #define PMIC_GPIO_PULL_DISABLE 5 /* PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL for LV/MV */ #define PMIC_GPIO_LV_MV_OUTPUT_INVERT 0x80 #define PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT 7 #define PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK 0xF /* PMIC_GPIO_REG_DIG_OUT_CTL */ #define PMIC_GPIO_REG_OUT_STRENGTH_SHIFT 0 #define PMIC_GPIO_REG_OUT_STRENGTH_MASK 0x3 Loading @@ -87,9 +104,28 @@ #define PMIC_GPIO_PHYSICAL_OFFSET 1 /* PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL */ #define PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK 0x3 /* Qualcomm specific pin configurations */ #define PMIC_GPIO_CONF_PULL_UP (PIN_CONFIG_END + 1) #define PMIC_GPIO_CONF_STRENGTH (PIN_CONFIG_END + 2) #define PMIC_GPIO_CONF_ATEST (PIN_CONFIG_END + 3) /* The index of each function in pmic_gpio_functions[] array */ enum pmic_gpio_func_index { PMIC_GPIO_FUNC_INDEX_NORMAL = 0x00, PMIC_GPIO_FUNC_INDEX_PAIRED = 0x01, PMIC_GPIO_FUNC_INDEX_FUNC1 = 0x02, PMIC_GPIO_FUNC_INDEX_FUNC2 = 0x03, PMIC_GPIO_FUNC_INDEX_FUNC3 = 0x04, PMIC_GPIO_FUNC_INDEX_FUNC4 = 0x05, PMIC_GPIO_FUNC_INDEX_DTEST1 = 0x06, PMIC_GPIO_FUNC_INDEX_DTEST2 = 0x07, PMIC_GPIO_FUNC_INDEX_DTEST3 = 0x08, PMIC_GPIO_FUNC_INDEX_DTEST4 = 0x09, PMIC_GPIO_FUNC_INDEX_ANALOG = 0x10, }; /** * struct pmic_gpio_pad - keep current GPIO settings Loading @@ -101,12 +137,14 @@ * open-drain or open-source mode. * @output_enabled: Set to true if GPIO output logic is enabled. * @input_enabled: Set to true if GPIO input buffer logic is enabled. * @lv_mv_type: Set to true if GPIO subtype is GPIO_LV(0x10) or GPIO_MV(0x11). * @num_sources: Number of power-sources supported by this GPIO. * @power_source: Current power-source used. * @buffer_type: Push-pull, open-drain or open-source. * @pullup: Constant current which flow trough GPIO output buffer. * @strength: No, Low, Medium, High * @function: See pmic_gpio_functions[] * @atest: the ATEST selection for GPIO analog-pass-through mode */ struct pmic_gpio_pad { u16 base; Loading @@ -116,12 +154,14 @@ struct pmic_gpio_pad { bool have_buffer; bool output_enabled; bool input_enabled; bool lv_mv_type; unsigned int num_sources; unsigned int power_source; unsigned int buffer_type; unsigned int pullup; unsigned int strength; unsigned int function; unsigned int atest; }; struct pmic_gpio_state { Loading @@ -134,12 +174,14 @@ struct pmic_gpio_state { static const struct pinconf_generic_params pmic_gpio_bindings[] = { {"qcom,pull-up-strength", PMIC_GPIO_CONF_PULL_UP, 0}, {"qcom,drive-strength", PMIC_GPIO_CONF_STRENGTH, 0}, {"qcom,atest", PMIC_GPIO_CONF_ATEST, 0}, }; #ifdef CONFIG_DEBUG_FS static const struct pin_config_item pmic_conf_items[ARRAY_SIZE(pmic_gpio_bindings)] = { PCONFDUMP(PMIC_GPIO_CONF_PULL_UP, "pull up strength", NULL, true), PCONFDUMP(PMIC_GPIO_CONF_STRENGTH, "drive-strength", NULL, true), PCONFDUMP(PMIC_GPIO_CONF_ATEST, "atest", NULL, true), }; #endif Loading @@ -151,11 +193,25 @@ static const char *const pmic_gpio_groups[] = { "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36", }; /* * Treat LV/MV GPIO analog-pass-through mode as a function, add it * to the end of the function list. Add placeholder for the reserved * functions defined in LV/MV OUTPUT_SOURCE_SEL register. */ static const char *const pmic_gpio_functions[] = { PMIC_GPIO_FUNC_NORMAL, PMIC_GPIO_FUNC_PAIRED, PMIC_GPIO_FUNC_FUNC1, PMIC_GPIO_FUNC_FUNC2, PMIC_GPIO_FUNC_DTEST1, PMIC_GPIO_FUNC_DTEST2, PMIC_GPIO_FUNC_DTEST3, PMIC_GPIO_FUNC_DTEST4, [PMIC_GPIO_FUNC_INDEX_NORMAL] = PMIC_GPIO_FUNC_NORMAL, [PMIC_GPIO_FUNC_INDEX_PAIRED] = PMIC_GPIO_FUNC_PAIRED, [PMIC_GPIO_FUNC_INDEX_FUNC1] = PMIC_GPIO_FUNC_FUNC1, [PMIC_GPIO_FUNC_INDEX_FUNC2] = PMIC_GPIO_FUNC_FUNC2, [PMIC_GPIO_FUNC_INDEX_FUNC3] = PMIC_GPIO_FUNC_FUNC3, [PMIC_GPIO_FUNC_INDEX_FUNC4] = PMIC_GPIO_FUNC_FUNC4, [PMIC_GPIO_FUNC_INDEX_DTEST1] = PMIC_GPIO_FUNC_DTEST1, [PMIC_GPIO_FUNC_INDEX_DTEST2] = PMIC_GPIO_FUNC_DTEST2, [PMIC_GPIO_FUNC_INDEX_DTEST3] = PMIC_GPIO_FUNC_DTEST3, [PMIC_GPIO_FUNC_INDEX_DTEST4] = PMIC_GPIO_FUNC_DTEST4, "reserved-a", "reserved-b", "reserved-c", "reserved-d", "reserved-e", "reserved-f", [PMIC_GPIO_FUNC_INDEX_ANALOG] = PMIC_GPIO_FUNC_ANALOG, }; static inline struct pmic_gpio_state *to_gpio_state(struct gpio_chip *chip) Loading Loading @@ -252,12 +308,64 @@ static int pmic_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned function, pad->function = function; val = 0; val = PMIC_GPIO_MODE_DIGITAL_INPUT; if (pad->output_enabled) { if (pad->input_enabled) val = 2; val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT; else val = 1; val = PMIC_GPIO_MODE_DIGITAL_OUTPUT; } if (function > PMIC_GPIO_FUNC_INDEX_DTEST4 && function < PMIC_GPIO_FUNC_INDEX_ANALOG) { pr_err("reserved function: %s hasn't been enabled\n", pmic_gpio_functions[function]); return -EINVAL; } if (pad->lv_mv_type) { if (pad->function == PMIC_GPIO_FUNC_INDEX_ANALOG) { val = PMIC_GPIO_MODE_ANALOG_PASS_THRU; ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); if (ret < 0) return ret; ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, pad->atest); if (ret < 0) return ret; } else { ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); if (ret < 0) return ret; val = pad->out_value << PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT; val |= pad->function & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val); if (ret < 0) return ret; } } else { /* * GPIO not of LV/MV subtype doesn't have "func3", "func4" * "analog" functions, and "dtest1" to "dtest4" functions * have register value 2 bits lower than the function index * in pmic_gpio_functions[]. */ if (function == PMIC_GPIO_FUNC_INDEX_FUNC3 || function == PMIC_GPIO_FUNC_INDEX_FUNC4 || function == PMIC_GPIO_FUNC_INDEX_ANALOG) { return -EINVAL; } else if (function >= PMIC_GPIO_FUNC_INDEX_DTEST1 && function <= PMIC_GPIO_FUNC_INDEX_DTEST4) { pad->function -= (PMIC_GPIO_FUNC_INDEX_DTEST1 - PMIC_GPIO_FUNC_INDEX_FUNC3); } val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT; Loading @@ -267,6 +375,7 @@ static int pmic_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned function, ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); if (ret < 0) return ret; } val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT; Loading Loading @@ -326,6 +435,9 @@ static int pmic_gpio_config_get(struct pinctrl_dev *pctldev, case PMIC_GPIO_CONF_STRENGTH: arg = pad->strength; break; case PMIC_GPIO_CONF_ATEST: arg = pad->atest; break; default: return -EINVAL; } Loading Loading @@ -400,6 +512,11 @@ static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin, return -EINVAL; pad->strength = arg; break; case PMIC_GPIO_CONF_ATEST: if (arg > PMIC_GPIO_AOUT_ATEST4) return -EINVAL; pad->atest = arg; break; default: return -EINVAL; } Loading @@ -424,19 +541,53 @@ static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin, if (ret < 0) return ret; val = 0; val = PMIC_GPIO_MODE_DIGITAL_INPUT; if (pad->output_enabled) { if (pad->input_enabled) val = 2; val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT; else val = 1; val = PMIC_GPIO_MODE_DIGITAL_OUTPUT; } if (pad->lv_mv_type) { if (pad->function == PMIC_GPIO_FUNC_INDEX_ANALOG) { val = PMIC_GPIO_MODE_ANALOG_PASS_THRU; ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); if (ret < 0) return ret; ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, pad->atest); if (ret < 0) return ret; } else { ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); if (ret < 0) return ret; val = pad->out_value << PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT; val |= pad->function & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val); if (ret < 0) return ret; } } else { val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT; val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT; return pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val); if (ret < 0) return ret; } return ret; } static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev, Loading @@ -444,7 +595,7 @@ static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev, { struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev); struct pmic_gpio_pad *pad; int ret, val; int ret, val, function; static const char *const biases[] = { "pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA", Loading Loading @@ -475,9 +626,21 @@ static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev, ret &= PMIC_MPP_REG_RT_STS_VAL_MASK; pad->out_value = ret; } /* * For GPIO not of LV/MV subtypes, the register value of * the function mapping from "dtest1" to "dtest4" is 2 bits * lower than the function index in pmic_gpio_functions[]. */ if (!pad->lv_mv_type && pad->function >= PMIC_GPIO_FUNC_INDEX_FUNC3) { function = pad->function + (PMIC_GPIO_FUNC_INDEX_DTEST1 - PMIC_GPIO_FUNC_INDEX_FUNC3); } else { function = pad->function; } seq_printf(s, " %-4s", pad->output_enabled ? "out" : "in"); seq_printf(s, " %-7s", pmic_gpio_functions[pad->function]); seq_printf(s, " %-7s", pmic_gpio_functions[function]); seq_printf(s, " vin-%d", pad->power_source); seq_printf(s, " %-27s", biases[pad->pullup]); seq_printf(s, " %-10s", buffer_types[pad->buffer_type]); Loading Loading @@ -622,11 +785,36 @@ static int pmic_gpio_populate(struct pmic_gpio_state *state, case PMIC_GPIO_SUBTYPE_GPIOC_8CH: pad->num_sources = 8; break; case PMIC_GPIO_SUBTYPE_GPIO_LV: pad->num_sources = 1; pad->have_buffer = true; pad->lv_mv_type = true; break; case PMIC_GPIO_SUBTYPE_GPIO_MV: pad->num_sources = 2; pad->have_buffer = true; pad->lv_mv_type = true; break; default: dev_err(state->dev, "unknown GPIO type 0x%x\n", subtype); return -ENODEV; } if (pad->lv_mv_type) { val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL); if (val < 0) return val; pad->out_value = !!(val & PMIC_GPIO_LV_MV_OUTPUT_INVERT); pad->function = val & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK; val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL); if (val < 0) return val; dir = val & PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK; } else { val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL); if (val < 0) return val; Loading @@ -635,27 +823,34 @@ static int pmic_gpio_populate(struct pmic_gpio_state *state, dir = val >> PMIC_GPIO_REG_MODE_DIR_SHIFT; dir &= PMIC_GPIO_REG_MODE_DIR_MASK; pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK; } switch (dir) { case 0: case PMIC_GPIO_MODE_DIGITAL_INPUT: pad->input_enabled = true; pad->output_enabled = false; break; case 1: case PMIC_GPIO_MODE_DIGITAL_OUTPUT: pad->input_enabled = false; pad->output_enabled = true; break; case 2: case PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT: pad->input_enabled = true; pad->output_enabled = true; break; case PMIC_GPIO_MODE_ANALOG_PASS_THRU: if (pad->lv_mv_type) pad->function = PMIC_GPIO_FUNC_INDEX_ANALOG; else return -ENODEV; break; default: dev_err(state->dev, "unknown GPIO direction\n"); return -ENODEV; } pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT; pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK; val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL); if (val < 0) return val; Loading @@ -680,6 +875,13 @@ static int pmic_gpio_populate(struct pmic_gpio_state *state, pad->buffer_type = val >> PMIC_GPIO_REG_OUT_TYPE_SHIFT; pad->buffer_type &= PMIC_GPIO_REG_OUT_TYPE_MASK; if (pad->function == PMIC_GPIO_FUNC_INDEX_ANALOG) { val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL); if (val < 0) return val; pad->atest = val & PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK; } /* Pin could be disabled with PIN_CONFIG_BIAS_HIGH_IMPEDANCE */ pad->is_enabled = true; return 0; Loading @@ -693,18 +895,19 @@ static int pmic_gpio_probe(struct platform_device *pdev) struct pmic_gpio_pad *pad, *pads; struct pmic_gpio_state *state; int ret, npins, i; u32 res[2]; u32 reg; ret = of_property_read_u32_array(dev->of_node, "reg", res, 2); ret = of_property_read_u32(dev->of_node, "reg", ®); if (ret < 0) { dev_err(dev, "missing base address and/or range"); dev_err(dev, "missing base address"); return ret; } npins = res[1] / PMIC_GPIO_ADDRESS_RANGE; npins = platform_irq_count(pdev); if (!npins) return -EINVAL; if (npins < 0) return npins; BUG_ON(npins > ARRAY_SIZE(pmic_gpio_groups)); Loading Loading @@ -752,7 +955,7 @@ static int pmic_gpio_probe(struct platform_device *pdev) if (pad->irq < 0) return pad->irq; pad->base = res[0] + i * PMIC_GPIO_ADDRESS_RANGE; pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE; ret = pmic_gpio_populate(state, pad); if (ret < 0) Loading Loading @@ -805,6 +1008,7 @@ static const struct of_device_id pmic_gpio_of_match[] = { { .compatible = "qcom,pm8916-gpio" }, /* 4 GPIO's */ { .compatible = "qcom,pm8941-gpio" }, /* 36 GPIO's */ { .compatible = "qcom,pma8084-gpio" }, /* 22 GPIO's */ { .compatible = "qcom,spmi-gpio" }, /* Generic */ { }, }; Loading
drivers/pinctrl/qcom/pinctrl-spmi-mpp.c +58 −9 Original line number Diff line number Diff line /* * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. * Copyright (c) 2012-2014, 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 Loading @@ -14,6 +14,7 @@ #include <linux/gpio.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinmux.h> Loading Loading @@ -87,6 +88,10 @@ #define PMIC_MPP_REG_AIN_ROUTE_SHIFT 0 #define PMIC_MPP_REG_AIN_ROUTE_MASK 0x7 /* PMIC_MPP_REG_SINK_CTL */ #define PMIC_MPP_REG_CURRENT_SINK_MASK 0x7 #define MPP_CURRENT_SINK_MA_STEP_SIZE 5 #define PMIC_MPP_MODE_DIGITAL_INPUT 0 #define PMIC_MPP_MODE_DIGITAL_OUTPUT 1 #define PMIC_MPP_MODE_DIGITAL_BIDIR 2 Loading @@ -106,6 +111,7 @@ #define PMIC_MPP_CONF_ANALOG_LEVEL (PIN_CONFIG_END + 2) #define PMIC_MPP_CONF_DTEST_SELECTOR (PIN_CONFIG_END + 3) #define PMIC_MPP_CONF_PAIRED (PIN_CONFIG_END + 4) #define PMIC_MPP_CONF_DTEST_BUFFER (PIN_CONFIG_END + 5) /** * struct pmic_mpp_pad - keep current MPP settings Loading @@ -124,6 +130,7 @@ * @function: See pmic_mpp_functions[]. * @drive_strength: Amount of current in sink mode * @dtest: DTEST route selector * @dtest_buffer: the DTEST buffer selection for digital input mode */ struct pmic_mpp_pad { u16 base; Loading @@ -141,6 +148,7 @@ struct pmic_mpp_pad { unsigned int function; unsigned int drive_strength; unsigned int dtest; unsigned int dtest_buffer; }; struct pmic_mpp_state { Loading @@ -155,6 +163,7 @@ static const struct pinconf_generic_params pmic_mpp_bindings[] = { {"qcom,analog-level", PMIC_MPP_CONF_ANALOG_LEVEL, 0}, {"qcom,dtest", PMIC_MPP_CONF_DTEST_SELECTOR, 0}, {"qcom,paired", PMIC_MPP_CONF_PAIRED, 0}, {"qcom,dtest-buffer", PMIC_MPP_CONF_DTEST_BUFFER, 0}, }; #ifdef CONFIG_DEBUG_FS Loading @@ -163,6 +172,7 @@ static const struct pin_config_item pmic_conf_items[] = { PCONFDUMP(PMIC_MPP_CONF_ANALOG_LEVEL, "analog level", NULL, true), PCONFDUMP(PMIC_MPP_CONF_DTEST_SELECTOR, "dtest", NULL, true), PCONFDUMP(PMIC_MPP_CONF_PAIRED, "paired", NULL, false), PCONFDUMP(PMIC_MPP_CONF_DTEST_BUFFER, "dtest buffer", NULL, true), }; #endif Loading Loading @@ -392,6 +402,9 @@ static int pmic_mpp_config_get(struct pinctrl_dev *pctldev, case PMIC_MPP_CONF_ANALOG_LEVEL: arg = pad->aout_level; break; case PMIC_MPP_CONF_DTEST_BUFFER: arg = pad->dtest_buffer; break; default: return -EINVAL; } Loading Loading @@ -457,7 +470,7 @@ static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin, pad->dtest = arg; break; case PIN_CONFIG_DRIVE_STRENGTH: arg = pad->drive_strength; pad->drive_strength = arg; break; case PMIC_MPP_CONF_AMUX_ROUTE: if (arg >= PMIC_MPP_AMUX_ROUTE_ABUS4) Loading @@ -470,6 +483,15 @@ static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin, case PMIC_MPP_CONF_PAIRED: pad->paired = !!arg; break; case PMIC_MPP_CONF_DTEST_BUFFER: /* * 0xf is the max value which selects * 4 dtest rails simultaneously */ if (arg > 0xf) return -EINVAL; pad->dtest_buffer = arg; break; default: return -EINVAL; } Loading @@ -481,6 +503,11 @@ static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin, if (ret < 0) return ret; val = pad->dtest_buffer; ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_IN_CTL, val); if (ret < 0) return ret; val = pad->pullup << PMIC_MPP_REG_PULL_SHIFT; ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_PULL_CTL, val); Loading @@ -497,6 +524,16 @@ static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin, if (ret < 0) return ret; val = 0; if (pad->drive_strength >= MPP_CURRENT_SINK_MA_STEP_SIZE) val = DIV_ROUND_UP(pad->drive_strength, MPP_CURRENT_SINK_MA_STEP_SIZE) - 1; val &= PMIC_MPP_REG_CURRENT_SINK_MASK; ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_SINK_CTL, val); if (ret < 0) return ret; ret = pmic_mpp_write_mode_ctl(state, pad); if (ret < 0) return ret; Loading Loading @@ -544,6 +581,8 @@ static void pmic_mpp_config_dbg_show(struct pinctrl_dev *pctldev, seq_printf(s, " dtest%d", pad->dtest); if (pad->paired) seq_puts(s, " paired"); if (pad->dtest_buffer) seq_printf(s, " dtest buffer %d", pad->dtest_buffer); } } Loading Loading @@ -741,7 +780,7 @@ static int pmic_mpp_populate(struct pmic_mpp_state *state, sel &= PMIC_MPP_REG_MODE_FUNCTION_MASK; if (sel >= PMIC_MPP_SELECTOR_DTEST_FIRST) pad->dtest = sel + 1; pad->dtest = sel - PMIC_MPP_SELECTOR_DTEST_FIRST + 1; else if (sel == PMIC_MPP_SELECTOR_PAIRED) pad->paired = true; Loading @@ -752,6 +791,12 @@ static int pmic_mpp_populate(struct pmic_mpp_state *state, pad->power_source = val >> PMIC_MPP_REG_VIN_SHIFT; pad->power_source &= PMIC_MPP_REG_VIN_MASK; val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_IN_CTL); if (val < 0) return val; pad->dtest_buffer = val; val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_PULL_CTL); if (val < 0) return val; Loading @@ -770,7 +815,8 @@ static int pmic_mpp_populate(struct pmic_mpp_state *state, if (val < 0) return val; pad->drive_strength = val; val &= PMIC_MPP_REG_CURRENT_SINK_MASK; pad->drive_strength = (val + 1) * MPP_CURRENT_SINK_MA_STEP_SIZE; val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AOUT_CTL); if (val < 0) Loading @@ -795,17 +841,19 @@ static int pmic_mpp_probe(struct platform_device *pdev) struct pmic_mpp_pad *pad, *pads; struct pmic_mpp_state *state; int ret, npins, i; u32 res[2]; u32 reg; ret = of_property_read_u32_array(dev->of_node, "reg", res, 2); ret = of_property_read_u32(dev->of_node, "reg", ®); if (ret < 0) { dev_err(dev, "missing base address and/or range"); dev_err(dev, "missing base address"); return ret; } npins = res[1] / PMIC_MPP_ADDRESS_RANGE; npins = platform_irq_count(pdev); if (!npins) return -EINVAL; if (npins < 0) return npins; BUG_ON(npins > ARRAY_SIZE(pmic_mpp_groups)); Loading Loading @@ -854,7 +902,7 @@ static int pmic_mpp_probe(struct platform_device *pdev) if (pad->irq < 0) return pad->irq; pad->base = res[0] + i * PMIC_MPP_ADDRESS_RANGE; pad->base = reg + i * PMIC_MPP_ADDRESS_RANGE; ret = pmic_mpp_populate(state, pad); if (ret < 0) Loading Loading @@ -908,6 +956,7 @@ static const struct of_device_id pmic_mpp_of_match[] = { { .compatible = "qcom,pm8916-mpp" }, /* 4 MPP's */ { .compatible = "qcom,pm8941-mpp" }, /* 8 MPP's */ { .compatible = "qcom,pma8084-mpp" }, /* 8 MPP's */ { .compatible = "qcom,spmi-mpp" }, /* Generic */ { }, }; Loading