| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * Texas Instruments sysc interconnect target driver | |
| * | |
| * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it> | |
| */ | |
| #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; | |
| } | |
| 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) | |
| }; |