// SPDX-License-Identifier: GPL-2.0
/*
* MediaTek Pinctrl Paris Driver, which implement the vendor per-pin
* bindings for MediaTek SoC.
*
* Copyright (C) 2018 MediaTek Inc.
* Author: Sean Wang <sean.wang@mediatek.com>
* Zhiyong Tao <zhiyong.tao@mediatek.com>
* Hongzhou.Yang <hongzhou.yang@mediatek.com>
*/
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/pinctrl/consumer.h>
#include <dt-bindings/pinctrl/mt65xx.h>
#include "pinctrl-paris.h"
#define PINCTRL_PINCTRL_DEV KBUILD_MODNAME
/* Custom pinconf parameters */
#define MTK_PIN_CONFIG_TDSEL (PIN_CONFIG_END + 1)
#define MTK_PIN_CONFIG_RDSEL (PIN_CONFIG_END + 2)
#define MTK_PIN_CONFIG_PU_ADV (PIN_CONFIG_END + 3)
#define MTK_PIN_CONFIG_PD_ADV (PIN_CONFIG_END + 4)
#define MTK_PIN_CONFIG_DRV_ADV (PIN_CONFIG_END + 5)
static const struct pinconf_generic_params mtk_custom_bindings[] = {
{"mediatek,tdsel", MTK_PIN_CONFIG_TDSEL, 0},
{"mediatek,rdsel", MTK_PIN_CONFIG_RDSEL, 0},
{"mediatek,pull-up-adv", MTK_PIN_CONFIG_PU_ADV, 1},
{"mediatek,pull-down-adv", MTK_PIN_CONFIG_PD_ADV, 1},
{"mediatek,drive-strength-adv", MTK_PIN_CONFIG_DRV_ADV, 2},
};
#ifdef CONFIG_DEBUG_FS
static const struct pin_config_item mtk_conf_items[] = {
PCONFDUMP(MTK_PIN_CONFIG_TDSEL, "tdsel", NULL, true),
PCONFDUMP(MTK_PIN_CONFIG_RDSEL, "rdsel", NULL, true),
PCONFDUMP(MTK_PIN_CONFIG_PU_ADV, "pu-adv", NULL, true),
PCONFDUMP(MTK_PIN_CONFIG_PD_ADV, "pd-adv", NULL, true),
PCONFDUMP(MTK_PIN_CONFIG_DRV_ADV, "drive-strength-adv", NULL, true),
};
#endif
static const char * const mtk_gpio_functions[] = {
"func0", "func1", "func2", "func3",
"func4", "func5", "func6", "func7",
"func8", "func9", "func10", "func11",
"func12", "func13", "func14", "func15",
};
/*
* This section supports converting to/from custom MTK_PIN_CONFIG_DRV_ADV
* and standard PIN_CONFIG_DRIVE_STRENGTH_UA pin configs.
*
* The custom value encodes three hardware bits as follows:
*
* | Bits |
* | 2 (E1) | 1 (E0) | 0 (EN) | drive strength (uA)
* ------------------------------------------------
* | x | x | 0 | disabled, use standard drive strength
* -------------------------------------
* | 0 | 0 | 1 | 125 uA
* | 0 | 1 | 1 | 250 uA
* | 1 | 0 | 1 | 500 uA
* | 1 | 1 | 1 | 1000 uA
*/
static const int mtk_drv_adv_uA[] = { 125, 250, 500, 1000 };
static int mtk_drv_adv_to_uA(int val)
{
/* This should never happen. */
if (WARN_ON_ONCE(val < 0 || val > 7))
return -EINVAL;
/* Bit 0 simply enables this hardware part */
if (!(val & BIT(0)))
return -EINVAL;
return mtk_drv_adv_uA[(val >> 1)];
}
static int mtk_drv_uA_to_adv(int val)
{
switch (val) {
case 125:
return 0x1;
case 250:
return 0x3;
case 500:
return 0x5;
case 1000:
return 0x7;
}
return -EINVAL;
}
static int mtk_pinmux_gpio_request_enable(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned int pin)
{
struct mtk_pinctrl *hw = pinctrl_dev_get_drvdata(pctldev);
const struct mtk_pin_desc *desc;
desc = (const struct mtk_pin_desc *)&hw->soc->pins[pin];
return mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_MODE,
hw->soc->gpio_m);
}
static int mtk_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned int pin, bool input)
{
struct mtk_pinctrl *hw = pinctrl_dev_get_drvdata(pctldev);
const struct mtk_pin_desc *desc;
desc = (const struct mtk_pin_desc *)&hw->soc->pins[pin];
/* hardware would take 0 as input direction */
return mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_DIR, !input);
}
static int mtk_pinconf_get(struct pinctrl_dev *pctldev,
unsigned int pin, unsigned long *config)
{
struct mtk_pinctrl *hw = pinctrl_dev_get_drvdata(pctldev);
u32 param = pinconf_to_config_param(*config);
int pullup, reg, err = -ENOTSUPP, ret = 1;
const struct mtk_pin_desc *desc;
if (pin >= hw->soc->npins)
return -EINVAL;
desc = (const struct mtk_pin_desc *)&hw->soc->pins[pin];
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
case PIN_CONFIG_BIAS_PULL_UP:
case PIN_CONFIG_BIAS_PULL_DOWN:
if (!hw->soc->bias_get_combo)
break;
err = hw->soc->bias_get_combo(hw, desc, &pullup, &ret);
if (err)
break;
if (ret == MTK_PUPD_SET_R1R0_00)
ret = MTK_DISABLE