Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Hisilicon clock driver
0004  *
0005  * Copyright (c) 2013-2017 Hisilicon Limited.
0006  * Copyright (c) 2017 Linaro Limited.
0007  *
0008  * Author: Kai Zhao <zhaokai1@hisilicon.com>
0009  *      Tao Wang <kevin.wangtao@hisilicon.com>
0010  *      Leo Yan <leo.yan@linaro.org>
0011  */
0012 
0013 #include <linux/clk-provider.h>
0014 #include <linux/device.h>
0015 #include <linux/err.h>
0016 #include <linux/init.h>
0017 #include <linux/io.h>
0018 #include <linux/mailbox_client.h>
0019 #include <linux/module.h>
0020 #include <linux/of.h>
0021 #include <linux/platform_device.h>
0022 #include <dt-bindings/clock/hi3660-clock.h>
0023 
0024 #define HI3660_STUB_CLOCK_DATA      (0x70)
0025 #define MHZ             (1000 * 1000)
0026 
0027 #define DEFINE_CLK_STUB(_id, _cmd, _name)           \
0028     {                           \
0029         .id = (_id),                    \
0030         .cmd = (_cmd),                  \
0031         .hw.init = &(struct clk_init_data) {        \
0032             .name = #_name,             \
0033             .ops = &hi3660_stub_clk_ops,        \
0034             .num_parents = 0,           \
0035             .flags = CLK_GET_RATE_NOCACHE,      \
0036         },                      \
0037     },
0038 
0039 #define to_stub_clk(_hw) container_of(_hw, struct hi3660_stub_clk, hw)
0040 
0041 struct hi3660_stub_clk_chan {
0042     struct mbox_client cl;
0043     struct mbox_chan *mbox;
0044 };
0045 
0046 struct hi3660_stub_clk {
0047     unsigned int id;
0048     struct clk_hw hw;
0049     unsigned int cmd;
0050     unsigned int msg[8];
0051     unsigned int rate;
0052 };
0053 
0054 static void __iomem *freq_reg;
0055 static struct hi3660_stub_clk_chan stub_clk_chan;
0056 
0057 static unsigned long hi3660_stub_clk_recalc_rate(struct clk_hw *hw,
0058                          unsigned long parent_rate)
0059 {
0060     struct hi3660_stub_clk *stub_clk = to_stub_clk(hw);
0061 
0062     /*
0063      * LPM3 writes back the CPU frequency in shared SRAM so read
0064      * back the frequency.
0065      */
0066     stub_clk->rate = readl(freq_reg + (stub_clk->id << 2)) * MHZ;
0067     return stub_clk->rate;
0068 }
0069 
0070 static long hi3660_stub_clk_round_rate(struct clk_hw *hw, unsigned long rate,
0071                        unsigned long *prate)
0072 {
0073     /*
0074      * LPM3 handles rate rounding so just return whatever
0075      * rate is requested.
0076      */
0077     return rate;
0078 }
0079 
0080 static int hi3660_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate,
0081                     unsigned long parent_rate)
0082 {
0083     struct hi3660_stub_clk *stub_clk = to_stub_clk(hw);
0084 
0085     stub_clk->msg[0] = stub_clk->cmd;
0086     stub_clk->msg[1] = rate / MHZ;
0087 
0088     dev_dbg(stub_clk_chan.cl.dev, "set rate msg[0]=0x%x msg[1]=0x%x\n",
0089         stub_clk->msg[0], stub_clk->msg[1]);
0090 
0091     mbox_send_message(stub_clk_chan.mbox, stub_clk->msg);
0092     mbox_client_txdone(stub_clk_chan.mbox, 0);
0093 
0094     stub_clk->rate = rate;
0095     return 0;
0096 }
0097 
0098 static const struct clk_ops hi3660_stub_clk_ops = {
0099     .recalc_rate    = hi3660_stub_clk_recalc_rate,
0100     .round_rate     = hi3660_stub_clk_round_rate,
0101     .set_rate       = hi3660_stub_clk_set_rate,
0102 };
0103 
0104 static struct hi3660_stub_clk hi3660_stub_clks[HI3660_CLK_STUB_NUM] = {
0105     DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER0, 0x0001030A, "cpu-cluster.0")
0106     DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER1, 0x0002030A, "cpu-cluster.1")
0107     DEFINE_CLK_STUB(HI3660_CLK_STUB_GPU, 0x0003030A, "clk-g3d")
0108     DEFINE_CLK_STUB(HI3660_CLK_STUB_DDR, 0x00040309, "clk-ddrc")
0109 };
0110 
0111 static struct clk_hw *hi3660_stub_clk_hw_get(struct of_phandle_args *clkspec,
0112                          void *data)
0113 {
0114     unsigned int idx = clkspec->args[0];
0115 
0116     if (idx >= HI3660_CLK_STUB_NUM) {
0117         pr_err("%s: invalid index %u\n", __func__, idx);
0118         return ERR_PTR(-EINVAL);
0119     }
0120 
0121     return &hi3660_stub_clks[idx].hw;
0122 }
0123 
0124 static int hi3660_stub_clk_probe(struct platform_device *pdev)
0125 {
0126     struct device *dev = &pdev->dev;
0127     struct resource *res;
0128     unsigned int i;
0129     int ret;
0130 
0131     /* Use mailbox client without blocking */
0132     stub_clk_chan.cl.dev = dev;
0133     stub_clk_chan.cl.tx_done = NULL;
0134     stub_clk_chan.cl.tx_block = false;
0135     stub_clk_chan.cl.knows_txdone = false;
0136 
0137     /* Allocate mailbox channel */
0138     stub_clk_chan.mbox = mbox_request_channel(&stub_clk_chan.cl, 0);
0139     if (IS_ERR(stub_clk_chan.mbox))
0140         return PTR_ERR(stub_clk_chan.mbox);
0141 
0142     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0143     if (!res)
0144         return -EINVAL;
0145     freq_reg = devm_ioremap(dev, res->start, resource_size(res));
0146     if (!freq_reg)
0147         return -ENOMEM;
0148 
0149     freq_reg += HI3660_STUB_CLOCK_DATA;
0150 
0151     for (i = 0; i < HI3660_CLK_STUB_NUM; i++) {
0152         ret = devm_clk_hw_register(&pdev->dev, &hi3660_stub_clks[i].hw);
0153         if (ret)
0154             return ret;
0155     }
0156 
0157     return devm_of_clk_add_hw_provider(&pdev->dev, hi3660_stub_clk_hw_get,
0158                        hi3660_stub_clks);
0159 }
0160 
0161 static const struct of_device_id hi3660_stub_clk_of_match[] = {
0162     { .compatible = "hisilicon,hi3660-stub-clk", },
0163     {}
0164 };
0165 
0166 static struct platform_driver hi3660_stub_clk_driver = {
0167     .probe  = hi3660_stub_clk_probe,
0168     .driver = {
0169         .name = "hi3660-stub-clk",
0170         .of_match_table = hi3660_stub_clk_of_match,
0171     },
0172 };
0173 
0174 static int __init hi3660_stub_clk_init(void)
0175 {
0176     return platform_driver_register(&hi3660_stub_clk_driver);
0177 }
0178 subsys_initcall(hi3660_stub_clk_init);