// SPDX-License-Identifier: GPL-2.0+ /* * OMAP clock controller support * * Copyright (C) 2020 Dario Binacchi */ #include #include #include #include #include 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%lx\n", 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%lx\n", 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%08lx, end=0x%08lx\n", priv->offs[i].start, 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), };