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