u-boot/drivers/bus/ti-sysc.c
Dario Binacchi 92cc4e1c21 bus: ti: add minimal sysc interconnect target driver
We can handle the sysc interconnect target module in a generic way for
many TI SoCs. Initially let's just enable domain clocks before the
children are probed.

The code is loosely based on the drivers/bus/ti-sysc.c of the Linux
kernel version 5.9-rc7.
For DT binding details see:
- Documentation/devicetree/bindings/bus/ti-sysc.txt

Signed-off-by: Dario Binacchi <dariobin@libero.it>
2021-01-12 10:58:04 +05:30

166 lines
3.2 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Texas Instruments sysc interconnect target driver
*
* Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
*/
#include <common.h>
#include <clk.h>
#include <dm.h>
#include <dm/device_compat.h>
enum ti_sysc_clocks {
TI_SYSC_FCK,
TI_SYSC_ICK,
TI_SYSC_MAX_CLOCKS,
};
static const char *const clock_names[] = {"fck", "ick"};
struct ti_sysc_priv {
int clocks_count;
struct clk clocks[TI_SYSC_MAX_CLOCKS];
};
static const struct udevice_id ti_sysc_ids[] = {
{.compatible = "ti,sysc-omap2"},
{.compatible = "ti,sysc-omap4"},
{.compatible = "ti,sysc-omap4-simple"},
{.compatible = "ti,sysc-omap3430-sr"},
{.compatible = "ti,sysc-omap3630-sr"},
{.compatible = "ti,sysc-omap4-sr"},
{.compatible = "ti,sysc-omap3-sham"},
{.compatible = "ti,sysc-omap-aes"},
{.compatible = "ti,sysc-mcasp"},
{.compatible = "ti,sysc-usb-host-fs"},
{}
};
static int ti_sysc_get_one_clock(struct udevice *dev, enum ti_sysc_clocks index)
{
struct ti_sysc_priv *priv = dev_get_priv(dev);
const char *name;
int err;
switch (index) {
case TI_SYSC_FCK:
break;
case TI_SYSC_ICK:
break;
default:
return -EINVAL;
}
name = clock_names[index];
err = clk_get_by_name(dev, name, &priv->clocks[index]);
if (err) {
if (err == -ENODATA)
return 0;
dev_err(dev, "failed to get %s clock\n", name);
return err;
}
return 0;
}
static int ti_sysc_put_clocks(struct udevice *dev)
{
struct ti_sysc_priv *priv = dev_get_priv(dev);
int err;
err = clk_release_all(priv->clocks, priv->clocks_count);
if (err)
dev_err(dev, "failed to release all clocks\n");
return err;
}
static int ti_sysc_get_clocks(struct udevice *dev)
{
struct ti_sysc_priv *priv = dev_get_priv(dev);
int i, err;
for (i = 0; i < TI_SYSC_MAX_CLOCKS; i++) {
err = ti_sysc_get_one_clock(dev, i);
if (!err)
priv->clocks_count++;
else if (err != -ENOENT)
return err;
}
return 0;
}
static int ti_sysc_child_post_remove(struct udevice *dev)
{
struct ti_sysc_priv *priv = dev_get_priv(dev->parent);
int i, err;
for (i = 0; i < priv->clocks_count; i++) {
err = clk_disable(&priv->clocks[i]);
if (err) {
dev_err(dev->parent, "failed to disable %s clock\n",
clock_names[i]);
return err;
}
}
return 0;
}
static int ti_sysc_child_pre_probe(struct udevice *dev)
{
struct ti_sysc_priv *priv = dev_get_priv(dev->parent);
int i, err;
for (i = 0; i < priv->clocks_count; i++) {
err = clk_enable(&priv->clocks[i]);
if (err) {
dev_err(dev->parent, "failed to enable %s clock\n",
clock_names[i]);
return err;
}
}
return 0;
}
static int ti_sysc_remove(struct udevice *dev)
{
return ti_sysc_put_clocks(dev);
}
static int ti_sysc_probe(struct udevice *dev)
{
int err;
err = ti_sysc_get_clocks(dev);
if (err)
goto clocks_err;
return 0;
clocks_err:
ti_sysc_put_clocks(dev);
return err;
}
UCLASS_DRIVER(ti_sysc) = {
.id = UCLASS_SIMPLE_BUS,
.name = "ti_sysc",
.post_bind = dm_scan_fdt_dev
};
U_BOOT_DRIVER(ti_sysc) = {
.name = "ti_sysc",
.id = UCLASS_SIMPLE_BUS,
.of_match = ti_sysc_ids,
.probe = ti_sysc_probe,
.remove = ti_sysc_remove,
.child_pre_probe = ti_sysc_child_pre_probe,
.child_post_remove = ti_sysc_child_post_remove,
.priv_auto = sizeof(struct ti_sysc_priv)
};