1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2018 Microhip / Atmel Corporation 4 * Wenyou.Yang <wenyou.yang@microchip.com> 5 */ 6 7 #include <common.h> 8 #include <clk-uclass.h> 9 #include <dm/device.h> 10 #include <linux/io.h> 11 #include <mach/at91_pmc.h> 12 #include "pmc.h" 13 14 DECLARE_GLOBAL_DATA_PTR; 15 16 #define AT91_USB_CLK_SOURCE_MAX 2 17 #define AT91_USB_CLK_MAX_DIV 15 18 19 struct at91_usb_clk_priv { 20 u32 num_clksource; 21 }; 22 23 static ulong at91_usb_clk_get_rate(struct clk *clk) 24 { 25 struct pmc_platdata *plat = dev_get_platdata(clk->dev); 26 struct at91_pmc *pmc = plat->reg_base; 27 struct clk source; 28 u32 tmp, usbdiv; 29 u8 source_index; 30 int ret; 31 32 tmp = readl(&pmc->pcr); 33 source_index = (tmp >> AT91_PMC_USB_USBS_OFFSET) & 34 AT91_PMC_USB_USBS_MASK; 35 usbdiv = (tmp >> AT91_PMC_USB_DIV_OFFSET) & AT91_PMC_USB_DIV_MASK; 36 37 ret = clk_get_by_index(clk->dev, source_index, &source); 38 if (ret) 39 return 0; 40 41 return clk_get_rate(&source) / (usbdiv + 1); 42 } 43 44 static ulong at91_usb_clk_set_rate(struct clk *clk, ulong rate) 45 { 46 struct pmc_platdata *plat = dev_get_platdata(clk->dev); 47 struct at91_pmc *pmc = plat->reg_base; 48 struct at91_usb_clk_priv *priv = dev_get_priv(clk->dev); 49 struct clk source, best_source; 50 ulong tmp_rate, best_rate = rate, source_rate; 51 int tmp_diff, best_diff = -1; 52 u32 div, best_div = 0; 53 u8 best_source_index = 0; 54 u8 i; 55 u32 tmp; 56 int ret; 57 58 for (i = 0; i < priv->num_clksource; i++) { 59 ret = clk_get_by_index(clk->dev, i, &source); 60 if (ret) 61 return ret; 62 63 source_rate = clk_get_rate(&source); 64 if (IS_ERR_VALUE(source_rate)) 65 return source_rate; 66 67 for (div = 1; div < AT91_USB_CLK_MAX_DIV + 2; div++) { 68 tmp_rate = DIV_ROUND_CLOSEST(source_rate, div); 69 tmp_diff = abs(rate - tmp_rate); 70 71 if (best_diff < 0 || best_diff > tmp_diff) { 72 best_rate = tmp_rate; 73 best_diff = tmp_diff; 74 75 best_div = div - 1; 76 best_source = source; 77 best_source_index = i; 78 } 79 80 if (!best_diff || tmp_rate < rate) 81 break; 82 } 83 84 if (!best_diff) 85 break; 86 } 87 88 debug("AT91 USB: best sourc: %s, best_rate = %ld, best_div = %d\n", 89 best_source.dev->name, best_rate, best_div); 90 91 ret = clk_enable(&best_source); 92 if (ret) 93 return ret; 94 95 tmp = AT91_PMC_USB_USBS_(best_source_index) | 96 AT91_PMC_USB_DIV_(best_div); 97 writel(tmp, &pmc->usb); 98 99 return 0; 100 } 101 102 static struct clk_ops at91_usb_clk_ops = { 103 .get_rate = at91_usb_clk_get_rate, 104 .set_rate = at91_usb_clk_set_rate, 105 }; 106 107 static int at91_usb_clk_ofdata_to_platdata(struct udevice *dev) 108 { 109 struct at91_usb_clk_priv *priv = dev_get_priv(dev); 110 u32 cells[AT91_USB_CLK_SOURCE_MAX]; 111 u32 num_clksource; 112 113 num_clksource = fdtdec_get_int_array_count(gd->fdt_blob, 114 dev_of_offset(dev), 115 "clocks", cells, 116 AT91_USB_CLK_SOURCE_MAX); 117 118 if (!num_clksource) 119 return -1; 120 121 priv->num_clksource = num_clksource; 122 123 return 0; 124 } 125 126 static int at91_usb_clk_probe(struct udevice *dev) 127 { 128 return at91_pmc_core_probe(dev); 129 } 130 131 static const struct udevice_id at91_usb_clk_match[] = { 132 { .compatible = "atmel,at91sam9x5-clk-usb" }, 133 {} 134 }; 135 136 U_BOOT_DRIVER(at91_usb_clk) = { 137 .name = "at91-usb-clk", 138 .id = UCLASS_CLK, 139 .of_match = at91_usb_clk_match, 140 .probe = at91_usb_clk_probe, 141 .ofdata_to_platdata = at91_usb_clk_ofdata_to_platdata, 142 .priv_auto_alloc_size = sizeof(struct at91_usb_clk_priv), 143 .platdata_auto_alloc_size = sizeof(struct pmc_platdata), 144 .ops = &at91_usb_clk_ops, 145 }; 146