1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Generic clock support for AT91 architectures. 4 * 5 * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries 6 * 7 * Author: Claudiu Beznea <claudiu.beznea@microchip.com> 8 * 9 * Based on drivers/clk/at91/clk-generated.c from Linux. 10 */ 11#include <common.h> 12#include <clk-uclass.h> 13#include <dm.h> 14#include <linux/io.h> 15#include <linux/clk-provider.h> 16#include <linux/clk/at91_pmc.h> 17 18#include "pmc.h" 19 20#define UBOOT_DM_CLK_AT91_GCK "at91-gck-clk" 21 22#define GENERATED_MAX_DIV 255 23 24struct clk_gck { 25 void __iomem *base; 26 const u32 *clk_mux_table; 27 const u32 *mux_table; 28 const struct clk_pcr_layout *layout; 29 struct clk_range range; 30 struct clk clk; 31 u32 num_parents; 32 u32 id; 33}; 34 35#define to_clk_gck(_c) container_of(_c, struct clk_gck, clk) 36 37static int clk_gck_enable(struct clk *clk) 38{ 39 struct clk_gck *gck = to_clk_gck(clk); 40 41 pmc_write(gck->base, gck->layout->offset, 42 (gck->id & gck->layout->pid_mask)); 43 pmc_update_bits(gck->base, gck->layout->offset, 44 gck->layout->cmd | AT91_PMC_PCR_GCKEN, 45 gck->layout->cmd | AT91_PMC_PCR_GCKEN); 46 47 return 0; 48} 49 50static int clk_gck_disable(struct clk *clk) 51{ 52 struct clk_gck *gck = to_clk_gck(clk); 53 54 pmc_write(gck->base, gck->layout->offset, 55 (gck->id & gck->layout->pid_mask)); 56 pmc_update_bits(gck->base, gck->layout->offset, 57 gck->layout->cmd | AT91_PMC_PCR_GCKEN, 58 gck->layout->cmd); 59 60 return 0; 61} 62 63static int clk_gck_set_parent(struct clk *clk, struct clk *parent) 64{ 65 struct clk_gck *gck = to_clk_gck(clk); 66 int index; 67 68 index = at91_clk_mux_val_to_index(gck->clk_mux_table, gck->num_parents, 69 parent->id); 70 if (index < 0) 71 return index; 72 73 index = at91_clk_mux_index_to_val(gck->mux_table, gck->num_parents, 74 index); 75 if (index < 0) 76 return index; 77 78 pmc_write(gck->base, gck->layout->offset, 79 (gck->id & gck->layout->pid_mask)); 80 pmc_update_bits(gck->base, gck->layout->offset, 81 gck->layout->gckcss_mask | gck->layout->cmd, 82 (index << (ffs(gck->layout->gckcss_mask) - 1)) | 83 gck->layout->cmd); 84 85 return 0; 86} 87 88static ulong clk_gck_set_rate(struct clk *clk, ulong rate) 89{ 90 struct clk_gck *gck = to_clk_gck(clk); 91 ulong parent_rate = clk_get_parent_rate(clk); 92 u32 div; 93 94 if (!rate || !parent_rate) 95 return 0; 96 97 if (gck->range.max && rate > gck->range.max) 98 return 0; 99 100 div = DIV_ROUND_CLOSEST(parent_rate, rate); 101 if (div > GENERATED_MAX_DIV + 1 || !div) 102 return 0; 103 104 pmc_write(gck->base, gck->layout->offset, 105 (gck->id & gck->layout->pid_mask)); 106 pmc_update_bits(gck->base, gck->layout->offset, 107 AT91_PMC_PCR_GCKDIV_MASK | gck->layout->cmd, 108 ((div - 1) << (ffs(AT91_PMC_PCR_GCKDIV_MASK) - 1)) | 109 gck->layout->cmd); 110 111 return parent_rate / div; 112} 113 114static ulong clk_gck_get_rate(struct clk *clk) 115{ 116 struct clk_gck *gck = to_clk_gck(clk); 117 ulong parent_rate = clk_get_parent_rate(clk); 118 u32 val, div; 119 120 if (!parent_rate) 121 return 0; 122 123 pmc_write(gck->base, gck->layout->offset, 124 (gck->id & gck->layout->pid_mask)); 125 pmc_read(gck->base, gck->layout->offset, &val); 126 127 div = (val & AT91_PMC_PCR_GCKDIV_MASK) >> 128 (ffs(AT91_PMC_PCR_GCKDIV_MASK) - 1); 129 130 return parent_rate / (div + 1); 131} 132 133static const struct clk_ops gck_ops = { 134 .enable = clk_gck_enable, 135 .disable = clk_gck_disable, 136 .set_parent = clk_gck_set_parent, 137 .set_rate = clk_gck_set_rate, 138 .get_rate = clk_gck_get_rate, 139}; 140 141struct clk * 142at91_clk_register_generic(void __iomem *base, 143 const struct clk_pcr_layout *layout, 144 const char *name, const char * const *parent_names, 145 const u32 *clk_mux_table, const u32 *mux_table, 146 u8 num_parents, u8 id, 147 const struct clk_range *range) 148{ 149 struct clk_gck *gck; 150 struct clk *clk; 151 int ret, index; 152 u32 val; 153 154 if (!base || !layout || !name || !parent_names || !num_parents || 155 !clk_mux_table || !mux_table || !range) 156 return ERR_PTR(-EINVAL); 157 158 gck = kzalloc(sizeof(*gck), GFP_KERNEL); 159 if (!gck) 160 return ERR_PTR(-ENOMEM); 161 162 gck->id = id; 163 gck->base = base; 164 gck->range = *range; 165 gck->layout = layout; 166 gck->clk_mux_table = clk_mux_table; 167 gck->mux_table = mux_table; 168 gck->num_parents = num_parents; 169 170 clk = &gck->clk; 171 clk->flags = CLK_GET_RATE_NOCACHE; 172 173 pmc_write(gck->base, gck->layout->offset, 174 (gck->id & gck->layout->pid_mask)); 175 pmc_read(gck->base, gck->layout->offset, &val); 176 177 val = (val & gck->layout->gckcss_mask) >> 178 (ffs(gck->layout->gckcss_mask) - 1); 179 180 index = at91_clk_mux_val_to_index(gck->mux_table, gck->num_parents, 181 val); 182 if (index < 0) { 183 kfree(gck); 184 return ERR_PTR(index); 185 } 186 187 ret = clk_register(clk, UBOOT_DM_CLK_AT91_GCK, name, 188 parent_names[index]); 189 if (ret) { 190 kfree(gck); 191 clk = ERR_PTR(ret); 192 } 193 194 return clk; 195} 196 197U_BOOT_DRIVER(at91_gck_clk) = { 198 .name = UBOOT_DM_CLK_AT91_GCK, 199 .id = UCLASS_CLK, 200 .ops = &gck_ops, 201 .flags = DM_FLAG_PRE_RELOC, 202}; 203