mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-25 20:43:32 +00:00
41575d8e4c
This construct is quite long-winded. In earlier days it made some sense since auto-allocation was a strange concept. But with driver model now used pretty universally, we can shorten this to 'auto'. This reduces verbosity and makes it easier to read. Coincidentally it also ensures that every declaration is on one line, thus making dtoc's job easier. Signed-off-by: Simon Glass <sjg@chromium.org>
235 lines
6.4 KiB
C
235 lines
6.4 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 <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")) {
|
|
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
|
|
};
|