18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 78c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 88c2ecf20Sopenharmony_ci#include <linux/clk/at91_pmc.h> 98c2ecf20Sopenharmony_ci#include <linux/of.h> 108c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 118c2ecf20Sopenharmony_ci#include <linux/regmap.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "pmc.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define SAM9X5_USB_DIV_SHIFT 8 168c2ecf20Sopenharmony_ci#define SAM9X5_USB_MAX_DIV 0xf 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define RM9200_USB_DIV_SHIFT 28 198c2ecf20Sopenharmony_ci#define RM9200_USB_DIV_TAB_SIZE 4 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define SAM9X5_USBS_MASK GENMASK(0, 0) 228c2ecf20Sopenharmony_ci#define SAM9X60_USBS_MASK GENMASK(1, 0) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct at91sam9x5_clk_usb { 258c2ecf20Sopenharmony_ci struct clk_hw hw; 268c2ecf20Sopenharmony_ci struct regmap *regmap; 278c2ecf20Sopenharmony_ci u32 usbs_mask; 288c2ecf20Sopenharmony_ci u8 num_parents; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define to_at91sam9x5_clk_usb(hw) \ 328c2ecf20Sopenharmony_ci container_of(hw, struct at91sam9x5_clk_usb, hw) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct at91rm9200_clk_usb { 358c2ecf20Sopenharmony_ci struct clk_hw hw; 368c2ecf20Sopenharmony_ci struct regmap *regmap; 378c2ecf20Sopenharmony_ci u32 divisors[4]; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define to_at91rm9200_clk_usb(hw) \ 418c2ecf20Sopenharmony_ci container_of(hw, struct at91rm9200_clk_usb, hw) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw, 448c2ecf20Sopenharmony_ci unsigned long parent_rate) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); 478c2ecf20Sopenharmony_ci unsigned int usbr; 488c2ecf20Sopenharmony_ci u8 usbdiv; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci regmap_read(usb->regmap, AT91_PMC_USB, &usbr); 518c2ecf20Sopenharmony_ci usbdiv = (usbr & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1)); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int at91sam9x5_clk_usb_determine_rate(struct clk_hw *hw, 578c2ecf20Sopenharmony_ci struct clk_rate_request *req) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct clk_hw *parent; 608c2ecf20Sopenharmony_ci long best_rate = -EINVAL; 618c2ecf20Sopenharmony_ci unsigned long tmp_rate; 628c2ecf20Sopenharmony_ci int best_diff = -1; 638c2ecf20Sopenharmony_ci int tmp_diff; 648c2ecf20Sopenharmony_ci int i; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci for (i = 0; i < clk_hw_get_num_parents(hw); i++) { 678c2ecf20Sopenharmony_ci int div; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci parent = clk_hw_get_parent_by_index(hw, i); 708c2ecf20Sopenharmony_ci if (!parent) 718c2ecf20Sopenharmony_ci continue; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci for (div = 1; div < SAM9X5_USB_MAX_DIV + 2; div++) { 748c2ecf20Sopenharmony_ci unsigned long tmp_parent_rate; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci tmp_parent_rate = req->rate * div; 778c2ecf20Sopenharmony_ci tmp_parent_rate = clk_hw_round_rate(parent, 788c2ecf20Sopenharmony_ci tmp_parent_rate); 798c2ecf20Sopenharmony_ci if (!tmp_parent_rate) 808c2ecf20Sopenharmony_ci continue; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci tmp_rate = DIV_ROUND_CLOSEST(tmp_parent_rate, div); 838c2ecf20Sopenharmony_ci if (tmp_rate < req->rate) 848c2ecf20Sopenharmony_ci tmp_diff = req->rate - tmp_rate; 858c2ecf20Sopenharmony_ci else 868c2ecf20Sopenharmony_ci tmp_diff = tmp_rate - req->rate; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (best_diff < 0 || best_diff > tmp_diff) { 898c2ecf20Sopenharmony_ci best_rate = tmp_rate; 908c2ecf20Sopenharmony_ci best_diff = tmp_diff; 918c2ecf20Sopenharmony_ci req->best_parent_rate = tmp_parent_rate; 928c2ecf20Sopenharmony_ci req->best_parent_hw = parent; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (!best_diff || tmp_rate < req->rate) 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (!best_diff) 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (best_rate < 0) 1048c2ecf20Sopenharmony_ci return best_rate; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci req->rate = best_rate; 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (index >= usb->num_parents) 1158c2ecf20Sopenharmony_ci return -EINVAL; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci regmap_update_bits(usb->regmap, AT91_PMC_USB, usb->usbs_mask, index); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic u8 at91sam9x5_clk_usb_get_parent(struct clk_hw *hw) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); 1258c2ecf20Sopenharmony_ci unsigned int usbr; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci regmap_read(usb->regmap, AT91_PMC_USB, &usbr); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return usbr & usb->usbs_mask; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, 1338c2ecf20Sopenharmony_ci unsigned long parent_rate) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); 1368c2ecf20Sopenharmony_ci unsigned long div; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (!rate) 1398c2ecf20Sopenharmony_ci return -EINVAL; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci div = DIV_ROUND_CLOSEST(parent_rate, rate); 1428c2ecf20Sopenharmony_ci if (div > SAM9X5_USB_MAX_DIV + 1 || !div) 1438c2ecf20Sopenharmony_ci return -EINVAL; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_OHCIUSBDIV, 1468c2ecf20Sopenharmony_ci (div - 1) << SAM9X5_USB_DIV_SHIFT); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic const struct clk_ops at91sam9x5_usb_ops = { 1528c2ecf20Sopenharmony_ci .recalc_rate = at91sam9x5_clk_usb_recalc_rate, 1538c2ecf20Sopenharmony_ci .determine_rate = at91sam9x5_clk_usb_determine_rate, 1548c2ecf20Sopenharmony_ci .get_parent = at91sam9x5_clk_usb_get_parent, 1558c2ecf20Sopenharmony_ci .set_parent = at91sam9x5_clk_usb_set_parent, 1568c2ecf20Sopenharmony_ci .set_rate = at91sam9x5_clk_usb_set_rate, 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int at91sam9n12_clk_usb_enable(struct clk_hw *hw) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 1648c2ecf20Sopenharmony_ci AT91_PMC_USBS); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void at91sam9n12_clk_usb_disable(struct clk_hw *hw) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 0); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int at91sam9n12_clk_usb_is_enabled(struct clk_hw *hw) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); 1798c2ecf20Sopenharmony_ci unsigned int usbr; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci regmap_read(usb->regmap, AT91_PMC_USB, &usbr); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return usbr & AT91_PMC_USBS; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic const struct clk_ops at91sam9n12_usb_ops = { 1878c2ecf20Sopenharmony_ci .enable = at91sam9n12_clk_usb_enable, 1888c2ecf20Sopenharmony_ci .disable = at91sam9n12_clk_usb_disable, 1898c2ecf20Sopenharmony_ci .is_enabled = at91sam9n12_clk_usb_is_enabled, 1908c2ecf20Sopenharmony_ci .recalc_rate = at91sam9x5_clk_usb_recalc_rate, 1918c2ecf20Sopenharmony_ci .determine_rate = at91sam9x5_clk_usb_determine_rate, 1928c2ecf20Sopenharmony_ci .set_rate = at91sam9x5_clk_usb_set_rate, 1938c2ecf20Sopenharmony_ci}; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic struct clk_hw * __init 1968c2ecf20Sopenharmony_ci_at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, 1978c2ecf20Sopenharmony_ci const char **parent_names, u8 num_parents, 1988c2ecf20Sopenharmony_ci u32 usbs_mask) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct at91sam9x5_clk_usb *usb; 2018c2ecf20Sopenharmony_ci struct clk_hw *hw; 2028c2ecf20Sopenharmony_ci struct clk_init_data init; 2038c2ecf20Sopenharmony_ci int ret; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci usb = kzalloc(sizeof(*usb), GFP_KERNEL); 2068c2ecf20Sopenharmony_ci if (!usb) 2078c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci init.name = name; 2108c2ecf20Sopenharmony_ci init.ops = &at91sam9x5_usb_ops; 2118c2ecf20Sopenharmony_ci init.parent_names = parent_names; 2128c2ecf20Sopenharmony_ci init.num_parents = num_parents; 2138c2ecf20Sopenharmony_ci init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | 2148c2ecf20Sopenharmony_ci CLK_SET_RATE_PARENT; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci usb->hw.init = &init; 2178c2ecf20Sopenharmony_ci usb->regmap = regmap; 2188c2ecf20Sopenharmony_ci usb->usbs_mask = usbs_mask; 2198c2ecf20Sopenharmony_ci usb->num_parents = num_parents; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci hw = &usb->hw; 2228c2ecf20Sopenharmony_ci ret = clk_hw_register(NULL, &usb->hw); 2238c2ecf20Sopenharmony_ci if (ret) { 2248c2ecf20Sopenharmony_ci kfree(usb); 2258c2ecf20Sopenharmony_ci hw = ERR_PTR(ret); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return hw; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistruct clk_hw * __init 2328c2ecf20Sopenharmony_ciat91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, 2338c2ecf20Sopenharmony_ci const char **parent_names, u8 num_parents) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci return _at91sam9x5_clk_register_usb(regmap, name, parent_names, 2368c2ecf20Sopenharmony_ci num_parents, SAM9X5_USBS_MASK); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistruct clk_hw * __init 2408c2ecf20Sopenharmony_cisam9x60_clk_register_usb(struct regmap *regmap, const char *name, 2418c2ecf20Sopenharmony_ci const char **parent_names, u8 num_parents) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci return _at91sam9x5_clk_register_usb(regmap, name, parent_names, 2448c2ecf20Sopenharmony_ci num_parents, SAM9X60_USBS_MASK); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistruct clk_hw * __init 2488c2ecf20Sopenharmony_ciat91sam9n12_clk_register_usb(struct regmap *regmap, const char *name, 2498c2ecf20Sopenharmony_ci const char *parent_name) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct at91sam9x5_clk_usb *usb; 2528c2ecf20Sopenharmony_ci struct clk_hw *hw; 2538c2ecf20Sopenharmony_ci struct clk_init_data init; 2548c2ecf20Sopenharmony_ci int ret; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci usb = kzalloc(sizeof(*usb), GFP_KERNEL); 2578c2ecf20Sopenharmony_ci if (!usb) 2588c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci init.name = name; 2618c2ecf20Sopenharmony_ci init.ops = &at91sam9n12_usb_ops; 2628c2ecf20Sopenharmony_ci init.parent_names = &parent_name; 2638c2ecf20Sopenharmony_ci init.num_parents = 1; 2648c2ecf20Sopenharmony_ci init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci usb->hw.init = &init; 2678c2ecf20Sopenharmony_ci usb->regmap = regmap; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci hw = &usb->hw; 2708c2ecf20Sopenharmony_ci ret = clk_hw_register(NULL, &usb->hw); 2718c2ecf20Sopenharmony_ci if (ret) { 2728c2ecf20Sopenharmony_ci kfree(usb); 2738c2ecf20Sopenharmony_ci hw = ERR_PTR(ret); 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return hw; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic unsigned long at91rm9200_clk_usb_recalc_rate(struct clk_hw *hw, 2808c2ecf20Sopenharmony_ci unsigned long parent_rate) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); 2838c2ecf20Sopenharmony_ci unsigned int pllbr; 2848c2ecf20Sopenharmony_ci u8 usbdiv; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci regmap_read(usb->regmap, AT91_CKGR_PLLBR, &pllbr); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci usbdiv = (pllbr & AT91_PMC_USBDIV) >> RM9200_USB_DIV_SHIFT; 2898c2ecf20Sopenharmony_ci if (usb->divisors[usbdiv]) 2908c2ecf20Sopenharmony_ci return parent_rate / usb->divisors[usbdiv]; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, 2968c2ecf20Sopenharmony_ci unsigned long *parent_rate) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); 2998c2ecf20Sopenharmony_ci struct clk_hw *parent = clk_hw_get_parent(hw); 3008c2ecf20Sopenharmony_ci unsigned long bestrate = 0; 3018c2ecf20Sopenharmony_ci int bestdiff = -1; 3028c2ecf20Sopenharmony_ci unsigned long tmprate; 3038c2ecf20Sopenharmony_ci int tmpdiff; 3048c2ecf20Sopenharmony_ci int i = 0; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) { 3078c2ecf20Sopenharmony_ci unsigned long tmp_parent_rate; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (!usb->divisors[i]) 3108c2ecf20Sopenharmony_ci continue; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci tmp_parent_rate = rate * usb->divisors[i]; 3138c2ecf20Sopenharmony_ci tmp_parent_rate = clk_hw_round_rate(parent, tmp_parent_rate); 3148c2ecf20Sopenharmony_ci tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]); 3158c2ecf20Sopenharmony_ci if (tmprate < rate) 3168c2ecf20Sopenharmony_ci tmpdiff = rate - tmprate; 3178c2ecf20Sopenharmony_ci else 3188c2ecf20Sopenharmony_ci tmpdiff = tmprate - rate; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (bestdiff < 0 || bestdiff > tmpdiff) { 3218c2ecf20Sopenharmony_ci bestrate = tmprate; 3228c2ecf20Sopenharmony_ci bestdiff = tmpdiff; 3238c2ecf20Sopenharmony_ci *parent_rate = tmp_parent_rate; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (!bestdiff) 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return bestrate; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, 3348c2ecf20Sopenharmony_ci unsigned long parent_rate) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci int i; 3378c2ecf20Sopenharmony_ci struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); 3388c2ecf20Sopenharmony_ci unsigned long div; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (!rate) 3418c2ecf20Sopenharmony_ci return -EINVAL; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci div = DIV_ROUND_CLOSEST(parent_rate, rate); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) { 3468c2ecf20Sopenharmony_ci if (usb->divisors[i] == div) { 3478c2ecf20Sopenharmony_ci regmap_update_bits(usb->regmap, AT91_CKGR_PLLBR, 3488c2ecf20Sopenharmony_ci AT91_PMC_USBDIV, 3498c2ecf20Sopenharmony_ci i << RM9200_USB_DIV_SHIFT); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return 0; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return -EINVAL; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic const struct clk_ops at91rm9200_usb_ops = { 3598c2ecf20Sopenharmony_ci .recalc_rate = at91rm9200_clk_usb_recalc_rate, 3608c2ecf20Sopenharmony_ci .round_rate = at91rm9200_clk_usb_round_rate, 3618c2ecf20Sopenharmony_ci .set_rate = at91rm9200_clk_usb_set_rate, 3628c2ecf20Sopenharmony_ci}; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistruct clk_hw * __init 3658c2ecf20Sopenharmony_ciat91rm9200_clk_register_usb(struct regmap *regmap, const char *name, 3668c2ecf20Sopenharmony_ci const char *parent_name, const u32 *divisors) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct at91rm9200_clk_usb *usb; 3698c2ecf20Sopenharmony_ci struct clk_hw *hw; 3708c2ecf20Sopenharmony_ci struct clk_init_data init; 3718c2ecf20Sopenharmony_ci int ret; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci usb = kzalloc(sizeof(*usb), GFP_KERNEL); 3748c2ecf20Sopenharmony_ci if (!usb) 3758c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci init.name = name; 3788c2ecf20Sopenharmony_ci init.ops = &at91rm9200_usb_ops; 3798c2ecf20Sopenharmony_ci init.parent_names = &parent_name; 3808c2ecf20Sopenharmony_ci init.num_parents = 1; 3818c2ecf20Sopenharmony_ci init.flags = CLK_SET_RATE_PARENT; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci usb->hw.init = &init; 3848c2ecf20Sopenharmony_ci usb->regmap = regmap; 3858c2ecf20Sopenharmony_ci memcpy(usb->divisors, divisors, sizeof(usb->divisors)); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci hw = &usb->hw; 3888c2ecf20Sopenharmony_ci ret = clk_hw_register(NULL, &usb->hw); 3898c2ecf20Sopenharmony_ci if (ret) { 3908c2ecf20Sopenharmony_ci kfree(usb); 3918c2ecf20Sopenharmony_ci hw = ERR_PTR(ret); 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return hw; 3958c2ecf20Sopenharmony_ci} 396