// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018 MediaTek Inc. * * Author: Sean Wang * */ #include #include #include #include #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; }