b906faf7b6
This patch adds members sz_reg fixed in struct mtk_pin_field_calc - The 'fixed' is used to represent the consecutive pins share the same bits within the same register with the 1st pin so that it can largely reduce the entry size a bit. - The 'sz_reg' is used to indicate the range of bits we use in a register that may vary by SoC The above changes make the code more generic and this is useful as there might be other existing or future chips all use the same logic to access their register set and then being a little more abstract could help in the long run. 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>
166 lines
3.8 KiB
C
166 lines
3.8 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;
|
|
}
|
|
|
|
/* 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 + (pin - c->s_pin) * (c->x_bits);
|
|
|
|
/* Fill pfd from bits. For example 32-bit register applied is assumed
|
|
* when c->sz_reg is equal to 32.
|
|
*/
|
|
pfd->offset = c->s_addr + c->x_addrs * (bits / c->sz_reg);
|
|
pfd->bitpos = bits % c->sz_reg;
|
|
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 > c->sz_reg ? 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;
|
|
}
|