u-boot/drivers/pinctrl/mvebu/pinctrl-mvebu.c
Konstantin Porotchkin ef6fcab85f mvebu: pinctrl: apply SDHCI PHY config for A7K
Current pin control driver applies SDHCI PHY MUX selection
when board DT calls for eMMC function on MPP wires.
However, for CP side eMMC, only the "armada-8k-cpm-pinctrl"
compatibility string is taken into account, which causes
CP-SDHCI on Armada-7K boards to fail.
This patch adds "armada-7k-pinctrl" compatibility string
handling for the CP-SDHCI PHY configuration case.

Signed-off-by: Konstantin Porotchkin <kostap@marvell.com>
Reviewed-by: Igal Liberman <igall@marvell.com>
Reviewed-by: Stefan Roese <sr@denx.de>
2022-07-29 10:02:43 +02:00

238 lines
6.5 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2016 Marvell International Ltd.
* https://spdx.org/licenses
*/
#include <common.h>
#include <config.h>
#include <fdtdec.h>
#include <errno.h>
#include <dm.h>
#include <log.h>
#include <asm/global_data.h>
#include <dm/pinctrl.h>
#include <dm/root.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/arch-armada8k/soc-info.h>
#include <linux/bitops.h>
#include "pinctrl-mvebu.h"
#define AP_EMMC_PHY_CTRL_REG 0x100
#define CP_EMMC_PHY_CTRL_REG 0x424
#define EMMC_PHY_CTRL_SDPHY_EN BIT(0)
#define AP806_EMMC_CLK_PIN_ID 0
#define AP806_EMMC_CLK_FUNC 0x1
#define CP110_EMMC_CLK_PIN_ID 56
#define CP110_EMMC_CLK_FUNC 0xe
DECLARE_GLOBAL_DATA_PTR;
/* mvebu_pinctl_emmc_set_mux: configure sd/mmc PHY mux
* To enable SDIO/eMMC in Armada-APN806/CP110, need to configure PHY mux.
* eMMC/SD PHY register responsible for muxing between MPPs and SD/eMMC
* controller:
* - Bit0 enabled SDIO/eMMC PHY is used as a MPP muxltiplexer,
* - Bit0 disabled SDIO/eMMC PHY is connected to SDIO/eMMC controller
* If pin function is set to eMMC/SD, then configure the eMMC/SD PHY
* muxltiplexer register to be on SDIO/eMMC controller
*/
void mvebu_pinctl_emmc_set_mux(struct udevice *dev, u32 pin, u32 func)
{
const void *blob = gd->fdt_blob;
int node = dev_of_offset(dev);
struct mvebu_pinctrl_priv *priv = dev_get_priv(dev);
if (!fdt_node_check_compatible(blob, node, "marvell,ap806-pinctrl")) {
if ((pin == AP806_EMMC_CLK_PIN_ID) &&
(func == AP806_EMMC_CLK_FUNC)) {
clrbits_le32(priv->base_reg + AP_EMMC_PHY_CTRL_REG,
EMMC_PHY_CTRL_SDPHY_EN);
}
} else if (!fdt_node_check_compatible(blob, node,
"marvell,armada-8k-cpm-pinctrl") ||
!fdt_node_check_compatible(blob, node,
"marvell,armada-7k-pinctrl")) {
if ((pin == CP110_EMMC_CLK_PIN_ID) &&
(func == CP110_EMMC_CLK_FUNC)) {
clrbits_le32(priv->base_reg + CP_EMMC_PHY_CTRL_REG,
EMMC_PHY_CTRL_SDPHY_EN);
}
}
}
/*
* mvebu_pinctrl_set_state: configure pin functions.
* @dev: the pinctrl device to be configured.
* @config: the state to be configured.
* @return: 0 in success
*/
int mvebu_pinctrl_set_state(struct udevice *dev, struct udevice *config)
{
const void *blob = gd->fdt_blob;
int node = dev_of_offset(config);
struct mvebu_pinctrl_priv *priv;
u32 pin_arr[MVEBU_MAX_PINS_PER_BANK];
u32 function;
int i, pin_count;
priv = dev_get_priv(dev);
pin_count = fdtdec_get_int_array_count(blob, node,
"marvell,pins",
pin_arr,
MVEBU_MAX_PINS_PER_BANK);
if (pin_count <= 0) {
debug("Failed reading pins array for pinconfig %s (%d)\n",
config->name, pin_count);
return -EINVAL;
}
function = fdtdec_get_int(blob, node, "marvell,function", 0xff);
/*
* Check if setup of PHY mux is needed for this pins group.
* Only the first pin id in array is tested, all the rest use the same
* pin function.
*/
mvebu_pinctl_emmc_set_mux(dev, pin_arr[0], function);
for (i = 0; i < pin_count; i++) {
int reg_offset;
int field_offset;
int pin = pin_arr[i];
if (function > priv->max_func) {
debug("Illegal function %d for pinconfig %s\n",
function, config->name);
return -EINVAL;
}
/* Calculate register address and bit in register */
reg_offset = priv->reg_direction * 4 *
(pin >> (PIN_REG_SHIFT));
field_offset = (BITS_PER_PIN) * (pin & PIN_FIELD_MASK);
clrsetbits_le32(priv->base_reg + reg_offset,
PIN_FUNC_MASK << field_offset,
(function & PIN_FUNC_MASK) << field_offset);
}
return 0;
}
/*
* mvebu_pinctrl_set_state_all: configure the entire bank pin functions.
* @dev: the pinctrl device to be configured.
* @config: the state to be configured.
* @return: 0 in success
*/
static int mvebu_pinctrl_set_state_all(struct udevice *dev,
struct udevice *config)
{
const void *blob = gd->fdt_blob;
int node = dev_of_offset(config);
struct mvebu_pinctrl_priv *priv;
u32 func_arr[MVEBU_MAX_PINS_PER_BANK];
int pin, err;
priv = dev_get_priv(dev);
err = fdtdec_get_int_array(blob, node, "pin-func",
func_arr, priv->pin_cnt);
if (err) {
debug("Failed reading pin functions for bank %s\n",
priv->bank_name);
return -EINVAL;
}
/* Check if setup of PHY mux is needed for this pins group. */
if (priv->pin_cnt < CP110_EMMC_CLK_PIN_ID)
mvebu_pinctl_emmc_set_mux(dev, AP806_EMMC_CLK_PIN_ID,
func_arr[AP806_EMMC_CLK_PIN_ID]);
else
mvebu_pinctl_emmc_set_mux(dev, CP110_EMMC_CLK_PIN_ID,
func_arr[CP110_EMMC_CLK_PIN_ID]);
for (pin = 0; pin < priv->pin_cnt; pin++) {
int reg_offset;
int field_offset;
u32 func = func_arr[pin];
/* Bypass pins with function 0xFF */
if (func == 0xff) {
debug("Warning: pin %d value is not modified ", pin);
debug("(kept as default)\n");
continue;
} else if (func > priv->max_func) {
debug("Illegal function %d for pin %d\n", func, pin);
return -EINVAL;
}
/* Calculate register address and bit in register */
reg_offset = priv->reg_direction * 4 *
(pin >> (PIN_REG_SHIFT));
field_offset = (BITS_PER_PIN) * (pin & PIN_FIELD_MASK);
clrsetbits_le32(priv->base_reg + reg_offset,
PIN_FUNC_MASK << field_offset,
(func & PIN_FUNC_MASK) << field_offset);
}
return 0;
}
int mvebu_pinctl_probe(struct udevice *dev)
{
const void *blob = gd->fdt_blob;
int node = dev_of_offset(dev);
struct mvebu_pinctrl_priv *priv;
priv = dev_get_priv(dev);
if (!priv) {
debug("%s: Failed to get private\n", __func__);
return -EINVAL;
}
priv->base_reg = dev_read_addr_ptr(dev);
if (!priv->base_reg) {
debug("%s: Failed to get base address\n", __func__);
return -EINVAL;
}
priv->pin_cnt = fdtdec_get_int(blob, node, "pin-count",
MVEBU_MAX_PINS_PER_BANK);
priv->max_func = fdtdec_get_int(blob, node, "max-func",
MVEBU_MAX_FUNC);
priv->bank_name = fdt_getprop(blob, node, "bank-name", NULL);
priv->reg_direction = 1;
if (fdtdec_get_bool(blob, node, "reverse-reg"))
priv->reg_direction = -1;
return mvebu_pinctrl_set_state_all(dev, dev);
}
static struct pinctrl_ops mvebu_pinctrl_ops = {
.set_state = mvebu_pinctrl_set_state
};
static const struct udevice_id mvebu_pinctrl_ids[] = {
{ .compatible = "marvell,mvebu-pinctrl" },
{ .compatible = "marvell,ap806-pinctrl" },
{ .compatible = "marvell,armada-7k-pinctrl" },
{ .compatible = "marvell,armada-8k-cpm-pinctrl" },
{ .compatible = "marvell,armada-8k-cps-pinctrl" },
{ }
};
U_BOOT_DRIVER(pinctrl_mvebu) = {
.name = "mvebu_pinctrl",
.id = UCLASS_PINCTRL,
.of_match = mvebu_pinctrl_ids,
.priv_auto = sizeof(struct mvebu_pinctrl_priv),
.ops = &mvebu_pinctrl_ops,
.probe = mvebu_pinctl_probe
};