clk-fch.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. // SPDX-License-Identifier: MIT
  2. /*
  3. * clock framework for AMD Stoney based clocks
  4. *
  5. * Copyright 2018 Advanced Micro Devices, Inc.
  6. */
  7. #include <linux/clk.h>
  8. #include <linux/clkdev.h>
  9. #include <linux/clk-provider.h>
  10. #include <linux/platform_data/clk-fch.h>
  11. #include <linux/platform_device.h>
  12. /* Clock Driving Strength 2 register */
  13. #define CLKDRVSTR2 0x28
  14. /* Clock Control 1 register */
  15. #define MISCCLKCNTL1 0x40
  16. /* Auxiliary clock1 enable bit */
  17. #define OSCCLKENB 2
  18. /* 25Mhz auxiliary output clock freq bit */
  19. #define OSCOUT1CLK25MHZ 16
  20. #define ST_CLK_48M 0
  21. #define ST_CLK_25M 1
  22. #define ST_CLK_MUX 2
  23. #define ST_CLK_GATE 3
  24. #define ST_MAX_CLKS 4
  25. #define RV_CLK_48M 0
  26. #define RV_CLK_GATE 1
  27. #define RV_MAX_CLKS 2
  28. static const char * const clk_oscout1_parents[] = { "clk48MHz", "clk25MHz" };
  29. static struct clk_hw *hws[ST_MAX_CLKS];
  30. static int fch_clk_probe(struct platform_device *pdev)
  31. {
  32. struct fch_clk_data *fch_data;
  33. fch_data = dev_get_platdata(&pdev->dev);
  34. if (!fch_data || !fch_data->base)
  35. return -EINVAL;
  36. if (!fch_data->is_rv) {
  37. hws[ST_CLK_48M] = clk_hw_register_fixed_rate(NULL, "clk48MHz",
  38. NULL, 0, 48000000);
  39. hws[ST_CLK_25M] = clk_hw_register_fixed_rate(NULL, "clk25MHz",
  40. NULL, 0, 25000000);
  41. hws[ST_CLK_MUX] = clk_hw_register_mux(NULL, "oscout1_mux",
  42. clk_oscout1_parents, ARRAY_SIZE(clk_oscout1_parents),
  43. 0, fch_data->base + CLKDRVSTR2, OSCOUT1CLK25MHZ, 3, 0,
  44. NULL);
  45. clk_set_parent(hws[ST_CLK_MUX]->clk, hws[ST_CLK_48M]->clk);
  46. hws[ST_CLK_GATE] = clk_hw_register_gate(NULL, "oscout1",
  47. "oscout1_mux", 0, fch_data->base + MISCCLKCNTL1,
  48. OSCCLKENB, CLK_GATE_SET_TO_DISABLE, NULL);
  49. devm_clk_hw_register_clkdev(&pdev->dev, hws[ST_CLK_GATE],
  50. "oscout1", NULL);
  51. } else {
  52. hws[RV_CLK_48M] = clk_hw_register_fixed_rate(NULL, "clk48MHz",
  53. NULL, 0, 48000000);
  54. hws[RV_CLK_GATE] = clk_hw_register_gate(NULL, "oscout1",
  55. "clk48MHz", 0, fch_data->base + MISCCLKCNTL1,
  56. OSCCLKENB, CLK_GATE_SET_TO_DISABLE, NULL);
  57. devm_clk_hw_register_clkdev(&pdev->dev, hws[RV_CLK_GATE],
  58. "oscout1", NULL);
  59. }
  60. return 0;
  61. }
  62. static int fch_clk_remove(struct platform_device *pdev)
  63. {
  64. int i, clks;
  65. struct fch_clk_data *fch_data;
  66. fch_data = dev_get_platdata(&pdev->dev);
  67. clks = fch_data->is_rv ? RV_MAX_CLKS : ST_MAX_CLKS;
  68. for (i = 0; i < clks; i++)
  69. clk_hw_unregister(hws[i]);
  70. return 0;
  71. }
  72. static struct platform_driver fch_clk_driver = {
  73. .driver = {
  74. .name = "clk-fch",
  75. .suppress_bind_attrs = true,
  76. },
  77. .probe = fch_clk_probe,
  78. .remove = fch_clk_remove,
  79. };
  80. builtin_platform_driver(fch_clk_driver);