Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Hi6220 stub clock driver
0004  *
0005  * Copyright (c) 2015 Hisilicon Limited.
0006  * Copyright (c) 2015 Linaro Limited.
0007  *
0008  * Author: Leo Yan <leo.yan@linaro.org>
0009  */
0010 
0011 #include <linux/clk-provider.h>
0012 #include <linux/err.h>
0013 #include <linux/kernel.h>
0014 #include <linux/mfd/syscon.h>
0015 #include <linux/mailbox_client.h>
0016 #include <linux/of.h>
0017 #include <linux/of_device.h>
0018 #include <linux/regmap.h>
0019 
0020 /* Stub clocks id */
0021 #define HI6220_STUB_ACPU0       0
0022 #define HI6220_STUB_ACPU1       1
0023 #define HI6220_STUB_GPU         2
0024 #define HI6220_STUB_DDR         5
0025 
0026 /* Mailbox message */
0027 #define HI6220_MBOX_MSG_LEN     8
0028 
0029 #define HI6220_MBOX_FREQ        0xA
0030 #define HI6220_MBOX_CMD_SET     0x3
0031 #define HI6220_MBOX_OBJ_AP      0x0
0032 
0033 /* CPU dynamic frequency scaling */
0034 #define ACPU_DFS_FREQ_MAX       0x1724
0035 #define ACPU_DFS_CUR_FREQ       0x17CC
0036 #define ACPU_DFS_FLAG           0x1B30
0037 #define ACPU_DFS_FREQ_REQ       0x1B34
0038 #define ACPU_DFS_FREQ_LMT       0x1B38
0039 #define ACPU_DFS_LOCK_FLAG      0xAEAEAEAE
0040 
0041 #define to_stub_clk(hw) container_of(hw, struct hi6220_stub_clk, hw)
0042 
0043 struct hi6220_stub_clk {
0044     u32 id;
0045 
0046     struct device *dev;
0047     struct clk_hw hw;
0048 
0049     struct regmap *dfs_map;
0050     struct mbox_client cl;
0051     struct mbox_chan *mbox;
0052 };
0053 
0054 struct hi6220_mbox_msg {
0055     unsigned char type;
0056     unsigned char cmd;
0057     unsigned char obj;
0058     unsigned char src;
0059     unsigned char para[4];
0060 };
0061 
0062 union hi6220_mbox_data {
0063     unsigned int data[HI6220_MBOX_MSG_LEN];
0064     struct hi6220_mbox_msg msg;
0065 };
0066 
0067 static unsigned int hi6220_acpu_get_freq(struct hi6220_stub_clk *stub_clk)
0068 {
0069     unsigned int freq;
0070 
0071     regmap_read(stub_clk->dfs_map, ACPU_DFS_CUR_FREQ, &freq);
0072     return freq;
0073 }
0074 
0075 static int hi6220_acpu_set_freq(struct hi6220_stub_clk *stub_clk,
0076                 unsigned int freq)
0077 {
0078     union hi6220_mbox_data data;
0079 
0080     /* set the frequency in sram */
0081     regmap_write(stub_clk->dfs_map, ACPU_DFS_FREQ_REQ, freq);
0082 
0083     /* compound mailbox message */
0084     data.msg.type = HI6220_MBOX_FREQ;
0085     data.msg.cmd  = HI6220_MBOX_CMD_SET;
0086     data.msg.obj  = HI6220_MBOX_OBJ_AP;
0087     data.msg.src  = HI6220_MBOX_OBJ_AP;
0088 
0089     mbox_send_message(stub_clk->mbox, &data);
0090     return 0;
0091 }
0092 
0093 static int hi6220_acpu_round_freq(struct hi6220_stub_clk *stub_clk,
0094                   unsigned int freq)
0095 {
0096     unsigned int limit_flag, limit_freq = UINT_MAX;
0097     unsigned int max_freq;
0098 
0099     /* check the constrained frequency */
0100     regmap_read(stub_clk->dfs_map, ACPU_DFS_FLAG, &limit_flag);
0101     if (limit_flag == ACPU_DFS_LOCK_FLAG)
0102         regmap_read(stub_clk->dfs_map, ACPU_DFS_FREQ_LMT, &limit_freq);
0103 
0104     /* check the supported maximum frequency */
0105     regmap_read(stub_clk->dfs_map, ACPU_DFS_FREQ_MAX, &max_freq);
0106 
0107     /* calculate the real maximum frequency */
0108     max_freq = min(max_freq, limit_freq);
0109 
0110     if (WARN_ON(freq > max_freq))
0111         freq = max_freq;
0112 
0113     return freq;
0114 }
0115 
0116 static unsigned long hi6220_stub_clk_recalc_rate(struct clk_hw *hw,
0117         unsigned long parent_rate)
0118 {
0119     u32 rate = 0;
0120     struct hi6220_stub_clk *stub_clk = to_stub_clk(hw);
0121 
0122     switch (stub_clk->id) {
0123     case HI6220_STUB_ACPU0:
0124         rate = hi6220_acpu_get_freq(stub_clk);
0125 
0126         /* convert from kHz to Hz */
0127         rate *= 1000;
0128         break;
0129 
0130     default:
0131         dev_err(stub_clk->dev, "%s: un-supported clock id %d\n",
0132             __func__, stub_clk->id);
0133         break;
0134     }
0135 
0136     return rate;
0137 }
0138 
0139 static int hi6220_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate,
0140         unsigned long parent_rate)
0141 {
0142     struct hi6220_stub_clk *stub_clk = to_stub_clk(hw);
0143     unsigned long new_rate = rate / 1000;  /* kHz */
0144     int ret = 0;
0145 
0146     switch (stub_clk->id) {
0147     case HI6220_STUB_ACPU0:
0148         ret = hi6220_acpu_set_freq(stub_clk, new_rate);
0149         if (ret < 0)
0150             return ret;
0151 
0152         break;
0153 
0154     default:
0155         dev_err(stub_clk->dev, "%s: un-supported clock id %d\n",
0156             __func__, stub_clk->id);
0157         break;
0158     }
0159 
0160     pr_debug("%s: set rate=%ldkHz\n", __func__, new_rate);
0161     return ret;
0162 }
0163 
0164 static long hi6220_stub_clk_round_rate(struct clk_hw *hw, unsigned long rate,
0165         unsigned long *parent_rate)
0166 {
0167     struct hi6220_stub_clk *stub_clk = to_stub_clk(hw);
0168     unsigned long new_rate = rate / 1000;  /* kHz */
0169 
0170     switch (stub_clk->id) {
0171     case HI6220_STUB_ACPU0:
0172         new_rate = hi6220_acpu_round_freq(stub_clk, new_rate);
0173 
0174         /* convert from kHz to Hz */
0175         new_rate *= 1000;
0176         break;
0177 
0178     default:
0179         dev_err(stub_clk->dev, "%s: un-supported clock id %d\n",
0180             __func__, stub_clk->id);
0181         break;
0182     }
0183 
0184     return new_rate;
0185 }
0186 
0187 static const struct clk_ops hi6220_stub_clk_ops = {
0188     .recalc_rate    = hi6220_stub_clk_recalc_rate,
0189     .round_rate = hi6220_stub_clk_round_rate,
0190     .set_rate   = hi6220_stub_clk_set_rate,
0191 };
0192 
0193 static int hi6220_stub_clk_probe(struct platform_device *pdev)
0194 {
0195     struct device *dev = &pdev->dev;
0196     struct clk_init_data init;
0197     struct hi6220_stub_clk *stub_clk;
0198     struct clk *clk;
0199     struct device_node *np = pdev->dev.of_node;
0200     int ret;
0201 
0202     stub_clk = devm_kzalloc(dev, sizeof(*stub_clk), GFP_KERNEL);
0203     if (!stub_clk)
0204         return -ENOMEM;
0205 
0206     stub_clk->dfs_map = syscon_regmap_lookup_by_phandle(np,
0207                 "hisilicon,hi6220-clk-sram");
0208     if (IS_ERR(stub_clk->dfs_map)) {
0209         dev_err(dev, "failed to get sram regmap\n");
0210         return PTR_ERR(stub_clk->dfs_map);
0211     }
0212 
0213     stub_clk->hw.init = &init;
0214     stub_clk->dev = dev;
0215     stub_clk->id = HI6220_STUB_ACPU0;
0216 
0217     /* Use mailbox client with blocking mode */
0218     stub_clk->cl.dev = dev;
0219     stub_clk->cl.tx_done = NULL;
0220     stub_clk->cl.tx_block = true;
0221     stub_clk->cl.tx_tout = 500;
0222     stub_clk->cl.knows_txdone = false;
0223 
0224     /* Allocate mailbox channel */
0225     stub_clk->mbox = mbox_request_channel(&stub_clk->cl, 0);
0226     if (IS_ERR(stub_clk->mbox)) {
0227         dev_err(dev, "failed get mailbox channel\n");
0228         return PTR_ERR(stub_clk->mbox);
0229     }
0230 
0231     init.name = "acpu0";
0232     init.ops = &hi6220_stub_clk_ops;
0233     init.num_parents = 0;
0234     init.flags = 0;
0235 
0236     clk = devm_clk_register(dev, &stub_clk->hw);
0237     if (IS_ERR(clk))
0238         return PTR_ERR(clk);
0239 
0240     ret = of_clk_add_provider(np, of_clk_src_simple_get, clk);
0241     if (ret) {
0242         dev_err(dev, "failed to register OF clock provider\n");
0243         return ret;
0244     }
0245 
0246     /* initialize buffer to zero */
0247     regmap_write(stub_clk->dfs_map, ACPU_DFS_FLAG, 0x0);
0248     regmap_write(stub_clk->dfs_map, ACPU_DFS_FREQ_REQ, 0x0);
0249     regmap_write(stub_clk->dfs_map, ACPU_DFS_FREQ_LMT, 0x0);
0250 
0251     dev_dbg(dev, "Registered clock '%s'\n", init.name);
0252     return 0;
0253 }
0254 
0255 static const struct of_device_id hi6220_stub_clk_of_match[] = {
0256     { .compatible = "hisilicon,hi6220-stub-clk", },
0257     {}
0258 };
0259 
0260 static struct platform_driver hi6220_stub_clk_driver = {
0261     .driver = {
0262         .name = "hi6220-stub-clk",
0263         .of_match_table = hi6220_stub_clk_of_match,
0264     },
0265     .probe = hi6220_stub_clk_probe,
0266 };
0267 
0268 static int __init hi6220_stub_clk_init(void)
0269 {
0270     return platform_driver_register(&hi6220_stub_clk_driver);
0271 }
0272 subsys_initcall(hi6220_stub_clk_init);