Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.8-rc7 447 lines 12 kB view raw
1/* 2 * AmLogic S805 / Meson8b Clock Controller Driver 3 * 4 * Copyright (c) 2015 Endless Mobile, Inc. 5 * Author: Carlo Caione <carlo@endlessm.com> 6 * 7 * Copyright (c) 2016 BayLibre, Inc. 8 * Michael Turquette <mturquette@baylibre.com> 9 * 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms and conditions of the GNU General Public License, 12 * version 2, as published by the Free Software Foundation. 13 * 14 * This program is distributed in the hope it will be useful, but WITHOUT 15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17 * more details. 18 * 19 * You should have received a copy of the GNU General Public License along with 20 * this program. If not, see <http://www.gnu.org/licenses/>. 21 */ 22 23#include <linux/clk.h> 24#include <linux/clk-provider.h> 25#include <linux/of_address.h> 26#include <dt-bindings/clock/meson8b-clkc.h> 27#include <linux/platform_device.h> 28#include <linux/init.h> 29 30#include "clkc.h" 31 32/* 33 * Clock controller register offsets 34 * 35 * Register offsets from the HardKernel[0] data sheet are listed in comment 36 * blocks below. Those offsets must be multiplied by 4 before adding them to 37 * the base address to get the right value 38 * 39 * [0] http://dn.odroid.com/S805/Datasheet/S805_Datasheet%20V0.8%2020150126.pdf 40 */ 41#define MESON8B_REG_SYS_CPU_CNTL1 0x015c /* 0x57 offset in data sheet */ 42#define MESON8B_REG_HHI_MPEG 0x0174 /* 0x5d offset in data sheet */ 43#define MESON8B_REG_MALI 0x01b0 /* 0x6c offset in data sheet */ 44#define MESON8B_REG_PLL_FIXED 0x0280 45#define MESON8B_REG_PLL_SYS 0x0300 46#define MESON8B_REG_PLL_VID 0x0320 47 48static DEFINE_SPINLOCK(clk_lock); 49 50static const struct pll_rate_table sys_pll_rate_table[] = { 51 PLL_RATE(312000000, 52, 1, 2), 52 PLL_RATE(336000000, 56, 1, 2), 53 PLL_RATE(360000000, 60, 1, 2), 54 PLL_RATE(384000000, 64, 1, 2), 55 PLL_RATE(408000000, 68, 1, 2), 56 PLL_RATE(432000000, 72, 1, 2), 57 PLL_RATE(456000000, 76, 1, 2), 58 PLL_RATE(480000000, 80, 1, 2), 59 PLL_RATE(504000000, 84, 1, 2), 60 PLL_RATE(528000000, 88, 1, 2), 61 PLL_RATE(552000000, 92, 1, 2), 62 PLL_RATE(576000000, 96, 1, 2), 63 PLL_RATE(600000000, 50, 1, 1), 64 PLL_RATE(624000000, 52, 1, 1), 65 PLL_RATE(648000000, 54, 1, 1), 66 PLL_RATE(672000000, 56, 1, 1), 67 PLL_RATE(696000000, 58, 1, 1), 68 PLL_RATE(720000000, 60, 1, 1), 69 PLL_RATE(744000000, 62, 1, 1), 70 PLL_RATE(768000000, 64, 1, 1), 71 PLL_RATE(792000000, 66, 1, 1), 72 PLL_RATE(816000000, 68, 1, 1), 73 PLL_RATE(840000000, 70, 1, 1), 74 PLL_RATE(864000000, 72, 1, 1), 75 PLL_RATE(888000000, 74, 1, 1), 76 PLL_RATE(912000000, 76, 1, 1), 77 PLL_RATE(936000000, 78, 1, 1), 78 PLL_RATE(960000000, 80, 1, 1), 79 PLL_RATE(984000000, 82, 1, 1), 80 PLL_RATE(1008000000, 84, 1, 1), 81 PLL_RATE(1032000000, 86, 1, 1), 82 PLL_RATE(1056000000, 88, 1, 1), 83 PLL_RATE(1080000000, 90, 1, 1), 84 PLL_RATE(1104000000, 92, 1, 1), 85 PLL_RATE(1128000000, 94, 1, 1), 86 PLL_RATE(1152000000, 96, 1, 1), 87 PLL_RATE(1176000000, 98, 1, 1), 88 PLL_RATE(1200000000, 50, 1, 0), 89 PLL_RATE(1224000000, 51, 1, 0), 90 PLL_RATE(1248000000, 52, 1, 0), 91 PLL_RATE(1272000000, 53, 1, 0), 92 PLL_RATE(1296000000, 54, 1, 0), 93 PLL_RATE(1320000000, 55, 1, 0), 94 PLL_RATE(1344000000, 56, 1, 0), 95 PLL_RATE(1368000000, 57, 1, 0), 96 PLL_RATE(1392000000, 58, 1, 0), 97 PLL_RATE(1416000000, 59, 1, 0), 98 PLL_RATE(1440000000, 60, 1, 0), 99 PLL_RATE(1464000000, 61, 1, 0), 100 PLL_RATE(1488000000, 62, 1, 0), 101 PLL_RATE(1512000000, 63, 1, 0), 102 PLL_RATE(1536000000, 64, 1, 0), 103 { /* sentinel */ }, 104}; 105 106static const struct clk_div_table cpu_div_table[] = { 107 { .val = 1, .div = 1 }, 108 { .val = 2, .div = 2 }, 109 { .val = 3, .div = 3 }, 110 { .val = 2, .div = 4 }, 111 { .val = 3, .div = 6 }, 112 { .val = 4, .div = 8 }, 113 { .val = 5, .div = 10 }, 114 { .val = 6, .div = 12 }, 115 { .val = 7, .div = 14 }, 116 { .val = 8, .div = 16 }, 117 { /* sentinel */ }, 118}; 119 120static struct clk_fixed_rate meson8b_xtal = { 121 .fixed_rate = 24000000, 122 .hw.init = &(struct clk_init_data){ 123 .name = "xtal", 124 .num_parents = 0, 125 .ops = &clk_fixed_rate_ops, 126 }, 127}; 128 129static struct meson_clk_pll meson8b_fixed_pll = { 130 .m = { 131 .reg_off = MESON8B_REG_PLL_FIXED, 132 .shift = 0, 133 .width = 9, 134 }, 135 .n = { 136 .reg_off = MESON8B_REG_PLL_FIXED, 137 .shift = 9, 138 .width = 5, 139 }, 140 .od = { 141 .reg_off = MESON8B_REG_PLL_FIXED, 142 .shift = 16, 143 .width = 2, 144 }, 145 .lock = &clk_lock, 146 .hw.init = &(struct clk_init_data){ 147 .name = "fixed_pll", 148 .ops = &meson_clk_pll_ro_ops, 149 .parent_names = (const char *[]){ "xtal" }, 150 .num_parents = 1, 151 .flags = CLK_GET_RATE_NOCACHE, 152 }, 153}; 154 155static struct meson_clk_pll meson8b_vid_pll = { 156 .m = { 157 .reg_off = MESON8B_REG_PLL_VID, 158 .shift = 0, 159 .width = 9, 160 }, 161 .n = { 162 .reg_off = MESON8B_REG_PLL_VID, 163 .shift = 9, 164 .width = 5, 165 }, 166 .od = { 167 .reg_off = MESON8B_REG_PLL_VID, 168 .shift = 16, 169 .width = 2, 170 }, 171 .lock = &clk_lock, 172 .hw.init = &(struct clk_init_data){ 173 .name = "vid_pll", 174 .ops = &meson_clk_pll_ro_ops, 175 .parent_names = (const char *[]){ "xtal" }, 176 .num_parents = 1, 177 .flags = CLK_GET_RATE_NOCACHE, 178 }, 179}; 180 181static struct meson_clk_pll meson8b_sys_pll = { 182 .m = { 183 .reg_off = MESON8B_REG_PLL_SYS, 184 .shift = 0, 185 .width = 9, 186 }, 187 .n = { 188 .reg_off = MESON8B_REG_PLL_SYS, 189 .shift = 9, 190 .width = 5, 191 }, 192 .od = { 193 .reg_off = MESON8B_REG_PLL_SYS, 194 .shift = 16, 195 .width = 2, 196 }, 197 .rate_table = sys_pll_rate_table, 198 .rate_count = ARRAY_SIZE(sys_pll_rate_table), 199 .lock = &clk_lock, 200 .hw.init = &(struct clk_init_data){ 201 .name = "sys_pll", 202 .ops = &meson_clk_pll_ops, 203 .parent_names = (const char *[]){ "xtal" }, 204 .num_parents = 1, 205 .flags = CLK_GET_RATE_NOCACHE, 206 }, 207}; 208 209static struct clk_fixed_factor meson8b_fclk_div2 = { 210 .mult = 1, 211 .div = 2, 212 .hw.init = &(struct clk_init_data){ 213 .name = "fclk_div2", 214 .ops = &clk_fixed_factor_ops, 215 .parent_names = (const char *[]){ "fixed_pll" }, 216 .num_parents = 1, 217 }, 218}; 219 220static struct clk_fixed_factor meson8b_fclk_div3 = { 221 .mult = 1, 222 .div = 3, 223 .hw.init = &(struct clk_init_data){ 224 .name = "fclk_div3", 225 .ops = &clk_fixed_factor_ops, 226 .parent_names = (const char *[]){ "fixed_pll" }, 227 .num_parents = 1, 228 }, 229}; 230 231static struct clk_fixed_factor meson8b_fclk_div4 = { 232 .mult = 1, 233 .div = 4, 234 .hw.init = &(struct clk_init_data){ 235 .name = "fclk_div4", 236 .ops = &clk_fixed_factor_ops, 237 .parent_names = (const char *[]){ "fixed_pll" }, 238 .num_parents = 1, 239 }, 240}; 241 242static struct clk_fixed_factor meson8b_fclk_div5 = { 243 .mult = 1, 244 .div = 5, 245 .hw.init = &(struct clk_init_data){ 246 .name = "fclk_div5", 247 .ops = &clk_fixed_factor_ops, 248 .parent_names = (const char *[]){ "fixed_pll" }, 249 .num_parents = 1, 250 }, 251}; 252 253static struct clk_fixed_factor meson8b_fclk_div7 = { 254 .mult = 1, 255 .div = 7, 256 .hw.init = &(struct clk_init_data){ 257 .name = "fclk_div7", 258 .ops = &clk_fixed_factor_ops, 259 .parent_names = (const char *[]){ "fixed_pll" }, 260 .num_parents = 1, 261 }, 262}; 263 264/* 265 * FIXME cpu clocks and the legacy composite clocks (e.g. clk81) are both PLL 266 * post-dividers and should be modeled with their respective PLLs via the 267 * forthcoming coordinated clock rates feature 268 */ 269static struct meson_clk_cpu meson8b_cpu_clk = { 270 .reg_off = MESON8B_REG_SYS_CPU_CNTL1, 271 .div_table = cpu_div_table, 272 .clk_nb.notifier_call = meson_clk_cpu_notifier_cb, 273 .hw.init = &(struct clk_init_data){ 274 .name = "cpu_clk", 275 .ops = &meson_clk_cpu_ops, 276 .parent_names = (const char *[]){ "sys_pll" }, 277 .num_parents = 1, 278 }, 279}; 280 281static u32 mux_table_clk81[] = { 6, 5, 7 }; 282 283struct clk_mux meson8b_mpeg_clk_sel = { 284 .reg = (void *)MESON8B_REG_HHI_MPEG, 285 .mask = 0x7, 286 .shift = 12, 287 .flags = CLK_MUX_READ_ONLY, 288 .table = mux_table_clk81, 289 .lock = &clk_lock, 290 .hw.init = &(struct clk_init_data){ 291 .name = "mpeg_clk_sel", 292 .ops = &clk_mux_ro_ops, 293 /* 294 * FIXME bits 14:12 selects from 8 possible parents: 295 * xtal, 1'b0 (wtf), fclk_div7, mpll_clkout1, mpll_clkout2, 296 * fclk_div4, fclk_div3, fclk_div5 297 */ 298 .parent_names = (const char *[]){ "fclk_div3", "fclk_div4", 299 "fclk_div5" }, 300 .num_parents = 3, 301 .flags = (CLK_SET_RATE_NO_REPARENT | CLK_IGNORE_UNUSED), 302 }, 303}; 304 305struct clk_divider meson8b_mpeg_clk_div = { 306 .reg = (void *)MESON8B_REG_HHI_MPEG, 307 .shift = 0, 308 .width = 7, 309 .lock = &clk_lock, 310 .hw.init = &(struct clk_init_data){ 311 .name = "mpeg_clk_div", 312 .ops = &clk_divider_ops, 313 .parent_names = (const char *[]){ "mpeg_clk_sel" }, 314 .num_parents = 1, 315 .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), 316 }, 317}; 318 319struct clk_gate meson8b_clk81 = { 320 .reg = (void *)MESON8B_REG_HHI_MPEG, 321 .bit_idx = 7, 322 .lock = &clk_lock, 323 .hw.init = &(struct clk_init_data){ 324 .name = "clk81", 325 .ops = &clk_gate_ops, 326 .parent_names = (const char *[]){ "mpeg_clk_div" }, 327 .num_parents = 1, 328 .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), 329 }, 330}; 331 332static struct clk_hw_onecell_data meson8b_hw_onecell_data = { 333 .hws = { 334 [CLKID_XTAL] = &meson8b_xtal.hw, 335 [CLKID_PLL_FIXED] = &meson8b_fixed_pll.hw, 336 [CLKID_PLL_VID] = &meson8b_vid_pll.hw, 337 [CLKID_PLL_SYS] = &meson8b_sys_pll.hw, 338 [CLKID_FCLK_DIV2] = &meson8b_fclk_div2.hw, 339 [CLKID_FCLK_DIV3] = &meson8b_fclk_div3.hw, 340 [CLKID_FCLK_DIV4] = &meson8b_fclk_div4.hw, 341 [CLKID_FCLK_DIV5] = &meson8b_fclk_div5.hw, 342 [CLKID_FCLK_DIV7] = &meson8b_fclk_div7.hw, 343 [CLKID_CPUCLK] = &meson8b_cpu_clk.hw, 344 [CLKID_MPEG_SEL] = &meson8b_mpeg_clk_sel.hw, 345 [CLKID_MPEG_DIV] = &meson8b_mpeg_clk_div.hw, 346 [CLKID_CLK81] = &meson8b_clk81.hw, 347 }, 348 .num = CLK_NR_CLKS, 349}; 350 351static struct meson_clk_pll *const meson8b_clk_plls[] = { 352 &meson8b_fixed_pll, 353 &meson8b_vid_pll, 354 &meson8b_sys_pll, 355}; 356 357static int meson8b_clkc_probe(struct platform_device *pdev) 358{ 359 void __iomem *clk_base; 360 int ret, clkid, i; 361 struct clk_hw *parent_hw; 362 struct clk *parent_clk; 363 struct device *dev = &pdev->dev; 364 365 /* Generic clocks and PLLs */ 366 clk_base = of_iomap(dev->of_node, 1); 367 if (!clk_base) { 368 pr_err("%s: Unable to map clk base\n", __func__); 369 return -ENXIO; 370 } 371 372 /* Populate base address for PLLs */ 373 for (i = 0; i < ARRAY_SIZE(meson8b_clk_plls); i++) 374 meson8b_clk_plls[i]->base = clk_base; 375 376 /* Populate the base address for CPU clk */ 377 meson8b_cpu_clk.base = clk_base; 378 379 /* Populate the base address for the MPEG clks */ 380 meson8b_mpeg_clk_sel.reg = clk_base + (u32)meson8b_mpeg_clk_sel.reg; 381 meson8b_mpeg_clk_div.reg = clk_base + (u32)meson8b_mpeg_clk_div.reg; 382 meson8b_clk81.reg = clk_base + (u32)meson8b_clk81.reg; 383 384 /* 385 * register all clks 386 * CLKID_UNUSED = 0, so skip it and start with CLKID_XTAL = 1 387 */ 388 for (clkid = CLKID_XTAL; clkid < CLK_NR_CLKS; clkid++) { 389 /* array might be sparse */ 390 if (!meson8b_hw_onecell_data.hws[clkid]) 391 continue; 392 393 /* FIXME convert to devm_clk_register */ 394 ret = devm_clk_hw_register(dev, meson8b_hw_onecell_data.hws[clkid]); 395 if (ret) 396 goto iounmap; 397 } 398 399 /* 400 * Register CPU clk notifier 401 * 402 * FIXME this is wrong for a lot of reasons. First, the muxes should be 403 * struct clk_hw objects. Second, we shouldn't program the muxes in 404 * notifier handlers. The tricky programming sequence will be handled 405 * by the forthcoming coordinated clock rates mechanism once that 406 * feature is released. 407 * 408 * Furthermore, looking up the parent this way is terrible. At some 409 * point we will stop allocating a default struct clk when registering 410 * a new clk_hw, and this hack will no longer work. Releasing the ccr 411 * feature before that time solves the problem :-) 412 */ 413 parent_hw = clk_hw_get_parent(&meson8b_cpu_clk.hw); 414 parent_clk = parent_hw->clk; 415 ret = clk_notifier_register(parent_clk, &meson8b_cpu_clk.clk_nb); 416 if (ret) { 417 pr_err("%s: failed to register clock notifier for cpu_clk\n", 418 __func__); 419 goto iounmap; 420 } 421 422 return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, 423 &meson8b_hw_onecell_data); 424 425iounmap: 426 iounmap(clk_base); 427 return ret; 428} 429 430static const struct of_device_id meson8b_clkc_match_table[] = { 431 { .compatible = "amlogic,meson8b-clkc" }, 432 { } 433}; 434 435static struct platform_driver meson8b_driver = { 436 .probe = meson8b_clkc_probe, 437 .driver = { 438 .name = "meson8b-clkc", 439 .of_match_table = meson8b_clkc_match_table, 440 }, 441}; 442 443static int __init meson8b_clkc_init(void) 444{ 445 return platform_driver_register(&meson8b_driver); 446} 447device_initcall(meson8b_clkc_init);