1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * UTMI 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-utmi.c from Linux. 10 */ 11#include <asm/processor.h> 12#include <common.h> 13#include <clk-uclass.h> 14#include <dm.h> 15#include <linux/clk-provider.h> 16#include <linux/clk/at91_pmc.h> 17#include <mach/at91_sfr.h> 18#include <regmap.h> 19#include <syscon.h> 20 21#include "pmc.h" 22 23#define UBOOT_DM_CLK_AT91_UTMI "at91-utmi-clk" 24#define UBOOT_DM_CLK_AT91_SAMA7G5_UTMI "at91-sama7g5-utmi-clk" 25 26/* 27 * The purpose of this clock is to generate a 480 MHz signal. A different 28 * rate can't be configured. 29 */ 30#define UTMI_RATE 480000000 31 32struct clk_utmi { 33 void __iomem *base; 34 struct regmap *regmap_sfr; 35 struct clk clk; 36}; 37 38#define to_clk_utmi(_clk) container_of(_clk, struct clk_utmi, clk) 39 40static inline bool clk_utmi_ready(struct regmap *regmap) 41{ 42 unsigned int status; 43 44 pmc_read(regmap, AT91_PMC_SR, &status); 45 46 return !!(status & AT91_PMC_LOCKU); 47} 48 49static int clk_utmi_enable(struct clk *clk) 50{ 51 struct clk_utmi *utmi = to_clk_utmi(clk); 52 unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT | 53 AT91_PMC_BIASEN; 54 unsigned int utmi_ref_clk_freq; 55 ulong parent_rate = clk_get_parent_rate(clk); 56 57 /* 58 * If mainck rate is different from 12 MHz, we have to configure the 59 * FREQ field of the SFR_UTMICKTRIM register to generate properly 60 * the utmi clock. 61 */ 62 switch (parent_rate) { 63 case 12000000: 64 utmi_ref_clk_freq = 0; 65 break; 66 case 16000000: 67 utmi_ref_clk_freq = 1; 68 break; 69 case 24000000: 70 utmi_ref_clk_freq = 2; 71 break; 72 /* 73 * Not supported on SAMA5D2 but it's not an issue since MAINCK 74 * maximum value is 24 MHz. 75 */ 76 case 48000000: 77 utmi_ref_clk_freq = 3; 78 break; 79 default: 80 debug("UTMICK: unsupported mainck rate\n"); 81 return -EINVAL; 82 } 83 84 if (utmi->regmap_sfr) { 85 regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM, 86 AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq); 87 } else if (utmi_ref_clk_freq) { 88 debug("UTMICK: sfr node required\n"); 89 return -EINVAL; 90 } 91 92 pmc_update_bits(utmi->base, AT91_CKGR_UCKR, uckr, uckr); 93 94 while (!clk_utmi_ready(utmi->base)) { 95 debug("waiting for utmi...\n"); 96 cpu_relax(); 97 } 98 99 return 0; 100} 101 102static int clk_utmi_disable(struct clk *clk) 103{ 104 struct clk_utmi *utmi = to_clk_utmi(clk); 105 106 pmc_update_bits(utmi->base, AT91_CKGR_UCKR, AT91_PMC_UPLLEN, 0); 107 108 return 0; 109} 110 111static ulong clk_utmi_get_rate(struct clk *clk) 112{ 113 /* UTMI clk rate is fixed. */ 114 return UTMI_RATE; 115} 116 117static const struct clk_ops utmi_ops = { 118 .enable = clk_utmi_enable, 119 .disable = clk_utmi_disable, 120 .get_rate = clk_utmi_get_rate, 121}; 122 123struct clk *at91_clk_register_utmi(void __iomem *base, struct udevice *dev, 124 const char *name, const char *parent_name) 125{ 126 struct udevice *syscon; 127 struct clk_utmi *utmi; 128 struct clk *clk; 129 int ret; 130 131 if (!base || !dev || !name || !parent_name) 132 return ERR_PTR(-EINVAL); 133 134 ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, 135 "regmap-sfr", &syscon); 136 if (ret) 137 return ERR_PTR(ret); 138 139 utmi = kzalloc(sizeof(*utmi), GFP_KERNEL); 140 if (!utmi) 141 return ERR_PTR(-ENOMEM); 142 143 utmi->base = base; 144 utmi->regmap_sfr = syscon_get_regmap(syscon); 145 if (!utmi->regmap_sfr) { 146 kfree(utmi); 147 return ERR_PTR(-ENODEV); 148 } 149 150 clk = &utmi->clk; 151 clk->flags = CLK_GET_RATE_NOCACHE; 152 ret = clk_register(clk, UBOOT_DM_CLK_AT91_UTMI, name, parent_name); 153 if (ret) { 154 kfree(utmi); 155 clk = ERR_PTR(ret); 156 } 157 158 return clk; 159} 160 161U_BOOT_DRIVER(at91_utmi_clk) = { 162 .name = UBOOT_DM_CLK_AT91_UTMI, 163 .id = UCLASS_CLK, 164 .ops = &utmi_ops, 165 .flags = DM_FLAG_PRE_RELOC, 166}; 167 168static int clk_utmi_sama7g5_enable(struct clk *clk) 169{ 170 struct clk_utmi *utmi = to_clk_utmi(clk); 171 ulong parent_rate = clk_get_parent_rate(clk); 172 unsigned int val; 173 174 switch (parent_rate) { 175 case 16000000: 176 val = 0; 177 break; 178 case 20000000: 179 val = 2; 180 break; 181 case 24000000: 182 val = 3; 183 break; 184 case 32000000: 185 val = 5; 186 break; 187 default: 188 debug("UTMICK: unsupported main_xtal rate\n"); 189 return -EINVAL; 190 } 191 192 pmc_write(utmi->base, AT91_PMC_XTALF, val); 193 194 return 0; 195} 196 197static const struct clk_ops sama7g5_utmi_ops = { 198 .enable = clk_utmi_sama7g5_enable, 199 .get_rate = clk_utmi_get_rate, 200}; 201 202struct clk *at91_clk_sama7g5_register_utmi(void __iomem *base, 203 const char *name, const char *parent_name) 204{ 205 struct clk_utmi *utmi; 206 struct clk *clk; 207 int ret; 208 209 if (!base || !name || !parent_name) 210 return ERR_PTR(-EINVAL); 211 212 utmi = kzalloc(sizeof(*utmi), GFP_KERNEL); 213 if (!utmi) 214 return ERR_PTR(-ENOMEM); 215 216 utmi->base = base; 217 218 clk = &utmi->clk; 219 ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_UTMI, name, 220 parent_name); 221 if (ret) { 222 kfree(utmi); 223 clk = ERR_PTR(ret); 224 } 225 226 return clk; 227} 228 229U_BOOT_DRIVER(at91_sama7g5_utmi_clk) = { 230 .name = UBOOT_DM_CLK_AT91_SAMA7G5_UTMI, 231 .id = UCLASS_CLK, 232 .ops = &sama7g5_utmi_ops, 233 .flags = DM_FLAG_PRE_RELOC, 234}; 235