// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 MediaTek Inc.
*
* Author: Sean Wang <sean.wang@mediatek.com>
*
*/
#include <dt-bindings/pinctrl/mt65xx.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include "mtk-eint.h"
#include "pinctrl-mtk-common-v2.h"
/**
* struct mtk_drive_desc - the structure that holds the information
* of the driving current
* @min: the minimum current of this group
* @max: the maximum current of this group
* @step: the step current of this group
* @scal: the weight factor
*
* formula: output = ((input) / step - 1) * scal
*/
struct mtk_drive_desc {
u8 min;
u8 max;
u8 step;
u8 scal;
};
/* The groups of drive strength */
static const struct mtk_drive_desc mtk_drive[] = {
[DRV_GRP0] = { 4, 16, 4, 1 },
[DRV_GRP1] = { 4, 16, 4, 2 },
[DRV_GRP2] = { 2, 8, 2, 1 },
[DRV_GRP3] = { 2, 8, 2, 2 },
[DRV_GRP4] = { 2, 16, 2, 1 },
};
static void mtk_w32(struct mtk_pinctrl *pctl, u8 i, u32 reg, u32 val)
{
writel_relaxed(val, pctl->base[i] + reg);
}
static u32 mtk_r32(struct mtk_pinctrl *pctl, u8 i, u32 reg)
{
return readl_relaxed(pctl->base[i] + reg);
}
void mtk_rmw(struct mtk_pinctrl *pctl, u8 i, u32 reg, u32 mask, u32 set)
{
u32 val;
val = mtk_r32(pctl, i, reg);
val &= ~mask;
val |= set;
mtk_w32(pctl, i, reg, val);
}
static int mtk_hw_pin_field_lookup(struct mtk_pinctrl *hw,
const struct mtk_pin_desc *desc,
int field, struct mtk_pin_field *pfd)
{
const struct mtk_pin_field_calc *c;
const struct mtk_pin_reg_calc *rc;
int start = 0, end, check;
bool found = false;
u32 bits;
if (hw->soc->reg_cal && hw->soc->reg_cal[field].range) {
rc = &hw->soc->reg_cal[field];
} else {
dev_dbg(hw->dev,
"Not support field %d for this soc\n", field);
return -ENOTSUPP;
}
end = rc->nranges - 1;
while (start <= end) {
check = (start + end) >> 1;
if (desc->number >= rc->range[check].s_pin
&& desc->number <= rc->range[check].e_pin) {
found = true;
break;
} else if (start == end)
break;
else if (desc->number < rc->range[check].s_pin)
end = check - 1;
else
start = check + 1;
}
if (!found) {
dev_dbg(hw->dev, "Not support field %d for pin = %d (%s)\n",
field, desc->number, desc->name);
return -ENOTSUPP;
}
c = rc->range + check;
if (c->i_base > hw->nbase - 1) {
dev_err(hw->dev,
"Invalid base for field %d for pin = %d (%s)\n",
field, desc->number, desc->name);
return -EINVAL;
}
/* Calculated bits as the overall offset the pin is located at,
* if c->fixed is held, that determines the all the pins in the
* range use the same field with the s_pin.
*/
bits = c->fixed ? c->s_bit : c->s_bit +
(desc->number <