mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-27 12:25:08 +07:00
1802d0beec
Based on 1 normalized pattern(s): this program is free software you can redistribute it and or modify it under the terms of the gnu general public license 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 extracted by the scancode license scanner the SPDX license identifier GPL-2.0-only has been chosen to replace the boilerplate/reference in 655 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org> Reviewed-by: Richard Fontana <rfontana@redhat.com> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190527070034.575739538@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1121 lines
28 KiB
C
1121 lines
28 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* mt65xx pinctrl driver based on Allwinner A1X pinctrl driver.
|
|
* Copyright (c) 2014 MediaTek Inc.
|
|
* Author: Hongzhou.Yang <hongzhou.yang@mediatek.com>
|
|
*/
|
|
|
|
#include <linux/io.h>
|
|
#include <linux/gpio/driver.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/pinctrl/consumer.h>
|
|
#include <linux/pinctrl/machine.h>
|
|
#include <linux/pinctrl/pinconf.h>
|
|
#include <linux/pinctrl/pinconf-generic.h>
|
|
#include <linux/pinctrl/pinctrl.h>
|
|
#include <linux/pinctrl/pinmux.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/pm.h>
|
|
#include <dt-bindings/pinctrl/mt65xx.h>
|
|
|
|
#include "../core.h"
|
|
#include "../pinconf.h"
|
|
#include "../pinctrl-utils.h"
|
|
#include "mtk-eint.h"
|
|
#include "pinctrl-mtk-common.h"
|
|
|
|
#define MAX_GPIO_MODE_PER_REG 5
|
|
#define GPIO_MODE_BITS 3
|
|
#define GPIO_MODE_PREFIX "GPIO"
|
|
|
|
static const char * const mtk_gpio_functions[] = {
|
|
"func0", "func1", "func2", "func3",
|
|
"func4", "func5", "func6", "func7",
|
|
"func8", "func9", "func10", "func11",
|
|
"func12", "func13", "func14", "func15",
|
|
};
|
|
|
|
/*
|
|
* There are two base address for pull related configuration
|
|
* in mt8135, and different GPIO pins use different base address.
|
|
* When pin number greater than type1_start and less than type1_end,
|
|
* should use the second base address.
|
|
*/
|
|
static struct regmap *mtk_get_regmap(struct mtk_pinctrl *pctl,
|
|
unsigned long pin)
|
|
{
|
|
if (pin >= pctl->devdata->type1_start && pin < pctl->devdata->type1_end)
|
|
return pctl->regmap2;
|
|
return pctl->regmap1;
|
|
}
|
|
|
|
static unsigned int mtk_get_port(struct mtk_pinctrl *pctl, unsigned long pin)
|
|
{
|
|
/* Different SoC has different mask and port shift. */
|
|
return ((pin >> 4) & pctl->devdata->port_mask)
|
|
<< pctl->devdata->port_shf;
|
|
}
|
|
|
|
static int mtk_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
|
|
struct pinctrl_gpio_range *range, unsigned offset,
|
|
bool input)
|
|
{
|
|
unsigned int reg_addr;
|
|
unsigned int bit;
|
|
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
reg_addr = mtk_get_port(pctl, offset) + pctl->devdata->dir_offset;
|
|
bit = BIT(offset & 0xf);
|
|
|
|
if (pctl->devdata->spec_dir_set)
|
|
pctl->devdata->spec_dir_set(®_addr, offset);
|
|
|
|
if (input)
|
|
/* Different SoC has different alignment offset. */
|
|
reg_addr = CLR_ADDR(reg_addr, pctl);
|
|
else
|
|
reg_addr = SET_ADDR(reg_addr, pctl);
|
|
|
|
regmap_write(mtk_get_regmap(pctl, offset), reg_addr, bit);
|
|
return 0;
|
|
}
|
|
|
|
static void mtk_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
|
{
|
|
unsigned int reg_addr;
|
|
unsigned int bit;
|
|
struct mtk_pinctrl *pctl = gpiochip_get_data(chip);
|
|
|
|
reg_addr = mtk_get_port(pctl, offset) + pctl->devdata->dout_offset;
|
|
bit = BIT(offset & 0xf);
|
|
|
|
if (value)
|
|
reg_addr = SET_ADDR(reg_addr, pctl);
|
|
else
|
|
reg_addr = CLR_ADDR(reg_addr, pctl);
|
|
|
|
regmap_write(mtk_get_regmap(pctl, offset), reg_addr, bit);
|
|
}
|
|
|
|
static int mtk_pconf_set_ies_smt(struct mtk_pinctrl *pctl, unsigned pin,
|
|
int value, enum pin_config_param arg)
|
|
{
|
|
unsigned int reg_addr, offset;
|
|
unsigned int bit;
|
|
|
|
/**
|
|
* Due to some soc are not support ies/smt config, add this special
|
|
* control to handle it.
|
|
*/
|
|
if (!pctl->devdata->spec_ies_smt_set &&
|
|
pctl->devdata->ies_offset == MTK_PINCTRL_NOT_SUPPORT &&
|
|
arg == PIN_CONFIG_INPUT_ENABLE)
|
|
return -EINVAL;
|
|
|
|
if (!pctl->devdata->spec_ies_smt_set &&
|
|
pctl->devdata->smt_offset == MTK_PINCTRL_NOT_SUPPORT &&
|
|
arg == PIN_CONFIG_INPUT_SCHMITT_ENABLE)
|
|
return -EINVAL;
|
|
|
|
/*
|
|
* Due to some pins are irregular, their input enable and smt
|
|
* control register are discontinuous, so we need this special handle.
|
|
*/
|
|
if (pctl->devdata->spec_ies_smt_set) {
|
|
return pctl->devdata->spec_ies_smt_set(mtk_get_regmap(pctl, pin),
|
|
pin, pctl->devdata->port_align, value, arg);
|
|
}
|
|
|
|
bit = BIT(pin & 0xf);
|
|
|
|
if (arg == PIN_CONFIG_INPUT_ENABLE)
|
|
offset = pctl->devdata->ies_offset;
|
|
else
|
|
offset = pctl->devdata->smt_offset;
|
|
|
|
if (value)
|
|
reg_addr = SET_ADDR(mtk_get_port(pctl, pin) + offset, pctl);
|
|
else
|
|
reg_addr = CLR_ADDR(mtk_get_port(pctl, pin) + offset, pctl);
|
|
|
|
regmap_write(mtk_get_regmap(pctl, pin), reg_addr, bit);
|
|
return 0;
|
|
}
|
|
|
|
int mtk_pconf_spec_set_ies_smt_range(struct regmap *regmap,
|
|
const struct mtk_pin_ies_smt_set *ies_smt_infos, unsigned int info_num,
|
|
unsigned int pin, unsigned char align, int value)
|
|
{
|
|
unsigned int i, reg_addr, bit;
|
|
|
|
for (i = 0; i < info_num; i++) {
|
|
if (pin >= ies_smt_infos[i].start &&
|
|
pin <= ies_smt_infos[i].end) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == info_num)
|
|
return -EINVAL;
|
|
|
|
if (value)
|
|
reg_addr = ies_smt_infos[i].offset + align;
|
|
else
|
|
reg_addr = ies_smt_infos[i].offset + (align << 1);
|
|
|
|
bit = BIT(ies_smt_infos[i].bit);
|
|
regmap_write(regmap, reg_addr, bit);
|
|
return 0;
|
|
}
|
|
|
|
static const struct mtk_pin_drv_grp *mtk_find_pin_drv_grp_by_pin(
|
|
struct mtk_pinctrl *pctl, unsigned long pin) {
|
|
int i;
|
|
|
|
for (i = 0; i < pctl->devdata->n_pin_drv_grps; i++) {
|
|
const struct mtk_pin_drv_grp *pin_drv =
|
|
pctl->devdata->pin_drv_grp + i;
|
|
if (pin == pin_drv->pin)
|
|
return pin_drv;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int mtk_pconf_set_driving(struct mtk_pinctrl *pctl,
|
|
unsigned int pin, unsigned char driving)
|
|
{
|
|
const struct mtk_pin_drv_grp *pin_drv;
|
|
unsigned int val;
|
|
unsigned int bits, mask, shift;
|
|
const struct mtk_drv_group_desc *drv_grp;
|
|
|
|
if (pin >= pctl->devdata->npins)
|
|
return -EINVAL;
|
|
|
|
pin_drv = mtk_find_pin_drv_grp_by_pin(pctl, pin);
|
|
if (!pin_drv || pin_drv->grp > pctl->devdata->n_grp_cls)
|
|
return -EINVAL;
|
|
|
|
drv_grp = pctl->devdata->grp_desc + pin_drv->grp;
|
|
if (driving >= drv_grp->min_drv && driving <= drv_grp->max_drv
|
|
&& !(driving % drv_grp->step)) {
|
|
val = driving / drv_grp->step - 1;
|
|
bits = drv_grp->high_bit - drv_grp->low_bit + 1;
|
|
mask = BIT(bits) - 1;
|
|
shift = pin_drv->bit + drv_grp->low_bit;
|
|
mask <<= shift;
|
|
val <<= shift;
|
|
return regmap_update_bits(mtk_get_regmap(pctl, pin),
|
|
pin_drv->offset, mask, val);
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
int mtk_pctrl_spec_pull_set_samereg(struct regmap *regmap,
|
|
const struct mtk_pin_spec_pupd_set_samereg *pupd_infos,
|
|
unsigned int info_num, unsigned int pin,
|
|
unsigned char align, bool isup, unsigned int r1r0)
|
|
{
|
|
unsigned int i;
|
|
unsigned int reg_pupd, reg_set, reg_rst;
|
|
unsigned int bit_pupd, bit_r0, bit_r1;
|
|
const struct mtk_pin_spec_pupd_set_samereg *spec_pupd_pin;
|
|
bool find = false;
|
|
|
|
for (i = 0; i < info_num; i++) {
|
|
if (pin == pupd_infos[i].pin) {
|
|
find = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!find)
|
|
return -EINVAL;
|
|
|
|
spec_pupd_pin = pupd_infos + i;
|
|
reg_set = spec_pupd_pin->offset + align;
|
|
reg_rst = spec_pupd_pin->offset + (align << 1);
|
|
|
|
if (isup)
|
|
reg_pupd = reg_rst;
|
|
else
|
|
reg_pupd = reg_set;
|
|
|
|
bit_pupd = BIT(spec_pupd_pin->pupd_bit);
|
|
regmap_write(regmap, reg_pupd, bit_pupd);
|
|
|
|
bit_r0 = BIT(spec_pupd_pin->r0_bit);
|
|
bit_r1 = BIT(spec_pupd_pin->r1_bit);
|
|
|
|
switch (r1r0) {
|
|
case MTK_PUPD_SET_R1R0_00:
|
|
regmap_write(regmap, reg_rst, bit_r0);
|
|
regmap_write(regmap, reg_rst, bit_r1);
|
|
break;
|
|
case MTK_PUPD_SET_R1R0_01:
|
|
regmap_write(regmap, reg_set, bit_r0);
|
|
regmap_write(regmap, reg_rst, bit_r1);
|
|
break;
|
|
case MTK_PUPD_SET_R1R0_10:
|
|
regmap_write(regmap, reg_rst, bit_r0);
|
|
regmap_write(regmap, reg_set, bit_r1);
|
|
break;
|
|
case MTK_PUPD_SET_R1R0_11:
|
|
regmap_write(regmap, reg_set, bit_r0);
|
|
regmap_write(regmap, reg_set, bit_r1);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_pconf_set_pull_select(struct mtk_pinctrl *pctl,
|
|
unsigned int pin, bool enable, bool isup, unsigned int arg)
|
|
{
|
|
unsigned int bit;
|
|
unsigned int reg_pullen, reg_pullsel, r1r0;
|
|
int ret;
|
|
|
|
/* Some pins' pull setting are very different,
|
|
* they have separate pull up/down bit, R0 and R1
|
|
* resistor bit, so we need this special handle.
|
|
*/
|
|
if (pctl->devdata->spec_pull_set) {
|
|
/* For special pins, bias-disable is set by R1R0,
|
|
* the parameter should be "MTK_PUPD_SET_R1R0_00".
|
|
*/
|
|
r1r0 = enable ? arg : MTK_PUPD_SET_R1R0_00;
|
|
ret = pctl->devdata->spec_pull_set(mtk_get_regmap(pctl, pin),
|
|
pin, pctl->devdata->port_align, isup, r1r0);
|
|
if (!ret)
|
|
return 0;
|
|
}
|
|
|
|
/* For generic pull config, default arg value should be 0 or 1. */
|
|
if (arg != 0 && arg != 1) {
|
|
dev_err(pctl->dev, "invalid pull-up argument %d on pin %d .\n",
|
|
arg, pin);
|
|
return -EINVAL;
|
|
}
|
|
|
|
bit = BIT(pin & 0xf);
|
|
if (enable)
|
|
reg_pullen = SET_ADDR(mtk_get_port(pctl, pin) +
|
|
pctl->devdata->pullen_offset, pctl);
|
|
else
|
|
reg_pullen = CLR_ADDR(mtk_get_port(pctl, pin) +
|
|
pctl->devdata->pullen_offset, pctl);
|
|
|
|
if (isup)
|
|
reg_pullsel = SET_ADDR(mtk_get_port(pctl, pin) +
|
|
pctl->devdata->pullsel_offset, pctl);
|
|
else
|
|
reg_pullsel = CLR_ADDR(mtk_get_port(pctl, pin) +
|
|
pctl->devdata->pullsel_offset, pctl);
|
|
|
|
regmap_write(mtk_get_regmap(pctl, pin), reg_pullen, bit);
|
|
regmap_write(mtk_get_regmap(pctl, pin), reg_pullsel, bit);
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_pconf_parse_conf(struct pinctrl_dev *pctldev,
|
|
unsigned int pin, enum pin_config_param param,
|
|
enum pin_config_param arg)
|
|
{
|
|
int ret = 0;
|
|
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
switch (param) {
|
|
case PIN_CONFIG_BIAS_DISABLE:
|
|
ret = mtk_pconf_set_pull_select(pctl, pin, false, false, arg);
|
|
break;
|
|
case PIN_CONFIG_BIAS_PULL_UP:
|
|
ret = mtk_pconf_set_pull_select(pctl, pin, true, true, arg);
|
|
break;
|
|
case PIN_CONFIG_BIAS_PULL_DOWN:
|
|
ret = mtk_pconf_set_pull_select(pctl, pin, true, false, arg);
|
|
break;
|
|
case PIN_CONFIG_INPUT_ENABLE:
|
|
mtk_pmx_gpio_set_direction(pctldev, NULL, pin, true);
|
|
ret = mtk_pconf_set_ies_smt(pctl, pin, arg, param);
|
|
break;
|
|
case PIN_CONFIG_OUTPUT:
|
|
mtk_gpio_set(pctl->chip, pin, arg);
|
|
ret = mtk_pmx_gpio_set_direction(pctldev, NULL, pin, false);
|
|
break;
|
|
case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
|
|
mtk_pmx_gpio_set_direction(pctldev, NULL, pin, true);
|
|
ret = mtk_pconf_set_ies_smt(pctl, pin, arg, param);
|
|
break;
|
|
case PIN_CONFIG_DRIVE_STRENGTH:
|
|
ret = mtk_pconf_set_driving(pctl, pin, arg);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mtk_pconf_group_get(struct pinctrl_dev *pctldev,
|
|
unsigned group,
|
|
unsigned long *config)
|
|
{
|
|
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
*config = pctl->groups[group].config;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_pconf_group_set(struct pinctrl_dev *pctldev, unsigned group,
|
|
unsigned long *configs, unsigned num_configs)
|
|
{
|
|
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
|
struct mtk_pinctrl_group *g = &pctl->groups[group];
|
|
int i, ret;
|
|
|
|
for (i = 0; i < num_configs; i++) {
|
|
ret = mtk_pconf_parse_conf(pctldev, g->pin,
|
|
pinconf_to_config_param(configs[i]),
|
|
pinconf_to_config_argument(configs[i]));
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
g->config = configs[i];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct pinconf_ops mtk_pconf_ops = {
|
|
.pin_config_group_get = mtk_pconf_group_get,
|
|
.pin_config_group_set = mtk_pconf_group_set,
|
|
};
|
|
|
|
static struct mtk_pinctrl_group *
|
|
mtk_pctrl_find_group_by_pin(struct mtk_pinctrl *pctl, u32 pin)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < pctl->ngroups; i++) {
|
|
struct mtk_pinctrl_group *grp = pctl->groups + i;
|
|
|
|
if (grp->pin == pin)
|
|
return grp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const struct mtk_desc_function *mtk_pctrl_find_function_by_pin(
|
|
struct mtk_pinctrl *pctl, u32 pin_num, u32 fnum)
|
|
{
|
|
const struct mtk_desc_pin *pin = pctl->devdata->pins + pin_num;
|
|
const struct mtk_desc_function *func = pin->functions;
|
|
|
|
while (func && func->name) {
|
|
if (func->muxval == fnum)
|
|
return func;
|
|
func++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static bool mtk_pctrl_is_function_valid(struct mtk_pinctrl *pctl,
|
|
u32 pin_num, u32 fnum)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < pctl->devdata->npins; i++) {
|
|
const struct mtk_desc_pin *pin = pctl->devdata->pins + i;
|
|
|
|
if (pin->pin.number == pin_num) {
|
|
const struct mtk_desc_function *func =
|
|
pin->functions;
|
|
|
|
while (func && func->name) {
|
|
if (func->muxval == fnum)
|
|
return true;
|
|
func++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int mtk_pctrl_dt_node_to_map_func(struct mtk_pinctrl *pctl,
|
|
u32 pin, u32 fnum, struct mtk_pinctrl_group *grp,
|
|
struct pinctrl_map **map, unsigned *reserved_maps,
|
|
unsigned *num_maps)
|
|
{
|
|
bool ret;
|
|
|
|
if (*num_maps == *reserved_maps)
|
|
return -ENOSPC;
|
|
|
|
(*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP;
|
|
(*map)[*num_maps].data.mux.group = grp->name;
|
|
|
|
ret = mtk_pctrl_is_function_valid(pctl, pin, fnum);
|
|
if (!ret) {
|
|
dev_err(pctl->dev, "invalid function %d on pin %d .\n",
|
|
fnum, pin);
|
|
return -EINVAL;
|
|
}
|
|
|
|
(*map)[*num_maps].data.mux.function = mtk_gpio_functions[fnum];
|
|
(*num_maps)++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
|
|
struct device_node *node,
|
|
struct pinctrl_map **map,
|
|
unsigned *reserved_maps,
|
|
unsigned *num_maps)
|
|
{
|
|
struct property *pins;
|
|
u32 pinfunc, pin, func;
|
|
int num_pins, num_funcs, maps_per_pin;
|
|
unsigned long *configs;
|
|
unsigned int num_configs;
|
|
bool has_config = false;
|
|
int i, err;
|
|
unsigned reserve = 0;
|
|
struct mtk_pinctrl_group *grp;
|
|
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
pins = of_find_property(node, "pinmux", NULL);
|
|
if (!pins) {
|
|
dev_err(pctl->dev, "missing pins property in node %pOFn .\n",
|
|
node);
|
|
return -EINVAL;
|
|
}
|
|
|
|
err = pinconf_generic_parse_dt_config(node, pctldev, &configs,
|
|
&num_configs);
|
|
if (err)
|
|
return err;
|
|
|
|
if (num_configs)
|
|
has_config = true;
|
|
|
|
num_pins = pins->length / sizeof(u32);
|
|
num_funcs = num_pins;
|
|
maps_per_pin = 0;
|
|
if (num_funcs)
|
|
maps_per_pin++;
|
|
if (has_config && num_pins >= 1)
|
|
maps_per_pin++;
|
|
|
|
if (!num_pins || !maps_per_pin) {
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
reserve = num_pins * maps_per_pin;
|
|
|
|
err = pinctrl_utils_reserve_map(pctldev, map,
|
|
reserved_maps, num_maps, reserve);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
for (i = 0; i < num_pins; i++) {
|
|
err = of_property_read_u32_index(node, "pinmux",
|
|
i, &pinfunc);
|
|
if (err)
|
|
goto exit;
|
|
|
|
pin = MTK_GET_PIN_NO(pinfunc);
|
|
func = MTK_GET_PIN_FUNC(pinfunc);
|
|
|
|
if (pin >= pctl->devdata->npins ||
|
|
func >= ARRAY_SIZE(mtk_gpio_functions)) {
|
|
dev_err(pctl->dev, "invalid pins value.\n");
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
grp = mtk_pctrl_find_group_by_pin(pctl, pin);
|
|
if (!grp) {
|
|
dev_err(pctl->dev, "unable to match pin %d to group\n",
|
|
pin);
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
err = mtk_pctrl_dt_node_to_map_func(pctl, pin, func, grp, map,
|
|
reserved_maps, num_maps);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
if (has_config) {
|
|
err = pinctrl_utils_add_map_configs(pctldev, map,
|
|
reserved_maps, num_maps, grp->name,
|
|
configs, num_configs,
|
|
PIN_MAP_TYPE_CONFIGS_GROUP);
|
|
if (err < 0)
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
err = 0;
|
|
|
|
exit:
|
|
kfree(configs);
|
|
return err;
|
|
}
|
|
|
|
static int mtk_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
|
|
struct device_node *np_config,
|
|
struct pinctrl_map **map, unsigned *num_maps)
|
|
{
|
|
struct device_node *np;
|
|
unsigned reserved_maps;
|
|
int ret;
|
|
|
|
*map = NULL;
|
|
*num_maps = 0;
|
|
reserved_maps = 0;
|
|
|
|
for_each_child_of_node(np_config, np) {
|
|
ret = mtk_pctrl_dt_subnode_to_map(pctldev, np, map,
|
|
&reserved_maps, num_maps);
|
|
if (ret < 0) {
|
|
pinctrl_utils_free_map(pctldev, *map, *num_maps);
|
|
of_node_put(np);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_pctrl_get_groups_count(struct pinctrl_dev *pctldev)
|
|
{
|
|
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
return pctl->ngroups;
|
|
}
|
|
|
|
static const char *mtk_pctrl_get_group_name(struct pinctrl_dev *pctldev,
|
|
unsigned group)
|
|
{
|
|
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
return pctl->groups[group].name;
|
|
}
|
|
|
|
static int mtk_pctrl_get_group_pins(struct pinctrl_dev *pctldev,
|
|
unsigned group,
|
|
const unsigned **pins,
|
|
unsigned *num_pins)
|
|
{
|
|
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
*pins = (unsigned *)&pctl->groups[group].pin;
|
|
*num_pins = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct pinctrl_ops mtk_pctrl_ops = {
|
|
.dt_node_to_map = mtk_pctrl_dt_node_to_map,
|
|
.dt_free_map = pinctrl_utils_free_map,
|
|
.get_groups_count = mtk_pctrl_get_groups_count,
|
|
.get_group_name = mtk_pctrl_get_group_name,
|
|
.get_group_pins = mtk_pctrl_get_group_pins,
|
|
};
|
|
|
|
static int mtk_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev)
|
|
{
|
|
return ARRAY_SIZE(mtk_gpio_functions);
|
|
}
|
|
|
|
static const char *mtk_pmx_get_func_name(struct pinctrl_dev *pctldev,
|
|
unsigned selector)
|
|
{
|
|
return mtk_gpio_functions[selector];
|
|
}
|
|
|
|
static int mtk_pmx_get_func_groups(struct pinctrl_dev *pctldev,
|
|
unsigned function,
|
|
const char * const **groups,
|
|
unsigned * const num_groups)
|
|
{
|
|
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
*groups = pctl->grp_names;
|
|
*num_groups = pctl->ngroups;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_pmx_set_mode(struct pinctrl_dev *pctldev,
|
|
unsigned long pin, unsigned long mode)
|
|
{
|
|
unsigned int reg_addr;
|
|
unsigned char bit;
|
|
unsigned int val;
|
|
unsigned int mask = (1L << GPIO_MODE_BITS) - 1;
|
|
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
if (pctl->devdata->spec_pinmux_set)
|
|
pctl->devdata->spec_pinmux_set(mtk_get_regmap(pctl, pin),
|
|
pin, mode);
|
|
|
|
reg_addr = ((pin / MAX_GPIO_MODE_PER_REG) << pctl->devdata->port_shf)
|
|
+ pctl->devdata->pinmux_offset;
|
|
|
|
mode &= mask;
|
|
bit = pin % MAX_GPIO_MODE_PER_REG;
|
|
mask <<= (GPIO_MODE_BITS * bit);
|
|
val = (mode << (GPIO_MODE_BITS * bit));
|
|
return regmap_update_bits(mtk_get_regmap(pctl, pin),
|
|
reg_addr, mask, val);
|
|
}
|
|
|
|
static const struct mtk_desc_pin *
|
|
mtk_find_pin_by_eint_num(struct mtk_pinctrl *pctl, unsigned int eint_num)
|
|
{
|
|
int i;
|
|
const struct mtk_desc_pin *pin;
|
|
|
|
for (i = 0; i < pctl->devdata->npins; i++) {
|
|
pin = pctl->devdata->pins + i;
|
|
if (pin->eint.eintnum == eint_num)
|
|
return pin;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int mtk_pmx_set_mux(struct pinctrl_dev *pctldev,
|
|
unsigned function,
|
|
unsigned group)
|
|
{
|
|
bool ret;
|
|
const struct mtk_desc_function *desc;
|
|
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
|
struct mtk_pinctrl_group *g = pctl->groups + group;
|
|
|
|
ret = mtk_pctrl_is_function_valid(pctl, g->pin, function);
|
|
if (!ret) {
|
|
dev_err(pctl->dev, "invalid function %d on group %d .\n",
|
|
function, group);
|
|
return -EINVAL;
|
|
}
|
|
|
|
desc = mtk_pctrl_find_function_by_pin(pctl, g->pin, function);
|
|
if (!desc)
|
|
return -EINVAL;
|
|
mtk_pmx_set_mode(pctldev, g->pin, desc->muxval);
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_pmx_find_gpio_mode(struct mtk_pinctrl *pctl,
|
|
unsigned offset)
|
|
{
|
|
const struct mtk_desc_pin *pin = pctl->devdata->pins + offset;
|
|
const struct mtk_desc_function *func = pin->functions;
|
|
|
|
while (func && func->name) {
|
|
if (!strncmp(func->name, GPIO_MODE_PREFIX,
|
|
sizeof(GPIO_MODE_PREFIX)-1))
|
|
return func->muxval;
|
|
func++;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int mtk_pmx_gpio_request_enable(struct pinctrl_dev *pctldev,
|
|
struct pinctrl_gpio_range *range,
|
|
unsigned offset)
|
|
{
|
|
int muxval;
|
|
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
muxval = mtk_pmx_find_gpio_mode(pctl, offset);
|
|
|
|
if (muxval < 0) {
|
|
dev_err(pctl->dev, "invalid gpio pin %d.\n", offset);
|
|
return -EINVAL;
|
|
}
|
|
|
|
mtk_pmx_set_mode(pctldev, offset, muxval);
|
|
mtk_pconf_set_ies_smt(pctl, offset, 1, PIN_CONFIG_INPUT_ENABLE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct pinmux_ops mtk_pmx_ops = {
|
|
.get_functions_count = mtk_pmx_get_funcs_cnt,
|
|
.get_function_name = mtk_pmx_get_func_name,
|
|
.get_function_groups = mtk_pmx_get_func_groups,
|
|
.set_mux = mtk_pmx_set_mux,
|
|
.gpio_set_direction = mtk_pmx_gpio_set_direction,
|
|
.gpio_request_enable = mtk_pmx_gpio_request_enable,
|
|
};
|
|
|
|
static int mtk_gpio_direction_input(struct gpio_chip *chip,
|
|
unsigned offset)
|
|
{
|
|
return pinctrl_gpio_direction_input(chip->base + offset);
|
|
}
|
|
|
|
static int mtk_gpio_direction_output(struct gpio_chip *chip,
|
|
unsigned offset, int value)
|
|
{
|
|
mtk_gpio_set(chip, offset, value);
|
|
return pinctrl_gpio_direction_output(chip->base + offset);
|
|
}
|
|
|
|
static int mtk_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
|
|
{
|
|
unsigned int reg_addr;
|
|
unsigned int bit;
|
|
unsigned int read_val = 0;
|
|
|
|
struct mtk_pinctrl *pctl = gpiochip_get_data(chip);
|
|
|
|
reg_addr = mtk_get_port(pctl, offset) + pctl->devdata->dir_offset;
|
|
bit = BIT(offset & 0xf);
|
|
|
|
if (pctl->devdata->spec_dir_set)
|
|
pctl->devdata->spec_dir_set(®_addr, offset);
|
|
|
|
regmap_read(pctl->regmap1, reg_addr, &read_val);
|
|
return !(read_val & bit);
|
|
}
|
|
|
|
static int mtk_gpio_get(struct gpio_chip *chip, unsigned offset)
|
|
{
|
|
unsigned int reg_addr;
|
|
unsigned int bit;
|
|
unsigned int read_val = 0;
|
|
struct mtk_pinctrl *pctl = gpiochip_get_data(chip);
|
|
|
|
reg_addr = mtk_get_port(pctl, offset) +
|
|
pctl->devdata->din_offset;
|
|
|
|
bit = BIT(offset & 0xf);
|
|
regmap_read(pctl->regmap1, reg_addr, &read_val);
|
|
return !!(read_val & bit);
|
|
}
|
|
|
|
static int mtk_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
|
{
|
|
struct mtk_pinctrl *pctl = gpiochip_get_data(chip);
|
|
const struct mtk_desc_pin *pin;
|
|
unsigned long eint_n;
|
|
|
|
pin = pctl->devdata->pins + offset;
|
|
if (pin->eint.eintnum == NO_EINT_SUPPORT)
|
|
return -EINVAL;
|
|
|
|
eint_n = pin->eint.eintnum;
|
|
|
|
return mtk_eint_find_irq(pctl->eint, eint_n);
|
|
}
|
|
|
|
static int mtk_gpio_set_config(struct gpio_chip *chip, unsigned offset,
|
|
unsigned long config)
|
|
{
|
|
struct mtk_pinctrl *pctl = gpiochip_get_data(chip);
|
|
const struct mtk_desc_pin *pin;
|
|
unsigned long eint_n;
|
|
u32 debounce;
|
|
|
|
if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE)
|
|
return -ENOTSUPP;
|
|
|
|
pin = pctl->devdata->pins + offset;
|
|
if (pin->eint.eintnum == NO_EINT_SUPPORT)
|
|
return -EINVAL;
|
|
|
|
debounce = pinconf_to_config_argument(config);
|
|
eint_n = pin->eint.eintnum;
|
|
|
|
return mtk_eint_set_debounce(pctl->eint, eint_n, debounce);
|
|
}
|
|
|
|
static const struct gpio_chip mtk_gpio_chip = {
|
|
.owner = THIS_MODULE,
|
|
.request = gpiochip_generic_request,
|
|
.free = gpiochip_generic_free,
|
|
.get_direction = mtk_gpio_get_direction,
|
|
.direction_input = mtk_gpio_direction_input,
|
|
.direction_output = mtk_gpio_direction_output,
|
|
.get = mtk_gpio_get,
|
|
.set = mtk_gpio_set,
|
|
.to_irq = mtk_gpio_to_irq,
|
|
.set_config = mtk_gpio_set_config,
|
|
.of_gpio_n_cells = 2,
|
|
};
|
|
|
|
static int mtk_eint_suspend(struct device *device)
|
|
{
|
|
struct mtk_pinctrl *pctl = dev_get_drvdata(device);
|
|
|
|
return mtk_eint_do_suspend(pctl->eint);
|
|
}
|
|
|
|
static int mtk_eint_resume(struct device *device)
|
|
{
|
|
struct mtk_pinctrl *pctl = dev_get_drvdata(device);
|
|
|
|
return mtk_eint_do_resume(pctl->eint);
|
|
}
|
|
|
|
const struct dev_pm_ops mtk_eint_pm_ops = {
|
|
.suspend_noirq = mtk_eint_suspend,
|
|
.resume_noirq = mtk_eint_resume,
|
|
};
|
|
|
|
static int mtk_pctrl_build_state(struct platform_device *pdev)
|
|
{
|
|
struct mtk_pinctrl *pctl = platform_get_drvdata(pdev);
|
|
int i;
|
|
|
|
pctl->ngroups = pctl->devdata->npins;
|
|
|
|
/* Allocate groups */
|
|
pctl->groups = devm_kcalloc(&pdev->dev, pctl->ngroups,
|
|
sizeof(*pctl->groups), GFP_KERNEL);
|
|
if (!pctl->groups)
|
|
return -ENOMEM;
|
|
|
|
/* We assume that one pin is one group, use pin name as group name. */
|
|
pctl->grp_names = devm_kcalloc(&pdev->dev, pctl->ngroups,
|
|
sizeof(*pctl->grp_names), GFP_KERNEL);
|
|
if (!pctl->grp_names)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < pctl->devdata->npins; i++) {
|
|
const struct mtk_desc_pin *pin = pctl->devdata->pins + i;
|
|
struct mtk_pinctrl_group *group = pctl->groups + i;
|
|
|
|
group->name = pin->pin.name;
|
|
group->pin = pin->pin.number;
|
|
|
|
pctl->grp_names[i] = pin->pin.name;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
mtk_xt_get_gpio_n(void *data, unsigned long eint_n, unsigned int *gpio_n,
|
|
struct gpio_chip **gpio_chip)
|
|
{
|
|
struct mtk_pinctrl *pctl = (struct mtk_pinctrl *)data;
|
|
const struct mtk_desc_pin *pin;
|
|
|
|
pin = mtk_find_pin_by_eint_num(pctl, eint_n);
|
|
if (!pin)
|
|
return -EINVAL;
|
|
|
|
*gpio_chip = pctl->chip;
|
|
*gpio_n = pin->pin.number;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_xt_get_gpio_state(void *data, unsigned long eint_n)
|
|
{
|
|
struct mtk_pinctrl *pctl = (struct mtk_pinctrl *)data;
|
|
const struct mtk_desc_pin *pin;
|
|
|
|
pin = mtk_find_pin_by_eint_num(pctl, eint_n);
|
|
if (!pin)
|
|
return -EINVAL;
|
|
|
|
return mtk_gpio_get(pctl->chip, pin->pin.number);
|
|
}
|
|
|
|
static int mtk_xt_set_gpio_as_eint(void *data, unsigned long eint_n)
|
|
{
|
|
struct mtk_pinctrl *pctl = (struct mtk_pinctrl *)data;
|
|
const struct mtk_desc_pin *pin;
|
|
|
|
pin = mtk_find_pin_by_eint_num(pctl, eint_n);
|
|
if (!pin)
|
|
return -EINVAL;
|
|
|
|
/* set mux to INT mode */
|
|
mtk_pmx_set_mode(pctl->pctl_dev, pin->pin.number, pin->eint.eintmux);
|
|
/* set gpio direction to input */
|
|
mtk_pmx_gpio_set_direction(pctl->pctl_dev, NULL, pin->pin.number,
|
|
true);
|
|
/* set input-enable */
|
|
mtk_pconf_set_ies_smt(pctl, pin->pin.number, 1,
|
|
PIN_CONFIG_INPUT_ENABLE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct mtk_eint_xt mtk_eint_xt = {
|
|
.get_gpio_n = mtk_xt_get_gpio_n,
|
|
.get_gpio_state = mtk_xt_get_gpio_state,
|
|
.set_gpio_as_eint = mtk_xt_set_gpio_as_eint,
|
|
};
|
|
|
|
static int mtk_eint_init(struct mtk_pinctrl *pctl, struct platform_device *pdev)
|
|
{
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct resource *res;
|
|
|
|
if (!of_property_read_bool(np, "interrupt-controller"))
|
|
return -ENODEV;
|
|
|
|
pctl->eint = devm_kzalloc(pctl->dev, sizeof(*pctl->eint), GFP_KERNEL);
|
|
if (!pctl->eint)
|
|
return -ENOMEM;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
pctl->eint->base = devm_ioremap_resource(&pdev->dev, res);
|
|
if (IS_ERR(pctl->eint->base))
|
|
return PTR_ERR(pctl->eint->base);
|
|
|
|
pctl->eint->irq = irq_of_parse_and_map(np, 0);
|
|
if (!pctl->eint->irq)
|
|
return -EINVAL;
|
|
|
|
pctl->eint->dev = &pdev->dev;
|
|
/*
|
|
* If pctl->eint->regs == NULL, it would fall back into using a generic
|
|
* register map in mtk_eint_do_init calls.
|
|
*/
|
|
pctl->eint->regs = pctl->devdata->eint_regs;
|
|
pctl->eint->hw = &pctl->devdata->eint_hw;
|
|
pctl->eint->pctl = pctl;
|
|
pctl->eint->gpio_xlate = &mtk_eint_xt;
|
|
|
|
return mtk_eint_do_init(pctl->eint);
|
|
}
|
|
|
|
int mtk_pctrl_init(struct platform_device *pdev,
|
|
const struct mtk_pinctrl_devdata *data,
|
|
struct regmap *regmap)
|
|
{
|
|
struct pinctrl_pin_desc *pins;
|
|
struct mtk_pinctrl *pctl;
|
|
struct device_node *np = pdev->dev.of_node, *node;
|
|
struct property *prop;
|
|
int ret, i;
|
|
|
|
pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
|
|
if (!pctl)
|
|
return -ENOMEM;
|
|
|
|
platform_set_drvdata(pdev, pctl);
|
|
|
|
prop = of_find_property(np, "pins-are-numbered", NULL);
|
|
if (!prop) {
|
|
dev_err(&pdev->dev, "only support pins-are-numbered format\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
node = of_parse_phandle(np, "mediatek,pctl-regmap", 0);
|
|
if (node) {
|
|
pctl->regmap1 = syscon_node_to_regmap(node);
|
|
if (IS_ERR(pctl->regmap1))
|
|
return PTR_ERR(pctl->regmap1);
|
|
} else if (regmap) {
|
|
pctl->regmap1 = regmap;
|
|
} else {
|
|
dev_err(&pdev->dev, "Pinctrl node has not register regmap.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Only 8135 has two base addr, other SoCs have only one. */
|
|
node = of_parse_phandle(np, "mediatek,pctl-regmap", 1);
|
|
if (node) {
|
|
pctl->regmap2 = syscon_node_to_regmap(node);
|
|
if (IS_ERR(pctl->regmap2))
|
|
return PTR_ERR(pctl->regmap2);
|
|
}
|
|
|
|
pctl->devdata = data;
|
|
ret = mtk_pctrl_build_state(pdev);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "build state failed: %d\n", ret);
|
|
return -EINVAL;
|
|
}
|
|
|
|
pins = devm_kcalloc(&pdev->dev, pctl->devdata->npins, sizeof(*pins),
|
|
GFP_KERNEL);
|
|
if (!pins)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < pctl->devdata->npins; i++)
|
|
pins[i] = pctl->devdata->pins[i].pin;
|
|
|
|
pctl->pctl_desc.name = dev_name(&pdev->dev);
|
|
pctl->pctl_desc.owner = THIS_MODULE;
|
|
pctl->pctl_desc.pins = pins;
|
|
pctl->pctl_desc.npins = pctl->devdata->npins;
|
|
pctl->pctl_desc.confops = &mtk_pconf_ops;
|
|
pctl->pctl_desc.pctlops = &mtk_pctrl_ops;
|
|
pctl->pctl_desc.pmxops = &mtk_pmx_ops;
|
|
pctl->dev = &pdev->dev;
|
|
|
|
pctl->pctl_dev = devm_pinctrl_register(&pdev->dev, &pctl->pctl_desc,
|
|
pctl);
|
|
if (IS_ERR(pctl->pctl_dev)) {
|
|
dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
|
|
return PTR_ERR(pctl->pctl_dev);
|
|
}
|
|
|
|
pctl->chip = devm_kzalloc(&pdev->dev, sizeof(*pctl->chip), GFP_KERNEL);
|
|
if (!pctl->chip)
|
|
return -ENOMEM;
|
|
|
|
*pctl->chip = mtk_gpio_chip;
|
|
pctl->chip->ngpio = pctl->devdata->npins;
|
|
pctl->chip->label = dev_name(&pdev->dev);
|
|
pctl->chip->parent = &pdev->dev;
|
|
pctl->chip->base = -1;
|
|
|
|
ret = gpiochip_add_data(pctl->chip, pctl);
|
|
if (ret)
|
|
return -EINVAL;
|
|
|
|
/* Register the GPIO to pin mappings. */
|
|
ret = gpiochip_add_pin_range(pctl->chip, dev_name(&pdev->dev),
|
|
0, 0, pctl->devdata->npins);
|
|
if (ret) {
|
|
ret = -EINVAL;
|
|
goto chip_error;
|
|
}
|
|
|
|
ret = mtk_eint_init(pctl, pdev);
|
|
if (ret)
|
|
goto chip_error;
|
|
|
|
return 0;
|
|
|
|
chip_error:
|
|
gpiochip_remove(pctl->chip);
|
|
return ret;
|
|
}
|