linux_dsm_epyc7002/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
Sean Wang a1a503a8c3 pinctrl: mediatek: add pinctrl-mtk-common-v2 for all MediaTek pinctrls
Irregular register arrangement and distinct logic access from various
MediaTek SoCs would cause pinctrl-mtk-common to bloat and really hard to
maintain in the future so that the patch creates pinctrl-mtk-common-v2
based on the core of mt7622-pinctrl.

The goals pinctrl-mtk-common-v2 want to achieve are to hopefully support
all of MediaTek SoCs, and two kinds of dt-bindings being supported,
Linux generic pinctrl dt-binding mt7622 supports and MediaTek per-pin
dt-binding the other SoCs support the MT8183 and MT6765 incline to make
use of.

The patch starts to refactor MT7622 pinctrl driver first with splitting
out these portable ways from there such as table-based register operation
and drive strength control that is common in both kinds of driver.

Signed-off-by: Ryder.Lee <ryder.lee@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2018-09-18 14:52:34 -07:00

161 lines
3.6 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 MediaTek Inc.
*
* Author: Sean Wang <sean.wang@mediatek.com>
*
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include "pinctrl-mtk-common-v2.h"
static void mtk_w32(struct mtk_pinctrl *pctl, u32 reg, u32 val)
{
writel_relaxed(val, pctl->base + reg);
}
static u32 mtk_r32(struct mtk_pinctrl *pctl, u32 reg)
{
return readl_relaxed(pctl->base + reg);
}
void mtk_rmw(struct mtk_pinctrl *pctl, u32 reg, u32 mask, u32 set)
{
u32 val;
val = mtk_r32(pctl, reg);
val &= ~mask;
val |= set;
mtk_w32(pctl, reg, val);
}
static int mtk_hw_pin_field_lookup(struct mtk_pinctrl *hw, int pin,
const struct mtk_pin_reg_calc *rc,
struct mtk_pin_field *pfd)
{
const struct mtk_pin_field_calc *c, *e;
u32 bits;
c = rc->range;
e = c + rc->nranges;
while (c < e) {
if (pin >= c->s_pin && pin <= c->e_pin)
break;
c++;
}
if (c >= e) {
dev_err(hw->dev, "Out of range for pin = %d\n", pin);
return -EINVAL;
}
/* Caculated bits as the overall offset the pin is located at */
bits = c->s_bit + (pin - c->s_pin) * (c->x_bits);
/* Fill pfd from bits and 32-bit register applied is assumed */
pfd->offset = c->s_addr + c->x_addrs * (bits / 32);
pfd->bitpos = bits % 32;
pfd->mask = (1 << c->x_bits) - 1;
/* pfd->next is used for indicating that bit wrapping-around happens
* which requires the manipulation for bit 0 starting in the next
* register to form the complete field read/write.
*/
pfd->next = pfd->bitpos + c->x_bits - 1 > 31 ? c->x_addrs : 0;
return 0;
}
static int mtk_hw_pin_field_get(struct mtk_pinctrl *hw, int pin,
int field, struct mtk_pin_field *pfd)
{
const struct mtk_pin_reg_calc *rc;
if (field < 0 || field >= PINCTRL_PIN_REG_MAX) {
dev_err(hw->dev, "Invalid Field %d\n", field);
return -EINVAL;
}
if (hw->soc->reg_cal && hw->soc->reg_cal[field].range) {
rc = &hw->soc->reg_cal[field];
} else {
dev_err(hw->dev, "Undefined range for field %d\n", field);
return -EINVAL;
}
return mtk_hw_pin_field_lookup(hw, pin, rc, pfd);
}
static void mtk_hw_bits_part(struct mtk_pin_field *pf, int *h, int *l)
{
*l = 32 - pf->bitpos;
*h = get_count_order(pf->mask) - *l;
}
static void mtk_hw_write_cross_field(struct mtk_pinctrl *hw,
struct mtk_pin_field *pf, int value)
{
int nbits_l, nbits_h;
mtk_hw_bits_part(pf, &nbits_h, &nbits_l);
mtk_rmw(hw, pf->offset, pf->mask << pf->bitpos,
(value & pf->mask) << pf->bitpos);
mtk_rmw(hw, pf->offset + pf->next, BIT(nbits_h) - 1,
(value & pf->mask) >> nbits_l);
}
static void mtk_hw_read_cross_field(struct mtk_pinctrl *hw,
struct mtk_pin_field *pf, int *value)
{
int nbits_l, nbits_h, h, l;
mtk_hw_bits_part(pf, &nbits_h, &nbits_l);
l = (mtk_r32(hw, pf->offset) >> pf->bitpos) & (BIT(nbits_l) - 1);
h = (mtk_r32(hw, pf->offset + pf->next)) & (BIT(nbits_h) - 1);
*value = (h << nbits_l) | l;
}
int mtk_hw_set_value(struct mtk_pinctrl *hw, int pin, int field, int value)
{
struct mtk_pin_field pf;
int err;
err = mtk_hw_pin_field_get(hw, pin, field, &pf);
if (err)
return err;
if (!pf.next)
mtk_rmw(hw, pf.offset, pf.mask << pf.bitpos,
(value & pf.mask) << pf.bitpos);
else
mtk_hw_write_cross_field(hw, &pf, value);
return 0;
}
int mtk_hw_get_value(struct mtk_pinctrl *hw, int pin, int field, int *value)
{
struct mtk_pin_field pf;
int err;
err = mtk_hw_pin_field_get(hw, pin, field, &pf);
if (err)
return err;
if (!pf.next)
*value = (mtk_r32(hw, pf.offset) >> pf.bitpos) & pf.mask;
else
mtk_hw_read_cross_field(hw, &pf, value);
return 0;
}