diff options
Diffstat (limited to 'drivers/clk/at91/clk-usb.c')
-rw-r--r-- | drivers/clk/at91/clk-usb.c | 217 |
1 files changed, 124 insertions, 93 deletions
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c index 0eb0b1f5bc..4473dc7c34 100644 --- a/drivers/clk/at91/clk-usb.c +++ b/drivers/clk/at91/clk-usb.c @@ -1,55 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * */ -#include <common.h> -#include <clock.h> -#include <io.h> -#include <linux/list.h> -#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> #include "pmc.h" -#define USB_SOURCE_MAX 2 - #define SAM9X5_USB_DIV_SHIFT 8 #define SAM9X5_USB_MAX_DIV 0xf #define RM9200_USB_DIV_SHIFT 28 #define RM9200_USB_DIV_TAB_SIZE 4 +#define SAM9X5_USBS_MASK GENMASK(0, 0) +#define SAM9X60_USBS_MASK GENMASK(1, 0) + struct at91sam9x5_clk_usb { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; - const char *parent_names[USB_SOURCE_MAX]; + struct at91_clk_pms pms; + u32 usbs_mask; + u8 num_parents; }; -#define to_at91sam9x5_clk_usb(clk) \ - container_of(clk, struct at91sam9x5_clk_usb, clk) +#define to_at91sam9x5_clk_usb(hw) \ + container_of(hw, struct at91sam9x5_clk_usb, hw) struct at91rm9200_clk_usb { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; u32 divisors[4]; - const char *parent_name; }; -#define to_at91rm9200_clk_usb(clk) \ - container_of(clk, struct at91rm9200_clk_usb, clk) +#define to_at91rm9200_clk_usb(hw) \ + container_of(hw, struct at91rm9200_clk_usb, hw) -static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk *clk, +static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); unsigned int usbr; u8 usbdiv; @@ -59,33 +54,32 @@ static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk *clk, return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1)); } -static int at91sam9x5_clk_usb_set_parent(struct clk *clk, u8 index) +static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index) { - struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); - if (index > 1) + if (index >= usb->num_parents) return -EINVAL; - regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, - index ? AT91_PMC_USBS : 0); + regmap_update_bits(usb->regmap, AT91_PMC_USB, usb->usbs_mask, index); return 0; } -static int at91sam9x5_clk_usb_get_parent(struct clk *clk) +static int at91sam9x5_clk_usb_get_parent(struct clk_hw *hw) { - struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); unsigned int usbr; regmap_read(usb->regmap, AT91_PMC_USB, &usbr); - return usbr & AT91_PMC_USBS; + return usbr & usb->usbs_mask; } -static int at91sam9x5_clk_usb_set_rate(struct clk *clk, unsigned long rate, +static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); unsigned long div; if (!rate) @@ -95,8 +89,8 @@ static int at91sam9x5_clk_usb_set_rate(struct clk *clk, unsigned long rate, if (div > SAM9X5_USB_MAX_DIV + 1 || !div) return -EINVAL; - regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_OHCIUSBDIV, - (div - 1) << SAM9X5_USB_DIV_SHIFT); + regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_OHCIUSBDIV, + (div - 1) << SAM9X5_USB_DIV_SHIFT); return 0; } @@ -108,26 +102,26 @@ static const struct clk_ops at91sam9x5_usb_ops = { .set_rate = at91sam9x5_clk_usb_set_rate, }; -static int at91sam9n12_clk_usb_enable(struct clk *clk) +static int at91sam9n12_clk_usb_enable(struct clk_hw *hw) { - struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); - regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, - AT91_PMC_USBS); + regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, + AT91_PMC_USBS); return 0; } -static void at91sam9n12_clk_usb_disable(struct clk *clk) +static void at91sam9n12_clk_usb_disable(struct clk_hw *hw) { - struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); - regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 0); + regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 0); } -static int at91sam9n12_clk_usb_is_enabled(struct clk *clk) +static int at91sam9n12_clk_usb_is_enabled(struct clk_hw *hw) { - struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); unsigned int usbr; regmap_read(usb->regmap, AT91_PMC_USB, &usbr); @@ -143,63 +137,94 @@ static const struct clk_ops at91sam9n12_usb_ops = { .set_rate = at91sam9x5_clk_usb_set_rate, }; -struct clk * -at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, - const char **parent_names, u8 num_parents) +static struct clk_hw * __init +_at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, + const char **parent_names, u8 num_parents, + u32 usbs_mask) { struct at91sam9x5_clk_usb *usb; + struct clk_hw *hw; + struct clk_init_data init; int ret; usb = kzalloc(sizeof(*usb), GFP_KERNEL); - usb->clk.name = name; - usb->clk.ops = &at91sam9x5_usb_ops; - memcpy(usb->parent_names, parent_names, - num_parents * sizeof(usb->parent_names[0])); - usb->clk.parent_names = usb->parent_names; - usb->clk.num_parents = num_parents; - usb->clk.flags = CLK_SET_RATE_PARENT; - /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | */ - /* CLK_SET_RATE_PARENT; */ + if (!usb) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &at91sam9x5_usb_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT; + + usb->hw.init = &init; usb->regmap = regmap; + usb->usbs_mask = usbs_mask; + usb->num_parents = num_parents; - ret = clk_register(&usb->clk); + hw = &usb->hw; + ret = clk_hw_register(NULL, &usb->hw); if (ret) { kfree(usb); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &usb->clk; + return hw; +} + +struct clk_hw * __init +at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, + const char **parent_names, u8 num_parents) +{ + return _at91sam9x5_clk_register_usb(regmap, name, parent_names, + num_parents, SAM9X5_USBS_MASK); +} + +struct clk_hw * __init +sam9x60_clk_register_usb(struct regmap *regmap, const char *name, + const char **parent_names, u8 num_parents) +{ + return _at91sam9x5_clk_register_usb(regmap, name, parent_names, + num_parents, SAM9X60_USBS_MASK); } -struct clk * +struct clk_hw * __init at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name, const char *parent_name) { struct at91sam9x5_clk_usb *usb; + struct clk_hw *hw; + struct clk_init_data init; int ret; - usb = xzalloc(sizeof(*usb)); - usb->clk.name = name; - usb->clk.ops = &at91sam9n12_usb_ops; - usb->parent_names[0] = parent_name; - usb->clk.parent_names = &usb->parent_names[0]; - usb->clk.num_parents = 1; - /* init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; */ + usb = kzalloc(sizeof(*usb), GFP_KERNEL); + if (!usb) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &at91sam9n12_usb_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; + + usb->hw.init = &init; usb->regmap = regmap; - ret = clk_register(&usb->clk); + hw = &usb->hw; + ret = clk_hw_register(NULL, &usb->hw); if (ret) { kfree(usb); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &usb->clk; + return hw; } -static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk *clk, +static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk); + struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); unsigned int pllbr; u8 usbdiv; @@ -212,11 +237,11 @@ static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk *clk, return 0; } -static long at91rm9200_clk_usb_round_rate(struct clk *clk, unsigned long rate, +static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { - struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk); - struct clk *parent = clk_get_parent(clk); + struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); + struct clk_hw *parent = clk_hw_get_parent(hw); unsigned long bestrate = 0; int bestdiff = -1; unsigned long tmprate; @@ -230,7 +255,7 @@ static long at91rm9200_clk_usb_round_rate(struct clk *clk, unsigned long rate, continue; tmp_parent_rate = rate * usb->divisors[i]; - tmp_parent_rate = clk_round_rate(parent, tmp_parent_rate); + tmp_parent_rate = clk_hw_round_rate(parent, tmp_parent_rate); tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]); if (tmprate < rate) tmpdiff = rate - tmprate; @@ -250,11 +275,11 @@ static long at91rm9200_clk_usb_round_rate(struct clk *clk, unsigned long rate, return bestrate; } -static int at91rm9200_clk_usb_set_rate(struct clk *clk, unsigned long rate, +static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { int i; - struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk); + struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); unsigned long div; if (!rate) @@ -264,9 +289,9 @@ static int at91rm9200_clk_usb_set_rate(struct clk *clk, unsigned long rate, for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) { if (usb->divisors[i] == div) { - regmap_write_bits(usb->regmap, AT91_CKGR_PLLBR, - AT91_PMC_USBDIV, - i << RM9200_USB_DIV_SHIFT); + regmap_update_bits(usb->regmap, AT91_CKGR_PLLBR, + AT91_PMC_USBDIV, + i << RM9200_USB_DIV_SHIFT); return 0; } @@ -281,29 +306,35 @@ static const struct clk_ops at91rm9200_usb_ops = { .set_rate = at91rm9200_clk_usb_set_rate, }; -struct clk * +struct clk_hw * __init at91rm9200_clk_register_usb(struct regmap *regmap, const char *name, const char *parent_name, const u32 *divisors) { struct at91rm9200_clk_usb *usb; + struct clk_hw *hw; + struct clk_init_data init; int ret; - usb = xzalloc(sizeof(*usb)); - usb->clk.name = name; - usb->clk.ops = &at91rm9200_usb_ops; - usb->parent_name = parent_name; - usb->clk.parent_names = &usb->parent_name; - usb->clk.num_parents = 1; - /* init.flags = CLK_SET_RATE_PARENT; */ + usb = kzalloc(sizeof(*usb), GFP_KERNEL); + if (!usb) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &at91rm9200_usb_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_SET_RATE_PARENT; + usb->hw.init = &init; usb->regmap = regmap; memcpy(usb->divisors, divisors, sizeof(usb->divisors)); - ret = clk_register(&usb->clk); + hw = &usb->hw; + ret = clk_hw_register(NULL, &usb->hw); if (ret) { kfree(usb); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &usb->clk; + return hw; } |