1 /* 2 * Copyright (C) 2016 Socionext Inc. 3 * Author: Masahiro Yamada <yamada.masahiro@socionext.com> 4 * 5 * SPDX-License-Identifier: GPL-2.0+ 6 */ 7 8 #include <common.h> 9 #include <linux/bitops.h> 10 #include <linux/io.h> 11 #include <linux/sizes.h> 12 #include <clk-uclass.h> 13 #include <dm/device.h> 14 15 #include "clk-uniphier.h" 16 17 /** 18 * struct uniphier_clk_priv - private data for UniPhier clock driver 19 * 20 * @base: base address of the clock provider 21 * @socdata: SoC specific data 22 */ 23 struct uniphier_clk_priv { 24 void __iomem *base; 25 const struct uniphier_clk_soc_data *socdata; 26 }; 27 28 int uniphier_clk_probe(struct udevice *dev) 29 { 30 struct uniphier_clk_priv *priv = dev_get_priv(dev); 31 fdt_addr_t addr; 32 33 addr = dev_get_addr(dev->parent); 34 if (addr == FDT_ADDR_T_NONE) 35 return -EINVAL; 36 37 priv->base = devm_ioremap(dev, addr, SZ_4K); 38 if (!priv->base) 39 return -ENOMEM; 40 41 priv->socdata = (void *)dev_get_driver_data(dev); 42 43 return 0; 44 } 45 46 static int uniphier_clk_enable(struct clk *clk) 47 { 48 struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); 49 const struct uniphier_clk_gate_data *gate = priv->socdata->gate; 50 unsigned int nr_gate = priv->socdata->nr_gate; 51 void __iomem *reg; 52 u32 mask, data, tmp; 53 int i; 54 55 for (i = 0; i < nr_gate; i++) { 56 if (gate[i].index != clk->id) 57 continue; 58 59 reg = priv->base + gate[i].reg; 60 mask = gate[i].mask; 61 data = gate[i].data & mask; 62 63 tmp = readl(reg); 64 tmp &= ~mask; 65 tmp |= data & mask; 66 debug("%s: %p: %08x\n", __func__, reg, tmp); 67 writel(tmp, reg); 68 } 69 70 return 0; 71 } 72 73 static ulong uniphier_clk_get_rate(struct clk *clk) 74 { 75 struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); 76 const struct uniphier_clk_rate_data *rdata = priv->socdata->rate; 77 unsigned int nr_rdata = priv->socdata->nr_rate; 78 void __iomem *reg; 79 u32 mask, data; 80 ulong matched_rate = 0; 81 int i; 82 83 for (i = 0; i < nr_rdata; i++) { 84 if (rdata[i].index != clk->id) 85 continue; 86 87 if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED) 88 return rdata[i].rate; 89 90 reg = priv->base + rdata[i].reg; 91 mask = rdata[i].mask; 92 data = rdata[i].data & mask; 93 if ((readl(reg) & mask) == data) { 94 if (matched_rate && rdata[i].rate != matched_rate) { 95 printf("failed to get clk rate for insane register values\n"); 96 return -EINVAL; 97 } 98 matched_rate = rdata[i].rate; 99 } 100 } 101 102 debug("%s: rate = %lu\n", __func__, matched_rate); 103 104 return matched_rate; 105 } 106 107 static ulong uniphier_clk_set_rate(struct clk *clk, ulong rate) 108 { 109 struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); 110 const struct uniphier_clk_rate_data *rdata = priv->socdata->rate; 111 unsigned int nr_rdata = priv->socdata->nr_rate; 112 void __iomem *reg; 113 u32 mask, data, tmp; 114 ulong best_rate = 0; 115 int i; 116 117 /* first, decide the best match rate */ 118 for (i = 0; i < nr_rdata; i++) { 119 if (rdata[i].index != clk->id) 120 continue; 121 122 if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED) 123 return 0; 124 125 if (rdata[i].rate > best_rate && rdata[i].rate <= rate) 126 best_rate = rdata[i].rate; 127 } 128 129 if (!best_rate) 130 return -ENODEV; 131 132 debug("%s: requested rate = %lu, set rate = %lu\n", __func__, 133 rate, best_rate); 134 135 /* second, really set registers */ 136 for (i = 0; i < nr_rdata; i++) { 137 if (rdata[i].index != clk->id || rdata[i].rate != best_rate) 138 continue; 139 140 reg = priv->base + rdata[i].reg; 141 mask = rdata[i].mask; 142 data = rdata[i].data & mask; 143 144 tmp = readl(reg); 145 tmp &= ~mask; 146 tmp |= data; 147 debug("%s: %p: %08x\n", __func__, reg, tmp); 148 writel(tmp, reg); 149 } 150 151 return best_rate; 152 } 153 154 const struct clk_ops uniphier_clk_ops = { 155 .enable = uniphier_clk_enable, 156 .get_rate = uniphier_clk_get_rate, 157 .set_rate = uniphier_clk_set_rate, 158 }; 159 160 static const struct udevice_id uniphier_clk_match[] = { 161 { 162 .compatible = "socionext,uniphier-sld3-mio-clock", 163 .data = (ulong)&uniphier_mio_clk_data, 164 }, 165 { 166 .compatible = "socionext,uniphier-ld4-mio-clock", 167 .data = (ulong)&uniphier_mio_clk_data, 168 }, 169 { 170 .compatible = "socionext,uniphier-pro4-mio-clock", 171 .data = (ulong)&uniphier_mio_clk_data, 172 }, 173 { 174 .compatible = "socionext,uniphier-sld8-mio-clock", 175 .data = (ulong)&uniphier_mio_clk_data, 176 }, 177 { 178 .compatible = "socionext,uniphier-pro5-mio-clock", 179 .data = (ulong)&uniphier_mio_clk_data, 180 }, 181 { 182 .compatible = "socionext,uniphier-pxs2-mio-clock", 183 .data = (ulong)&uniphier_mio_clk_data, 184 }, 185 { 186 .compatible = "socionext,uniphier-ld11-mio-clock", 187 .data = (ulong)&uniphier_mio_clk_data, 188 }, 189 { 190 .compatible = "socionext,uniphier-ld20-mio-clock", 191 .data = (ulong)&uniphier_mio_clk_data, 192 }, 193 { /* sentinel */ } 194 }; 195 196 U_BOOT_DRIVER(uniphier_clk) = { 197 .name = "uniphier-clk", 198 .id = UCLASS_CLK, 199 .of_match = uniphier_clk_match, 200 .probe = uniphier_clk_probe, 201 .priv_auto_alloc_size = sizeof(struct uniphier_clk_priv), 202 .ops = &uniphier_clk_ops, 203 }; 204