u-boot/drivers/soc/samsung/exynos-pmu.c
Sam Protsenko 8fd06aeb8a soc: samsung: Add Exynos PMU driver
Add basic Power Management Unit (PMU) driver for Exynos SoCs. For now
it's only capable of changing UART path in PMU, which is needed for
E850-96 board. The driver's structure resembles the exynos-pmu driver
from Linux kernel, and although it's very basic and slim at the moment,
it can be easily extended in future if the need arises.

UCLASS_NOP is used, as there are no benefits in using more elaborate
classes like UCLASS_MISC in this case. The DM_FLAG_PROBE_AFTER_BIND flag
is added in bind function, as the probe function must be always called
for this driver.

Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org>
Reviewed-by: Chanho Park <chanho61.park@samsung.com>
Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
2024-01-24 11:23:20 +09:00

102 lines
2.4 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2023 Linaro Ltd.
* Author: Sam Protsenko <semen.protsenko@linaro.org>
*
* Exynos PMU (Power Management Unit) driver.
*/
#include <dm.h>
#include <errno.h>
#include <regmap.h>
#include <syscon.h>
#include <linux/bitops.h>
#include <linux/err.h>
#define EXYNOS850_UART_IO_SHARE_CTRL 0x0760
#define SEL_RXD_AP_UART_SHIFT 16
#define SEL_RXD_AP_UART_MASK GENMASK(17, 16)
#define SEL_TXD_GPIO_1_SHIFT 20
#define SEL_TXD_GPIO_1_MASK GENMASK(21, 20)
#define RXD_GPIO_1 0x3
#define TXD_AP_UART 0x0
struct exynos_pmu {
struct udevice *dev;
const struct exynos_pmu_data *pmu_data;
struct regmap *regmap;
};
struct exynos_pmu_data {
int (*pmu_init)(struct exynos_pmu *priv);
};
static int exynos850_pmu_init(struct exynos_pmu *priv)
{
ofnode node;
bool uart_debug_1;
unsigned int offset, mask, value;
node = dev_ofnode(priv->dev);
uart_debug_1 = ofnode_read_bool(node, "samsung,uart-debug-1");
if (!uart_debug_1)
return 0;
/*
* If uart1_pins are used for serial, AP UART lines have to be muxed
* in PMU block to UART_DEBUG_1 path (GPIO_1). By default (reset value)
* UART_DEBUG_0 path (uart0_pins) is connected to AP UART lines.
*/
offset = EXYNOS850_UART_IO_SHARE_CTRL;
mask = SEL_RXD_AP_UART_MASK | SEL_TXD_GPIO_1_MASK;
value = RXD_GPIO_1 << SEL_RXD_AP_UART_SHIFT |
TXD_AP_UART << SEL_TXD_GPIO_1_SHIFT;
return regmap_update_bits(priv->regmap, offset, mask, value);
}
static const struct exynos_pmu_data exynos850_pmu_data = {
.pmu_init = exynos850_pmu_init,
};
static int exynos_pmu_bind(struct udevice *dev)
{
dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND);
return 0;
}
static int exynos_pmu_probe(struct udevice *dev)
{
ofnode node;
struct exynos_pmu *priv;
priv = dev_get_priv(dev);
priv->dev = dev;
node = dev_ofnode(dev);
priv->regmap = syscon_node_to_regmap(node);
if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap);
priv->pmu_data = (struct exynos_pmu_data *)dev_get_driver_data(dev);
if (priv->pmu_data && priv->pmu_data->pmu_init)
return priv->pmu_data->pmu_init(priv);
return 0;
}
static const struct udevice_id exynos_pmu_ids[] = {
{
.compatible = "samsung,exynos850-pmu",
.data = (ulong)&exynos850_pmu_data
},
{ /* sentinel */ }
};
U_BOOT_DRIVER(exynos_pmu) = {
.name = "exynos-pmu",
.id = UCLASS_NOP,
.of_match = exynos_pmu_ids,
.bind = exynos_pmu_bind,
.probe = exynos_pmu_probe,
.priv_auto = sizeof(struct exynos_pmu),
};