179 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Hisilicon clock driver4 *5 * Copyright (c) 2013-2017 Hisilicon Limited.6 * Copyright (c) 2017 Linaro Limited.7 *8 * Author: Kai Zhao <zhaokai1@hisilicon.com>9 * Tao Wang <kevin.wangtao@hisilicon.com>10 * Leo Yan <leo.yan@linaro.org>11 */12 13#include <linux/clk-provider.h>14#include <linux/device.h>15#include <linux/err.h>16#include <linux/init.h>17#include <linux/io.h>18#include <linux/mailbox_client.h>19#include <linux/module.h>20#include <linux/of.h>21#include <linux/platform_device.h>22#include <dt-bindings/clock/hi3660-clock.h>23 24#define HI3660_STUB_CLOCK_DATA (0x70)25#define MHZ (1000 * 1000)26 27#define DEFINE_CLK_STUB(_id, _cmd, _name) \28 { \29 .id = (_id), \30 .cmd = (_cmd), \31 .hw.init = &(struct clk_init_data) { \32 .name = #_name, \33 .ops = &hi3660_stub_clk_ops, \34 .num_parents = 0, \35 .flags = CLK_GET_RATE_NOCACHE, \36 }, \37 },38 39#define to_stub_clk(_hw) container_of(_hw, struct hi3660_stub_clk, hw)40 41struct hi3660_stub_clk_chan {42 struct mbox_client cl;43 struct mbox_chan *mbox;44};45 46struct hi3660_stub_clk {47 unsigned int id;48 struct clk_hw hw;49 unsigned int cmd;50 unsigned int msg[8];51 unsigned int rate;52};53 54static void __iomem *freq_reg;55static struct hi3660_stub_clk_chan stub_clk_chan;56 57static unsigned long hi3660_stub_clk_recalc_rate(struct clk_hw *hw,58 unsigned long parent_rate)59{60 struct hi3660_stub_clk *stub_clk = to_stub_clk(hw);61 62 /*63 * LPM3 writes back the CPU frequency in shared SRAM so read64 * back the frequency.65 */66 stub_clk->rate = readl(freq_reg + (stub_clk->id << 2)) * MHZ;67 return stub_clk->rate;68}69 70static long hi3660_stub_clk_round_rate(struct clk_hw *hw, unsigned long rate,71 unsigned long *prate)72{73 /*74 * LPM3 handles rate rounding so just return whatever75 * rate is requested.76 */77 return rate;78}79 80static int hi3660_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate,81 unsigned long parent_rate)82{83 struct hi3660_stub_clk *stub_clk = to_stub_clk(hw);84 85 stub_clk->msg[0] = stub_clk->cmd;86 stub_clk->msg[1] = rate / MHZ;87 88 dev_dbg(stub_clk_chan.cl.dev, "set rate msg[0]=0x%x msg[1]=0x%x\n",89 stub_clk->msg[0], stub_clk->msg[1]);90 91 mbox_send_message(stub_clk_chan.mbox, stub_clk->msg);92 mbox_client_txdone(stub_clk_chan.mbox, 0);93 94 stub_clk->rate = rate;95 return 0;96}97 98static const struct clk_ops hi3660_stub_clk_ops = {99 .recalc_rate = hi3660_stub_clk_recalc_rate,100 .round_rate = hi3660_stub_clk_round_rate,101 .set_rate = hi3660_stub_clk_set_rate,102};103 104static struct hi3660_stub_clk hi3660_stub_clks[HI3660_CLK_STUB_NUM] = {105 DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER0, 0x0001030A, "cpu-cluster.0")106 DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER1, 0x0002030A, "cpu-cluster.1")107 DEFINE_CLK_STUB(HI3660_CLK_STUB_GPU, 0x0003030A, "clk-g3d")108 DEFINE_CLK_STUB(HI3660_CLK_STUB_DDR, 0x00040309, "clk-ddrc")109};110 111static struct clk_hw *hi3660_stub_clk_hw_get(struct of_phandle_args *clkspec,112 void *data)113{114 unsigned int idx = clkspec->args[0];115 116 if (idx >= HI3660_CLK_STUB_NUM) {117 pr_err("%s: invalid index %u\n", __func__, idx);118 return ERR_PTR(-EINVAL);119 }120 121 return &hi3660_stub_clks[idx].hw;122}123 124static int hi3660_stub_clk_probe(struct platform_device *pdev)125{126 struct device *dev = &pdev->dev;127 struct resource *res;128 unsigned int i;129 int ret;130 131 /* Use mailbox client without blocking */132 stub_clk_chan.cl.dev = dev;133 stub_clk_chan.cl.tx_done = NULL;134 stub_clk_chan.cl.tx_block = false;135 stub_clk_chan.cl.knows_txdone = false;136 137 /* Allocate mailbox channel */138 stub_clk_chan.mbox = mbox_request_channel(&stub_clk_chan.cl, 0);139 if (IS_ERR(stub_clk_chan.mbox))140 return PTR_ERR(stub_clk_chan.mbox);141 142 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);143 if (!res)144 return -EINVAL;145 freq_reg = devm_ioremap(dev, res->start, resource_size(res));146 if (!freq_reg)147 return -ENOMEM;148 149 freq_reg += HI3660_STUB_CLOCK_DATA;150 151 for (i = 0; i < HI3660_CLK_STUB_NUM; i++) {152 ret = devm_clk_hw_register(&pdev->dev, &hi3660_stub_clks[i].hw);153 if (ret)154 return ret;155 }156 157 return devm_of_clk_add_hw_provider(&pdev->dev, hi3660_stub_clk_hw_get,158 hi3660_stub_clks);159}160 161static const struct of_device_id hi3660_stub_clk_of_match[] = {162 { .compatible = "hisilicon,hi3660-stub-clk", },163 {}164};165 166static struct platform_driver hi3660_stub_clk_driver = {167 .probe = hi3660_stub_clk_probe,168 .driver = {169 .name = "hi3660-stub-clk",170 .of_match_table = hi3660_stub_clk_of_match,171 },172};173 174static int __init hi3660_stub_clk_init(void)175{176 return platform_driver_register(&hi3660_stub_clk_driver);177}178subsys_initcall(hi3660_stub_clk_init);179