1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Programmable 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-programmable.c from Linux. 10 */ 11#include <common.h> 12#include <clk-uclass.h> 13#include <dm.h> 14#include <linux/clk-provider.h> 15#include <linux/clk/at91_pmc.h> 16 17#include "pmc.h" 18 19#define UBOOT_DM_CLK_AT91_PROG "at91-prog-clk" 20 21#define PROG_ID_MAX 7 22 23#define PROG_STATUS_MASK(id) (1 << ((id) + 8)) 24#define PROG_PRES(_l, _p) (((_p) >> (_l)->pres_shift) & (_l)->pres_mask) 25#define PROG_MAX_RM9200_CSS 3 26 27struct clk_programmable { 28 void __iomem *base; 29 const u32 *clk_mux_table; 30 const u32 *mux_table; 31 const struct clk_programmable_layout *layout; 32 u32 num_parents; 33 struct clk clk; 34 u8 id; 35}; 36 37#define to_clk_programmable(_c) container_of(_c, struct clk_programmable, clk) 38 39static ulong clk_programmable_get_rate(struct clk *clk) 40{ 41 struct clk_programmable *prog = to_clk_programmable(clk); 42 const struct clk_programmable_layout *layout = prog->layout; 43 ulong rate, parent_rate = clk_get_parent_rate(clk); 44 unsigned int pckr; 45 46 pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &pckr); 47 48 if (layout->is_pres_direct) 49 rate = parent_rate / (PROG_PRES(layout, pckr) + 1); 50 else 51 rate = parent_rate >> PROG_PRES(layout, pckr); 52 53 return rate; 54} 55 56static int clk_programmable_set_parent(struct clk *clk, struct clk *parent) 57{ 58 struct clk_programmable *prog = to_clk_programmable(clk); 59 const struct clk_programmable_layout *layout = prog->layout; 60 unsigned int mask = layout->css_mask; 61 int index; 62 63 index = at91_clk_mux_val_to_index(prog->clk_mux_table, 64 prog->num_parents, parent->id); 65 if (index < 0) 66 return index; 67 68 index = at91_clk_mux_index_to_val(prog->mux_table, prog->num_parents, 69 index); 70 if (index < 0) 71 return index; 72 73 if (layout->have_slck_mck) 74 mask |= AT91_PMC_CSSMCK_MCK; 75 76 if (index > layout->css_mask) { 77 if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck) 78 return -EINVAL; 79 80 index |= AT91_PMC_CSSMCK_MCK; 81 } 82 83 pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id), mask, index); 84 85 return 0; 86} 87 88static ulong clk_programmable_set_rate(struct clk *clk, ulong rate) 89{ 90 struct clk_programmable *prog = to_clk_programmable(clk); 91 const struct clk_programmable_layout *layout = prog->layout; 92 ulong parent_rate = clk_get_parent_rate(clk); 93 ulong div = parent_rate / rate; 94 int shift = 0; 95 96 if (!parent_rate || !div) 97 return -EINVAL; 98 99 if (layout->is_pres_direct) { 100 shift = div - 1; 101 102 if (shift > layout->pres_mask) 103 return -EINVAL; 104 } else { 105 shift = fls(div) - 1; 106 107 if (div != (1 << shift)) 108 return -EINVAL; 109 110 if (shift >= layout->pres_mask) 111 return -EINVAL; 112 } 113 114 pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id), 115 layout->pres_mask << layout->pres_shift, 116 shift << layout->pres_shift); 117 118 if (layout->is_pres_direct) 119 return (parent_rate / shift + 1); 120 121 return parent_rate >> shift; 122} 123 124static const struct clk_ops programmable_ops = { 125 .get_rate = clk_programmable_get_rate, 126 .set_parent = clk_programmable_set_parent, 127 .set_rate = clk_programmable_set_rate, 128}; 129 130struct clk *at91_clk_register_programmable(void __iomem *base, const char *name, 131 const char *const *parent_names, u8 num_parents, u8 id, 132 const struct clk_programmable_layout *layout, 133 const u32 *clk_mux_table, const u32 *mux_table) 134{ 135 struct clk_programmable *prog; 136 struct clk *clk; 137 u32 val, tmp; 138 int ret; 139 140 if (!base || !name || !parent_names || !num_parents || 141 !layout || !clk_mux_table || !mux_table || id > PROG_ID_MAX) 142 return ERR_PTR(-EINVAL); 143 144 prog = kzalloc(sizeof(*prog), GFP_KERNEL); 145 if (!prog) 146 return ERR_PTR(-ENOMEM); 147 148 prog->id = id; 149 prog->layout = layout; 150 prog->base = base; 151 prog->clk_mux_table = clk_mux_table; 152 prog->mux_table = mux_table; 153 prog->num_parents = num_parents; 154 155 pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &tmp); 156 val = tmp & prog->layout->css_mask; 157 if (layout->have_slck_mck && (tmp & AT91_PMC_CSSMCK_MCK) && !val) 158 ret = PROG_MAX_RM9200_CSS + 1; 159 else 160 ret = at91_clk_mux_val_to_index(prog->mux_table, 161 prog->num_parents, val); 162 if (ret < 0) { 163 kfree(prog); 164 return ERR_PTR(ret); 165 } 166 167 clk = &prog->clk; 168 clk->flags = CLK_GET_RATE_NOCACHE; 169 ret = clk_register(clk, UBOOT_DM_CLK_AT91_PROG, name, 170 parent_names[ret]); 171 if (ret) { 172 kfree(prog); 173 clk = ERR_PTR(ret); 174 } 175 176 return clk; 177} 178 179U_BOOT_DRIVER(at91_prog_clk) = { 180 .name = UBOOT_DM_CLK_AT91_PROG, 181 .id = UCLASS_CLK, 182 .ops = &programmable_ops, 183 .flags = DM_FLAG_PRE_RELOC, 184}; 185 186const struct clk_programmable_layout at91rm9200_programmable_layout = { 187 .pres_mask = 0x7, 188 .pres_shift = 2, 189 .css_mask = 0x3, 190 .have_slck_mck = 0, 191 .is_pres_direct = 0, 192}; 193 194const struct clk_programmable_layout at91sam9g45_programmable_layout = { 195 .pres_mask = 0x7, 196 .pres_shift = 2, 197 .css_mask = 0x3, 198 .have_slck_mck = 1, 199 .is_pres_direct = 0, 200}; 201 202const struct clk_programmable_layout at91sam9x5_programmable_layout = { 203 .pres_mask = 0x7, 204 .pres_shift = 4, 205 .css_mask = 0x7, 206 .have_slck_mck = 0, 207 .is_pres_direct = 0, 208}; 209