1 /* 2 * Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com> 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <mapmem.h> 9 #include <linux/bitops.h> 10 #include <linux/io.h> 11 #include <clk.h> 12 #include <dm/device.h> 13 14 #include "clk-uniphier.h" 15 16 DECLARE_GLOBAL_DATA_PTR; 17 18 static int uniphier_clk_enable(struct udevice *dev, int index) 19 { 20 struct uniphier_clk_priv *priv = dev_get_priv(dev); 21 struct uniphier_clk_gate_data *gate = priv->socdata->gate; 22 unsigned int nr_gate = priv->socdata->nr_gate; 23 void __iomem *reg; 24 u32 mask, data, tmp; 25 int i; 26 27 for (i = 0; i < nr_gate; i++) { 28 if (gate[i].index != index) 29 continue; 30 31 reg = priv->base + gate[i].reg; 32 mask = gate[i].mask; 33 data = gate[i].data & mask; 34 35 tmp = readl(reg); 36 tmp &= ~mask; 37 tmp |= data & mask; 38 debug("%s: %p: %08x\n", __func__, reg, tmp); 39 writel(tmp, reg); 40 } 41 42 return 0; 43 } 44 45 static ulong uniphier_clk_get_rate(struct udevice *dev, int index) 46 { 47 struct uniphier_clk_priv *priv = dev_get_priv(dev); 48 struct uniphier_clk_rate_data *rdata = priv->socdata->rate; 49 unsigned int nr_rdata = priv->socdata->nr_rate; 50 void __iomem *reg; 51 u32 mask, data; 52 ulong matched_rate = 0; 53 int i; 54 55 for (i = 0; i < nr_rdata; i++) { 56 if (rdata[i].index != index) 57 continue; 58 59 if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED) 60 return rdata[i].rate; 61 62 reg = priv->base + rdata[i].reg; 63 mask = rdata[i].mask; 64 data = rdata[i].data & mask; 65 if ((readl(reg) & mask) == data) { 66 if (matched_rate && rdata[i].rate != matched_rate) { 67 printf("failed to get clk rate for insane register values\n"); 68 return -EINVAL; 69 } 70 matched_rate = rdata[i].rate; 71 } 72 } 73 74 debug("%s: rate = %lu\n", __func__, matched_rate); 75 76 return matched_rate; 77 } 78 79 static ulong uniphier_clk_set_rate(struct udevice *dev, int index, ulong rate) 80 { 81 struct uniphier_clk_priv *priv = dev_get_priv(dev); 82 struct uniphier_clk_rate_data *rdata = priv->socdata->rate; 83 unsigned int nr_rdata = priv->socdata->nr_rate; 84 void __iomem *reg; 85 u32 mask, data, tmp; 86 ulong best_rate = 0; 87 int i; 88 89 /* first, decide the best match rate */ 90 for (i = 0; i < nr_rdata; i++) { 91 if (rdata[i].index != index) 92 continue; 93 94 if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED) 95 return 0; 96 97 if (rdata[i].rate > best_rate && rdata[i].rate <= rate) 98 best_rate = rdata[i].rate; 99 } 100 101 if (!best_rate) 102 return -ENODEV; 103 104 debug("%s: requested rate = %lu, set rate = %lu\n", __func__, 105 rate, best_rate); 106 107 /* second, really set registers */ 108 for (i = 0; i < nr_rdata; i++) { 109 if (rdata[i].index != index || rdata[i].rate != best_rate) 110 continue; 111 112 reg = priv->base + rdata[i].reg; 113 mask = rdata[i].mask; 114 data = rdata[i].data & mask; 115 116 tmp = readl(reg); 117 tmp &= ~mask; 118 tmp |= data; 119 debug("%s: %p: %08x\n", __func__, reg, tmp); 120 writel(tmp, reg); 121 } 122 123 return best_rate; 124 } 125 126 const struct clk_ops uniphier_clk_ops = { 127 .enable = uniphier_clk_enable, 128 .get_periph_rate = uniphier_clk_get_rate, 129 .set_periph_rate = uniphier_clk_set_rate, 130 }; 131 132 int uniphier_clk_probe(struct udevice *dev) 133 { 134 struct uniphier_clk_priv *priv = dev_get_priv(dev); 135 fdt_addr_t addr; 136 fdt_size_t size; 137 138 addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", 139 &size); 140 if (addr == FDT_ADDR_T_NONE) 141 return -EINVAL; 142 143 priv->base = map_sysmem(addr, size); 144 if (!priv->base) 145 return -ENOMEM; 146 147 priv->socdata = (void *)dev_get_driver_data(dev); 148 149 return 0; 150 } 151 152 int uniphier_clk_remove(struct udevice *dev) 153 { 154 struct uniphier_clk_priv *priv = dev_get_priv(dev); 155 156 unmap_sysmem(priv->base); 157 158 return 0; 159 } 160