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