meson-mx-sdhc-clkc.c (4219B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Amlogic Meson SDHC clock controller 4 * 5 * Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com> 6 */ 7 8#include <linux/clk.h> 9#include <linux/clk-provider.h> 10#include <linux/device.h> 11#include <linux/platform_device.h> 12 13#include "meson-mx-sdhc.h" 14 15struct meson_mx_sdhc_clkc { 16 struct clk_mux src_sel; 17 struct clk_divider div; 18 struct clk_gate mod_clk_en; 19 struct clk_gate tx_clk_en; 20 struct clk_gate rx_clk_en; 21 struct clk_gate sd_clk_en; 22}; 23 24static const struct clk_parent_data meson_mx_sdhc_src_sel_parents[4] = { 25 { .fw_name = "clkin0" }, 26 { .fw_name = "clkin1" }, 27 { .fw_name = "clkin2" }, 28 { .fw_name = "clkin3" }, 29}; 30 31static const struct clk_div_table meson_mx_sdhc_div_table[] = { 32 { .div = 6, .val = 5, }, 33 { .div = 8, .val = 7, }, 34 { .div = 9, .val = 8, }, 35 { .div = 10, .val = 9, }, 36 { .div = 12, .val = 11, }, 37 { .div = 16, .val = 15, }, 38 { .div = 18, .val = 17, }, 39 { .div = 34, .val = 33, }, 40 { .div = 142, .val = 141, }, 41 { .div = 850, .val = 849, }, 42 { .div = 2126, .val = 2125, }, 43 { .div = 4096, .val = 4095, }, 44 { /* sentinel */ } 45}; 46 47static int meson_mx_sdhc_clk_hw_register(struct device *dev, 48 const char *name_suffix, 49 const struct clk_parent_data *parents, 50 unsigned int num_parents, 51 const struct clk_ops *ops, 52 struct clk_hw *hw) 53{ 54 struct clk_init_data init = { }; 55 char clk_name[32]; 56 57 snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(dev), 58 name_suffix); 59 60 init.name = clk_name; 61 init.ops = ops; 62 init.flags = CLK_SET_RATE_PARENT; 63 init.parent_data = parents; 64 init.num_parents = num_parents; 65 66 hw->init = &init; 67 68 return devm_clk_hw_register(dev, hw); 69} 70 71static int meson_mx_sdhc_gate_clk_hw_register(struct device *dev, 72 const char *name_suffix, 73 struct clk_hw *parent, 74 struct clk_hw *hw) 75{ 76 struct clk_parent_data parent_data = { .hw = parent }; 77 78 return meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1, 79 &clk_gate_ops, hw); 80} 81 82int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base, 83 struct clk_bulk_data *clk_bulk_data) 84{ 85 struct clk_parent_data div_parent = { }; 86 struct meson_mx_sdhc_clkc *clkc_data; 87 int ret; 88 89 clkc_data = devm_kzalloc(dev, sizeof(*clkc_data), GFP_KERNEL); 90 if (!clkc_data) 91 return -ENOMEM; 92 93 clkc_data->src_sel.reg = base + MESON_SDHC_CLKC; 94 clkc_data->src_sel.mask = 0x3; 95 clkc_data->src_sel.shift = 16; 96 ret = meson_mx_sdhc_clk_hw_register(dev, "src_sel", 97 meson_mx_sdhc_src_sel_parents, 4, 98 &clk_mux_ops, 99 &clkc_data->src_sel.hw); 100 if (ret) 101 return ret; 102 103 clkc_data->div.reg = base + MESON_SDHC_CLKC; 104 clkc_data->div.shift = 0; 105 clkc_data->div.width = 12; 106 clkc_data->div.table = meson_mx_sdhc_div_table; 107 div_parent.hw = &clkc_data->src_sel.hw; 108 ret = meson_mx_sdhc_clk_hw_register(dev, "div", &div_parent, 1, 109 &clk_divider_ops, 110 &clkc_data->div.hw); 111 if (ret) 112 return ret; 113 114 clkc_data->mod_clk_en.reg = base + MESON_SDHC_CLKC; 115 clkc_data->mod_clk_en.bit_idx = 15; 116 ret = meson_mx_sdhc_gate_clk_hw_register(dev, "mod_clk_on", 117 &clkc_data->div.hw, 118 &clkc_data->mod_clk_en.hw); 119 if (ret) 120 return ret; 121 122 clkc_data->tx_clk_en.reg = base + MESON_SDHC_CLKC; 123 clkc_data->tx_clk_en.bit_idx = 14; 124 ret = meson_mx_sdhc_gate_clk_hw_register(dev, "tx_clk_on", 125 &clkc_data->div.hw, 126 &clkc_data->tx_clk_en.hw); 127 if (ret) 128 return ret; 129 130 clkc_data->rx_clk_en.reg = base + MESON_SDHC_CLKC; 131 clkc_data->rx_clk_en.bit_idx = 13; 132 ret = meson_mx_sdhc_gate_clk_hw_register(dev, "rx_clk_on", 133 &clkc_data->div.hw, 134 &clkc_data->rx_clk_en.hw); 135 if (ret) 136 return ret; 137 138 clkc_data->sd_clk_en.reg = base + MESON_SDHC_CLKC; 139 clkc_data->sd_clk_en.bit_idx = 12; 140 ret = meson_mx_sdhc_gate_clk_hw_register(dev, "sd_clk_on", 141 &clkc_data->div.hw, 142 &clkc_data->sd_clk_en.hw); 143 if (ret) 144 return ret; 145 146 /* 147 * TODO: Replace clk_hw.clk with devm_clk_hw_get_clk() once that is 148 * available. 149 */ 150 clk_bulk_data[0].clk = clkc_data->mod_clk_en.hw.clk; 151 clk_bulk_data[1].clk = clkc_data->sd_clk_en.hw.clk; 152 clk_bulk_data[2].clk = clkc_data->tx_clk_en.hw.clk; 153 clk_bulk_data[3].clk = clkc_data->rx_clk_en.hw.clk; 154 155 return 0; 156}