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 static int uniphier_clk_enable(struct clk *clk) 18 { 19 struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); 20 struct uniphier_clk_gate_data *gate = priv->socdata->gate; 21 unsigned int nr_gate = priv->socdata->nr_gate; 22 void __iomem *reg; 23 u32 mask, data, tmp; 24 int i; 25 26 for (i = 0; i < nr_gate; i++) { 27 if (gate[i].index != clk->id) 28 continue; 29 30 reg = priv->base + gate[i].reg; 31 mask = gate[i].mask; 32 data = gate[i].data & mask; 33 34 tmp = readl(reg); 35 tmp &= ~mask; 36 tmp |= data & mask; 37 debug("%s: %p: %08x\n", __func__, reg, tmp); 38 writel(tmp, reg); 39 } 40 41 return 0; 42 } 43 44 static ulong uniphier_clk_get_rate(struct clk *clk) 45 { 46 struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); 47 struct uniphier_clk_rate_data *rdata = priv->socdata->rate; 48 unsigned int nr_rdata = priv->socdata->nr_rate; 49 void __iomem *reg; 50 u32 mask, data; 51 ulong matched_rate = 0; 52 int i; 53 54 for (i = 0; i < nr_rdata; i++) { 55 if (rdata[i].index != clk->id) 56 continue; 57 58 if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED) 59 return rdata[i].rate; 60 61 reg = priv->base + rdata[i].reg; 62 mask = rdata[i].mask; 63 data = rdata[i].data & mask; 64 if ((readl(reg) & mask) == data) { 65 if (matched_rate && rdata[i].rate != matched_rate) { 66 printf("failed to get clk rate for insane register values\n"); 67 return -EINVAL; 68 } 69 matched_rate = rdata[i].rate; 70 } 71 } 72 73 debug("%s: rate = %lu\n", __func__, matched_rate); 74 75 return matched_rate; 76 } 77 78 static ulong uniphier_clk_set_rate(struct clk *clk, ulong rate) 79 { 80 struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); 81 struct uniphier_clk_rate_data *rdata = priv->socdata->rate; 82 unsigned int nr_rdata = priv->socdata->nr_rate; 83 void __iomem *reg; 84 u32 mask, data, tmp; 85 ulong best_rate = 0; 86 int i; 87 88 /* first, decide the best match rate */ 89 for (i = 0; i < nr_rdata; i++) { 90 if (rdata[i].index != clk->id) 91 continue; 92 93 if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED) 94 return 0; 95 96 if (rdata[i].rate > best_rate && rdata[i].rate <= rate) 97 best_rate = rdata[i].rate; 98 } 99 100 if (!best_rate) 101 return -ENODEV; 102 103 debug("%s: requested rate = %lu, set rate = %lu\n", __func__, 104 rate, best_rate); 105 106 /* second, really set registers */ 107 for (i = 0; i < nr_rdata; i++) { 108 if (rdata[i].index != clk->id || rdata[i].rate != best_rate) 109 continue; 110 111 reg = priv->base + rdata[i].reg; 112 mask = rdata[i].mask; 113 data = rdata[i].data & mask; 114 115 tmp = readl(reg); 116 tmp &= ~mask; 117 tmp |= data; 118 debug("%s: %p: %08x\n", __func__, reg, tmp); 119 writel(tmp, reg); 120 } 121 122 return best_rate; 123 } 124 125 const struct clk_ops uniphier_clk_ops = { 126 .enable = uniphier_clk_enable, 127 .get_rate = uniphier_clk_get_rate, 128 .set_rate = uniphier_clk_set_rate, 129 }; 130 131 int uniphier_clk_probe(struct udevice *dev) 132 { 133 struct uniphier_clk_priv *priv = dev_get_priv(dev); 134 fdt_addr_t addr; 135 136 addr = dev_get_addr(dev); 137 if (addr == FDT_ADDR_T_NONE) 138 return -EINVAL; 139 140 priv->base = devm_ioremap(dev, addr, SZ_4K); 141 if (!priv->base) 142 return -ENOMEM; 143 144 priv->socdata = (void *)dev_get_driver_data(dev); 145 146 return 0; 147 } 148