19e5935c0SWenyou Yang /*
29e5935c0SWenyou Yang * Copyright (C) 2016 Atmel Corporation
39e5935c0SWenyou Yang * Wenyou.Yang <wenyou.yang@atmel.com>
49e5935c0SWenyou Yang *
59e5935c0SWenyou Yang * SPDX-License-Identifier: GPL-2.0+
69e5935c0SWenyou Yang */
79e5935c0SWenyou Yang
89e5935c0SWenyou Yang #include <common.h>
99e5935c0SWenyou Yang #include <clk-uclass.h>
10*9d922450SSimon Glass #include <dm.h>
119e5935c0SWenyou Yang #include <linux/io.h>
129e5935c0SWenyou Yang #include <mach/at91_pmc.h>
139e5935c0SWenyou Yang #include "pmc.h"
149e5935c0SWenyou Yang
159e5935c0SWenyou Yang #define PERIPHERAL_ID_MIN 2
169e5935c0SWenyou Yang #define PERIPHERAL_ID_MAX 31
179e5935c0SWenyou Yang #define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX))
189e5935c0SWenyou Yang
19bb1adf29SWenyou Yang enum periph_clk_type {
20bb1adf29SWenyou Yang CLK_PERIPH_AT91RM9200 = 0,
21bb1adf29SWenyou Yang CLK_PERIPH_AT91SAM9X5,
22bb1adf29SWenyou Yang };
236cadaa04SWenyou Yang /**
246cadaa04SWenyou Yang * sam9x5_periph_clk_bind() - for the periph clock driver
256cadaa04SWenyou Yang * Recursively bind its children as clk devices.
266cadaa04SWenyou Yang *
276cadaa04SWenyou Yang * @return: 0 on success, or negative error code on failure
286cadaa04SWenyou Yang */
sam9x5_periph_clk_bind(struct udevice * dev)296cadaa04SWenyou Yang static int sam9x5_periph_clk_bind(struct udevice *dev)
306cadaa04SWenyou Yang {
316cadaa04SWenyou Yang return at91_clk_sub_device_bind(dev, "periph-clk");
326cadaa04SWenyou Yang }
336cadaa04SWenyou Yang
346cadaa04SWenyou Yang static const struct udevice_id sam9x5_periph_clk_match[] = {
35bb1adf29SWenyou Yang {
36bb1adf29SWenyou Yang .compatible = "atmel,at91rm9200-clk-peripheral",
37bb1adf29SWenyou Yang .data = CLK_PERIPH_AT91RM9200,
38bb1adf29SWenyou Yang },
39bb1adf29SWenyou Yang {
40bb1adf29SWenyou Yang .compatible = "atmel,at91sam9x5-clk-peripheral",
41bb1adf29SWenyou Yang .data = CLK_PERIPH_AT91SAM9X5,
42bb1adf29SWenyou Yang },
436cadaa04SWenyou Yang {}
446cadaa04SWenyou Yang };
456cadaa04SWenyou Yang
466cadaa04SWenyou Yang U_BOOT_DRIVER(sam9x5_periph_clk) = {
476cadaa04SWenyou Yang .name = "sam9x5-periph-clk",
486cadaa04SWenyou Yang .id = UCLASS_MISC,
496cadaa04SWenyou Yang .of_match = sam9x5_periph_clk_match,
506cadaa04SWenyou Yang .bind = sam9x5_periph_clk_bind,
516cadaa04SWenyou Yang };
526cadaa04SWenyou Yang
536cadaa04SWenyou Yang /*---------------------------------------------------------*/
546cadaa04SWenyou Yang
periph_clk_enable(struct clk * clk)556cadaa04SWenyou Yang static int periph_clk_enable(struct clk *clk)
569e5935c0SWenyou Yang {
579e5935c0SWenyou Yang struct pmc_platdata *plat = dev_get_platdata(clk->dev);
589e5935c0SWenyou Yang struct at91_pmc *pmc = plat->reg_base;
59bb1adf29SWenyou Yang enum periph_clk_type clk_type;
60bb1adf29SWenyou Yang void *addr;
619e5935c0SWenyou Yang
629e5935c0SWenyou Yang if (clk->id < PERIPHERAL_ID_MIN)
639e5935c0SWenyou Yang return -1;
649e5935c0SWenyou Yang
65bb1adf29SWenyou Yang clk_type = dev_get_driver_data(dev_get_parent(clk->dev));
66bb1adf29SWenyou Yang if (clk_type == CLK_PERIPH_AT91RM9200) {
67bb1adf29SWenyou Yang addr = &pmc->pcer;
68bb1adf29SWenyou Yang if (clk->id > PERIPHERAL_ID_MAX)
69bb1adf29SWenyou Yang addr = &pmc->pcer1;
70bb1adf29SWenyou Yang
71bb1adf29SWenyou Yang setbits_le32(addr, PERIPHERAL_MASK(clk->id));
72bb1adf29SWenyou Yang } else {
739e5935c0SWenyou Yang writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
74bb1adf29SWenyou Yang setbits_le32(&pmc->pcr,
75bb1adf29SWenyou Yang AT91_PMC_PCR_CMD_WRITE | AT91_PMC_PCR_EN);
76bb1adf29SWenyou Yang }
779e5935c0SWenyou Yang
789e5935c0SWenyou Yang return 0;
799e5935c0SWenyou Yang }
809e5935c0SWenyou Yang
periph_get_rate(struct clk * clk)816cadaa04SWenyou Yang static ulong periph_get_rate(struct clk *clk)
829e5935c0SWenyou Yang {
836cadaa04SWenyou Yang struct udevice *dev;
846cadaa04SWenyou Yang struct clk clk_dev;
856cadaa04SWenyou Yang ulong clk_rate;
866cadaa04SWenyou Yang int ret;
876cadaa04SWenyou Yang
886cadaa04SWenyou Yang dev = dev_get_parent(clk->dev);
896cadaa04SWenyou Yang
906cadaa04SWenyou Yang ret = clk_get_by_index(dev, 0, &clk_dev);
916cadaa04SWenyou Yang if (ret)
926cadaa04SWenyou Yang return ret;
936cadaa04SWenyou Yang
946cadaa04SWenyou Yang clk_rate = clk_get_rate(&clk_dev);
956cadaa04SWenyou Yang
966cadaa04SWenyou Yang clk_free(&clk_dev);
976cadaa04SWenyou Yang
986cadaa04SWenyou Yang return clk_rate;
999e5935c0SWenyou Yang }
1009e5935c0SWenyou Yang
1016cadaa04SWenyou Yang static struct clk_ops periph_clk_ops = {
1026cadaa04SWenyou Yang .of_xlate = at91_clk_of_xlate,
1036cadaa04SWenyou Yang .enable = periph_clk_enable,
1046cadaa04SWenyou Yang .get_rate = periph_get_rate,
1059e5935c0SWenyou Yang };
1069e5935c0SWenyou Yang
1076cadaa04SWenyou Yang U_BOOT_DRIVER(clk_periph) = {
1086cadaa04SWenyou Yang .name = "periph-clk",
1099e5935c0SWenyou Yang .id = UCLASS_CLK,
1109e5935c0SWenyou Yang .platdata_auto_alloc_size = sizeof(struct pmc_platdata),
1116cadaa04SWenyou Yang .probe = at91_clk_probe,
1126cadaa04SWenyou Yang .ops = &periph_clk_ops,
1139e5935c0SWenyou Yang };
114