123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2018 Marek Vasut <marex@denx.de>
- */
- #include <common.h>
- #include <malloc.h>
- #include <asm/io.h>
- #include <clk-uclass.h>
- #include <dm.h>
- #include <dm/device_compat.h>
- #include <dm/devres.h>
- #include <dm/lists.h>
- #include <dm/util.h>
- #include <linux/bitops.h>
- #include <asm/arch/clock_manager.h>
- enum socfpga_a10_clk_type {
- SOCFPGA_A10_CLK_MAIN_PLL,
- SOCFPGA_A10_CLK_PER_PLL,
- SOCFPGA_A10_CLK_PERIP_CLK,
- SOCFPGA_A10_CLK_GATE_CLK,
- SOCFPGA_A10_CLK_UNKNOWN_CLK,
- };
- struct socfpga_a10_clk_plat {
- enum socfpga_a10_clk_type type;
- struct clk_bulk clks;
- u32 regs;
- /* Fixed divider */
- u16 fix_div;
- /* Control register */
- u16 ctl_reg;
- /* Divider register */
- u16 div_reg;
- u8 div_len;
- u8 div_off;
- /* Clock gating register */
- u16 gate_reg;
- u8 gate_bit;
- };
- static int socfpga_a10_clk_get_upstream(struct clk *clk, struct clk **upclk)
- {
- struct socfpga_a10_clk_plat *plat = dev_get_plat(clk->dev);
- u32 reg, maxval;
- if (plat->clks.count == 0)
- return 0;
- if (plat->clks.count == 1) {
- *upclk = &plat->clks.clks[0];
- return 0;
- }
- if (!plat->ctl_reg) {
- dev_err(clk->dev, "Invalid control register\n");
- return -EINVAL;
- }
- reg = readl(plat->regs + plat->ctl_reg);
- /* Assume PLLs are ON for now */
- if (plat->type == SOCFPGA_A10_CLK_MAIN_PLL) {
- reg = (reg >> 8) & 0x3;
- maxval = 2;
- } else if (plat->type == SOCFPGA_A10_CLK_PER_PLL) {
- reg = (reg >> 8) & 0x3;
- maxval = 3;
- } else {
- reg = (reg >> 16) & 0x7;
- maxval = 4;
- }
- if (reg > maxval) {
- dev_err(clk->dev, "Invalid clock source\n");
- return -EINVAL;
- }
- *upclk = &plat->clks.clks[reg];
- return 0;
- }
- static int socfpga_a10_clk_endisable(struct clk *clk, bool enable)
- {
- struct socfpga_a10_clk_plat *plat = dev_get_plat(clk->dev);
- struct clk *upclk = NULL;
- int ret;
- if (!enable && plat->gate_reg)
- clrbits_le32(plat->regs + plat->gate_reg, BIT(plat->gate_bit));
- ret = socfpga_a10_clk_get_upstream(clk, &upclk);
- if (ret)
- return ret;
- if (upclk) {
- if (enable)
- clk_enable(upclk);
- else
- clk_disable(upclk);
- }
- if (enable && plat->gate_reg)
- setbits_le32(plat->regs + plat->gate_reg, BIT(plat->gate_bit));
- return 0;
- }
- static int socfpga_a10_clk_enable(struct clk *clk)
- {
- return socfpga_a10_clk_endisable(clk, true);
- }
- static int socfpga_a10_clk_disable(struct clk *clk)
- {
- return socfpga_a10_clk_endisable(clk, false);
- }
- static ulong socfpga_a10_clk_get_rate(struct clk *clk)
- {
- struct socfpga_a10_clk_plat *plat = dev_get_plat(clk->dev);
- struct clk *upclk = NULL;
- ulong rate = 0, reg, numer, denom;
- int ret;
- ret = socfpga_a10_clk_get_upstream(clk, &upclk);
- if (ret || !upclk)
- return 0;
- rate = clk_get_rate(upclk);
- if (plat->type == SOCFPGA_A10_CLK_MAIN_PLL) {
- reg = readl(plat->regs + plat->ctl_reg + 4); /* VCO1 */
- numer = reg & CLKMGR_MAINPLL_VCO1_NUMER_MSK;
- denom = (reg >> CLKMGR_MAINPLL_VCO1_DENOM_LSB) &
- CLKMGR_MAINPLL_VCO1_DENOM_MSK;
- rate /= denom + 1;
- rate *= numer + 1;
- } else if (plat->type == SOCFPGA_A10_CLK_PER_PLL) {
- reg = readl(plat->regs + plat->ctl_reg + 4); /* VCO1 */
- numer = reg & CLKMGR_PERPLL_VCO1_NUMER_MSK;
- denom = (reg >> CLKMGR_PERPLL_VCO1_DENOM_LSB) &
- CLKMGR_PERPLL_VCO1_DENOM_MSK;
- rate /= denom + 1;
- rate *= numer + 1;
- } else {
- rate /= plat->fix_div;
- if (plat->fix_div == 1 && plat->ctl_reg) {
- reg = readl(plat->regs + plat->ctl_reg);
- reg &= 0x7ff;
- rate /= reg + 1;
- }
- if (plat->div_reg) {
- reg = readl(plat->regs + plat->div_reg);
- reg >>= plat->div_off;
- reg &= (1 << plat->div_len) - 1;
- if (plat->type == SOCFPGA_A10_CLK_PERIP_CLK)
- rate /= reg + 1;
- if (plat->type == SOCFPGA_A10_CLK_GATE_CLK)
- rate /= 1 << reg;
- }
- }
- return rate;
- }
- static struct clk_ops socfpga_a10_clk_ops = {
- .enable = socfpga_a10_clk_enable,
- .disable = socfpga_a10_clk_disable,
- .get_rate = socfpga_a10_clk_get_rate,
- };
- /*
- * This workaround tries to fix the massively broken generated "handoff" DT,
- * which contains duplicate clock nodes without any connection to the clock
- * manager DT node. Yet, those "handoff" DT nodes contain configuration of
- * the fixed input clock of the Arria10 which are missing from the base DT
- * for Arria10.
- *
- * This workaround sets up upstream clock for the fixed input clocks of the
- * A10 described in the base DT such that they map to the fixed clock from
- * the "handoff" DT. This does not fully match how the clock look on the
- * A10, but it is the least intrusive way to fix this mess.
- */
- static void socfpga_a10_handoff_workaround(struct udevice *dev)
- {
- struct socfpga_a10_clk_plat *plat = dev_get_plat(dev);
- const void *fdt = gd->fdt_blob;
- struct clk_bulk *bulk = &plat->clks;
- int i, ret, offset = dev_of_offset(dev);
- static const char * const socfpga_a10_fixedclk_map[] = {
- "osc1", "altera_arria10_hps_eosc1",
- "cb_intosc_ls_clk", "altera_arria10_hps_cb_intosc_ls",
- "f2s_free_clk", "altera_arria10_hps_f2h_free",
- };
- if (fdt_node_check_compatible(fdt, offset, "fixed-clock"))
- return;
- for (i = 0; i < ARRAY_SIZE(socfpga_a10_fixedclk_map); i += 2)
- if (!strcmp(dev->name, socfpga_a10_fixedclk_map[i]))
- break;
- if (i == ARRAY_SIZE(socfpga_a10_fixedclk_map))
- return;
- ret = uclass_get_device_by_name(UCLASS_CLK,
- socfpga_a10_fixedclk_map[i + 1], &dev);
- if (ret)
- return;
- bulk->count = 1;
- bulk->clks = devm_kcalloc(dev, bulk->count,
- sizeof(struct clk), GFP_KERNEL);
- if (!bulk->clks)
- return;
- ret = clk_request(dev, &bulk->clks[0]);
- if (ret)
- free(bulk->clks);
- }
- static int socfpga_a10_clk_bind(struct udevice *dev)
- {
- const void *fdt = gd->fdt_blob;
- int offset = dev_of_offset(dev);
- bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC);
- const char *name;
- int ret;
- for (offset = fdt_first_subnode(fdt, offset);
- offset > 0;
- offset = fdt_next_subnode(fdt, offset)) {
- name = fdt_get_name(fdt, offset, NULL);
- if (!name)
- return -EINVAL;
- if (!strcmp(name, "clocks")) {
- offset = fdt_first_subnode(fdt, offset);
- name = fdt_get_name(fdt, offset, NULL);
- if (!name)
- return -EINVAL;
- }
- /* Filter out supported sub-clock */
- if (fdt_node_check_compatible(fdt, offset,
- "altr,socfpga-a10-pll-clock") &&
- fdt_node_check_compatible(fdt, offset,
- "altr,socfpga-a10-perip-clk") &&
- fdt_node_check_compatible(fdt, offset,
- "altr,socfpga-a10-gate-clk") &&
- fdt_node_check_compatible(fdt, offset, "fixed-clock"))
- continue;
- if (pre_reloc_only &&
- !ofnode_pre_reloc(offset_to_ofnode(offset)))
- continue;
- ret = device_bind_driver_to_node(dev, "clk-a10", name,
- offset_to_ofnode(offset),
- NULL);
- if (ret)
- return ret;
- }
- return 0;
- }
- static int socfpga_a10_clk_probe(struct udevice *dev)
- {
- struct socfpga_a10_clk_plat *plat = dev_get_plat(dev);
- struct socfpga_a10_clk_plat *pplat;
- struct udevice *pdev;
- const void *fdt = gd->fdt_blob;
- int offset = dev_of_offset(dev);
- clk_get_bulk(dev, &plat->clks);
- socfpga_a10_handoff_workaround(dev);
- if (!fdt_node_check_compatible(fdt, offset, "altr,clk-mgr")) {
- plat->regs = dev_read_addr(dev);
- } else {
- pdev = dev_get_parent(dev);
- if (!pdev)
- return -ENODEV;
- pplat = dev_get_plat(pdev);
- if (!pplat)
- return -EINVAL;
- plat->ctl_reg = dev_read_u32_default(dev, "reg", 0x0);
- plat->regs = pplat->regs;
- }
- if (!fdt_node_check_compatible(fdt, offset,
- "altr,socfpga-a10-pll-clock")) {
- /* Main PLL has 3 upstream clock */
- if (plat->clks.count == 3)
- plat->type = SOCFPGA_A10_CLK_MAIN_PLL;
- else
- plat->type = SOCFPGA_A10_CLK_PER_PLL;
- } else if (!fdt_node_check_compatible(fdt, offset,
- "altr,socfpga-a10-perip-clk")) {
- plat->type = SOCFPGA_A10_CLK_PERIP_CLK;
- } else if (!fdt_node_check_compatible(fdt, offset,
- "altr,socfpga-a10-gate-clk")) {
- plat->type = SOCFPGA_A10_CLK_GATE_CLK;
- } else {
- plat->type = SOCFPGA_A10_CLK_UNKNOWN_CLK;
- }
- return 0;
- }
- static int socfpga_a10_of_to_plat(struct udevice *dev)
- {
- struct socfpga_a10_clk_plat *plat = dev_get_plat(dev);
- unsigned int divreg[3], gatereg[2];
- int ret;
- plat->type = SOCFPGA_A10_CLK_UNKNOWN_CLK;
- plat->fix_div = dev_read_u32_default(dev, "fixed-divider", 1);
- ret = dev_read_u32_array(dev, "div-reg", divreg, ARRAY_SIZE(divreg));
- if (!ret) {
- plat->div_reg = divreg[0];
- plat->div_len = divreg[2];
- plat->div_off = divreg[1];
- }
- ret = dev_read_u32_array(dev, "clk-gate", gatereg, ARRAY_SIZE(gatereg));
- if (!ret) {
- plat->gate_reg = gatereg[0];
- plat->gate_bit = gatereg[1];
- }
- return 0;
- }
- static const struct udevice_id socfpga_a10_clk_match[] = {
- { .compatible = "altr,clk-mgr" },
- {}
- };
- U_BOOT_DRIVER(socfpga_a10_clk) = {
- .name = "clk-a10",
- .id = UCLASS_CLK,
- .of_match = socfpga_a10_clk_match,
- .ops = &socfpga_a10_clk_ops,
- .bind = socfpga_a10_clk_bind,
- .probe = socfpga_a10_clk_probe,
- .of_to_plat = socfpga_a10_of_to_plat,
- .plat_auto = sizeof(struct socfpga_a10_clk_plat),
- };
|