// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2012-2014, 2016-2021 The Linux Foundation. All rights reserved. * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
/* * Output type - indicates pin should be configured as push-pull, * open drain or open source.
*/ #define PMIC_GPIO_OUT_BUF_CMOS 0 #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS 1 #define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS 2
/* The index of each function in pmic_gpio_functions[] array */ enum pmic_gpio_func_index {
PMIC_GPIO_FUNC_INDEX_NORMAL,
PMIC_GPIO_FUNC_INDEX_PAIRED,
PMIC_GPIO_FUNC_INDEX_FUNC1,
PMIC_GPIO_FUNC_INDEX_FUNC2,
PMIC_GPIO_FUNC_INDEX_FUNC3,
PMIC_GPIO_FUNC_INDEX_FUNC4,
PMIC_GPIO_FUNC_INDEX_DTEST1,
PMIC_GPIO_FUNC_INDEX_DTEST2,
PMIC_GPIO_FUNC_INDEX_DTEST3,
PMIC_GPIO_FUNC_INDEX_DTEST4,
};
/** * struct pmic_gpio_pad - keep current GPIO settings * @base: Address base in SPMI device. * @is_enabled: Set to false when GPIO should be put in high Z state. * @out_value: Cached pin output value * @have_buffer: Set to true if GPIO output could be configured in push-pull, * 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. * @analog_pass: Set to true if GPIO is in analog-pass-through mode. * @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 * @dtest_buffer: the DTEST buffer selection for digital input mode.
*/ struct pmic_gpio_pad {
u16 base; bool is_enabled; bool out_value; bool have_buffer; bool output_enabled; bool input_enabled; bool analog_pass; bool lv_mv_type; unsignedint num_sources; unsignedint power_source; unsignedint buffer_type; unsignedint pullup; unsignedint strength; unsignedint function; unsignedint atest; unsignedint dtest_buffer;
};
if (function > PMIC_GPIO_FUNC_INDEX_DTEST4) {
pr_err("function: %d is not defined\n", function); return -EINVAL;
}
pad = pctldev->desc->pins[pin].drv_data; /* * Non-LV/MV subtypes only support 2 special functions, * offsetting the dtestx function values by 2
*/ if (!pad->lv_mv_type) { if (function == PMIC_GPIO_FUNC_INDEX_FUNC3 ||
function == PMIC_GPIO_FUNC_INDEX_FUNC4) {
pr_err("LV/MV subtype doesn't have func3/func4\n"); return -EINVAL;
} if (function >= PMIC_GPIO_FUNC_INDEX_DTEST1)
function -= (PMIC_GPIO_FUNC_INDEX_DTEST1 -
PMIC_GPIO_FUNC_INDEX_FUNC3);
}
pad->function = function;
if (pad->analog_pass)
val = PMIC_GPIO_MODE_ANALOG_PASS_THRU; elseif (pad->output_enabled && pad->input_enabled)
val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT; elseif (pad->output_enabled)
val = PMIC_GPIO_MODE_DIGITAL_OUTPUT; else
val = PMIC_GPIO_MODE_DIGITAL_INPUT;
if (pad->lv_mv_type) {
ret = pmic_gpio_write(state, pad,
PMIC_GPIO_REG_MODE_CTL, val); if (ret < 0) return ret;
val = pad->atest - 1;
ret = pmic_gpio_write(state, pad,
PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, 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;
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;
pad->is_enabled = true; for (i = 0; i < nconfs; i++) {
param = pinconf_to_config_param(configs[i]);
arg = pinconf_to_config_argument(configs[i]);
switch (param) { case PIN_CONFIG_DRIVE_PUSH_PULL:
pad->buffer_type = PMIC_GPIO_OUT_BUF_CMOS; break; case PIN_CONFIG_DRIVE_OPEN_DRAIN: if (!pad->have_buffer) return -EINVAL;
pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS; break; case PIN_CONFIG_DRIVE_OPEN_SOURCE: if (!pad->have_buffer) return -EINVAL;
pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS; break; case PIN_CONFIG_BIAS_DISABLE:
pad->pullup = PMIC_GPIO_PULL_DISABLE; break; case PIN_CONFIG_BIAS_PULL_UP:
pad->pullup = PMIC_GPIO_PULL_UP_30; break; case PIN_CONFIG_BIAS_PULL_DOWN: if (arg)
pad->pullup = PMIC_GPIO_PULL_DOWN; else
pad->pullup = PMIC_GPIO_PULL_DISABLE; break; case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
pad->is_enabled = false; break; case PIN_CONFIG_POWER_SOURCE: if (arg >= pad->num_sources) return -EINVAL;
pad->power_source = arg; break; case PIN_CONFIG_INPUT_ENABLE:
pad->input_enabled = arg ? true : false; break; case PIN_CONFIG_OUTPUT_ENABLE:
pad->output_enabled = arg ? true : false; break; case PIN_CONFIG_OUTPUT:
pad->output_enabled = true;
pad->out_value = arg; break; case PMIC_GPIO_CONF_PULL_UP: if (arg > PMIC_GPIO_PULL_UP_1P5_30) return -EINVAL;
pad->pullup = arg; break; case PMIC_GPIO_CONF_STRENGTH: if (arg > PMIC_GPIO_STRENGTH_LOW) return -EINVAL; switch (arg) { case PMIC_GPIO_STRENGTH_HIGH:
pad->strength = PMIC_GPIO_OUT_STRENGTH_HIGH; break; case PMIC_GPIO_STRENGTH_LOW:
pad->strength = PMIC_GPIO_OUT_STRENGTH_LOW; break; default:
pad->strength = arg; break;
} break; case PMIC_GPIO_CONF_ATEST: if (!pad->lv_mv_type || arg > 4) return -EINVAL;
pad->atest = arg; break; case PMIC_GPIO_CONF_ANALOG_PASS: if (!pad->lv_mv_type) return -EINVAL;
pad->analog_pass = true; break; case PMIC_GPIO_CONF_DTEST_BUFFER: if (arg > 4) return -EINVAL;
pad->dtest_buffer = arg; break; default: return -EINVAL;
}
}
val = pad->power_source << PMIC_GPIO_REG_VIN_SHIFT;
ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL, val); if (ret < 0) return ret;
val = pad->pullup << PMIC_GPIO_REG_PULL_SHIFT;
ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL, val); if (ret < 0) return ret;
val = pad->buffer_type << PMIC_GPIO_REG_OUT_TYPE_SHIFT;
val |= pad->strength << PMIC_GPIO_REG_OUT_STRENGTH_SHIFT;
ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL, val); if (ret < 0) return ret;
if (pad->dtest_buffer == 0) {
val = 0;
} else { if (pad->lv_mv_type) {
val = pad->dtest_buffer - 1;
val |= PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN;
} else {
val = BIT(pad->dtest_buffer - 1);
}
}
ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_IN_CTL, val); if (ret < 0) return ret;
if (pad->analog_pass)
val = PMIC_GPIO_MODE_ANALOG_PASS_THRU; elseif (pad->output_enabled && pad->input_enabled)
val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT; elseif (pad->output_enabled)
val = PMIC_GPIO_MODE_DIGITAL_OUTPUT; else
val = PMIC_GPIO_MODE_DIGITAL_INPUT;
if (pad->lv_mv_type) {
ret = pmic_gpio_write(state, pad,
PMIC_GPIO_REG_MODE_CTL, val); if (ret < 0) return ret;
val = pad->atest - 1;
ret = pmic_gpio_write(state, pad,
PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL, 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;
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;
ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val);
val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_EN_CTL);
if (val < 0 || !(val >> PMIC_GPIO_REG_MASTER_EN_SHIFT)) {
seq_puts(s, " ---");
} else { if (pad->input_enabled) {
ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS); if (ret < 0) return;
ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
pad->out_value = ret;
} /* * For the non-LV/MV subtypes only 2 special functions are * available, offsetting the dtest function values by 2.
*/
function = pad->function; if (!pad->lv_mv_type &&
pad->function >= PMIC_GPIO_FUNC_INDEX_FUNC3)
function += PMIC_GPIO_FUNC_INDEX_DTEST1 -
PMIC_GPIO_FUNC_INDEX_FUNC3;
switch (subtype) { case PMIC_GPIO_SUBTYPE_GPIO_4CH:
pad->have_buffer = true;
fallthrough; case PMIC_GPIO_SUBTYPE_GPIOC_4CH:
pad->num_sources = 4; break; case PMIC_GPIO_SUBTYPE_GPIO_8CH:
pad->have_buffer = true;
fallthrough; 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; case PMIC_GPIO_SUBTYPE_GPIO_LV_VIN2:
pad->num_sources = 2;
pad->have_buffer = true;
pad->lv_mv_type = true; break; case PMIC_GPIO_SUBTYPE_GPIO_MV_VIN3:
pad->num_sources = 3;
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;
pad->out_value = val & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
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 PMIC_GPIO_MODE_DIGITAL_INPUT:
pad->input_enabled = true;
pad->output_enabled = false; break; case PMIC_GPIO_MODE_DIGITAL_OUTPUT:
pad->input_enabled = false;
pad->output_enabled = true; break; 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) return -ENODEV;
pad->analog_pass = true; break; default:
dev_err(state->dev, "unknown GPIO direction\n"); return -ENODEV;
}
val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL); if (val < 0) return val;
pad->power_source = val >> PMIC_GPIO_REG_VIN_SHIFT;
pad->power_source &= PMIC_GPIO_REG_VIN_MASK;
val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL); if (val < 0) return val;
pad->pullup = val >> PMIC_GPIO_REG_PULL_SHIFT;
pad->pullup &= PMIC_GPIO_REG_PULL_MASK;
val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_IN_CTL); if (val < 0) return val;
fwspec->param_count = 4;
fwspec->param[0] = state->usid;
fwspec->param[1] = parent_hwirq; /* param[2] must be left as 0 */
fwspec->param[3] = parent_type;
ret = gpiochip_add_data(&state->chip, state); if (ret) {
dev_err(state->dev, "can't add gpio chip\n"); return ret;
}
/* * For DeviceTree-supported systems, the gpio core checks the * pinctrl's device node for the "gpio-ranges" property. * If it is present, it takes care of adding the pin ranges * for the driver. In this case the driver can skip ahead. * * In order to remain compatible with older, existing DeviceTree * files which don't set the "gpio-ranges" property or systems that * utilize ACPI the driver has to call gpiochip_add_pin_range().
*/ if (!of_property_present(dev->of_node, "gpio-ranges")) {
ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0,
npins); if (ret) {
dev_err(dev, "failed to add pin range\n"); goto err_range;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.