| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * OMAP clock controller support |
| * |
| * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it> |
| */ |
| |
| #include <dm.h> |
| #include <dm/device_compat.h> |
| #include <clk-uclass.h> |
| #include <asm/arch-am33xx/clock.h> |
| |
| struct clk_ti_ctrl_offs { |
| fdt_addr_t start; |
| fdt_size_t end; |
| }; |
| |
| struct clk_ti_ctrl_priv { |
| int offs_num; |
| struct clk_ti_ctrl_offs *offs; |
| }; |
| |
| static int clk_ti_ctrl_check_offs(struct clk *clk, fdt_addr_t offs) |
| { |
| struct clk_ti_ctrl_priv *priv = dev_get_priv(clk->dev); |
| int i; |
| |
| for (i = 0; i < priv->offs_num; i++) { |
| if (offs >= priv->offs[i].start && offs <= priv->offs[i].end) |
| return 0; |
| } |
| |
| return -EFAULT; |
| } |
| |
| static int clk_ti_ctrl_disable(struct clk *clk) |
| { |
| struct clk_ti_ctrl_priv *priv = dev_get_priv(clk->dev); |
| u32 *clk_modules[2] = { }; |
| fdt_addr_t offs; |
| int err; |
| |
| offs = priv->offs[0].start + clk->id; |
| err = clk_ti_ctrl_check_offs(clk, offs); |
| if (err) { |
| dev_err(clk->dev, "invalid offset: 0x%llx\n", (fdt64_t)offs); |
| return err; |
| } |
| |
| clk_modules[0] = (u32 *)(offs); |
| dev_dbg(clk->dev, "disable module @ %p\n", clk_modules[0]); |
| do_disable_clocks(NULL, clk_modules, 1); |
| return 0; |
| } |
| |
| static int clk_ti_ctrl_enable(struct clk *clk) |
| { |
| struct clk_ti_ctrl_priv *priv = dev_get_priv(clk->dev); |
| u32 *clk_modules[2] = { }; |
| fdt_addr_t offs; |
| int err; |
| |
| offs = priv->offs[0].start + clk->id; |
| err = clk_ti_ctrl_check_offs(clk, offs); |
| if (err) { |
| dev_err(clk->dev, "invalid offset: 0x%llx\n", (fdt64_t)offs); |
| return err; |
| } |
| |
| clk_modules[0] = (u32 *)(offs); |
| dev_dbg(clk->dev, "enable module @ %p\n", clk_modules[0]); |
| do_enable_clocks(NULL, clk_modules, 1); |
| return 0; |
| } |
| |
| static ulong clk_ti_ctrl_get_rate(struct clk *clk) |
| { |
| return 0; |
| } |
| |
| static int clk_ti_ctrl_of_xlate(struct clk *clk, |
| struct ofnode_phandle_args *args) |
| { |
| if (args->args_count != 2) { |
| dev_err(clk->dev, "invalid args_count: %d\n", args->args_count); |
| return -EINVAL; |
| } |
| |
| if (args->args_count) |
| clk->id = args->args[0]; |
| else |
| clk->id = 0; |
| |
| dev_dbg(clk->dev, "name=%s, id=%ld\n", clk->dev->name, clk->id); |
| return 0; |
| } |
| |
| static int clk_ti_ctrl_of_to_plat(struct udevice *dev) |
| { |
| struct clk_ti_ctrl_priv *priv = dev_get_priv(dev); |
| fdt_size_t fdt_size; |
| int i, size; |
| |
| size = dev_read_size(dev, "reg"); |
| if (size < 0) { |
| dev_err(dev, "failed to get 'reg' size\n"); |
| return size; |
| } |
| |
| priv->offs_num = size / 2 / sizeof(u32); |
| dev_dbg(dev, "size=%d, regs_num=%d\n", size, priv->offs_num); |
| |
| priv->offs = kmalloc_array(priv->offs_num, sizeof(*priv->offs), |
| GFP_KERNEL); |
| if (!priv->offs) |
| return -ENOMEM; |
| |
| for (i = 0; i < priv->offs_num; i++) { |
| priv->offs[i].start = |
| dev_read_addr_size_index(dev, i, &fdt_size); |
| if (priv->offs[i].start == FDT_ADDR_T_NONE) { |
| dev_err(dev, "failed to get offset %d\n", i); |
| return -EINVAL; |
| } |
| |
| priv->offs[i].end = priv->offs[i].start + fdt_size; |
| dev_dbg(dev, "start=0x%016llx, end=0x%016llx\n", |
| (fdt64_t)priv->offs[i].start, |
| (fdt64_t)priv->offs[i].end); |
| } |
| |
| return 0; |
| } |
| |
| static struct clk_ops clk_ti_ctrl_ops = { |
| .of_xlate = clk_ti_ctrl_of_xlate, |
| .enable = clk_ti_ctrl_enable, |
| .disable = clk_ti_ctrl_disable, |
| .get_rate = clk_ti_ctrl_get_rate, |
| }; |
| |
| static const struct udevice_id clk_ti_ctrl_ids[] = { |
| {.compatible = "ti,clkctrl"}, |
| {}, |
| }; |
| |
| U_BOOT_DRIVER(clk_ti_ctrl) = { |
| .name = "ti_ctrl_clk", |
| .id = UCLASS_CLK, |
| .of_match = clk_ti_ctrl_ids, |
| .of_to_plat = clk_ti_ctrl_of_to_plat, |
| .ops = &clk_ti_ctrl_ops, |
| .priv_auto = sizeof(struct clk_ti_ctrl_priv), |
| }; |