linux/drivers/clk/at91/clk-usb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
   4 */
   5
   6#include <linux/clk-provider.h>
   7#include <linux/clkdev.h>
   8#include <linux/clk/at91_pmc.h>
   9#include <linux/of.h>
  10#include <linux/mfd/syscon.h>
  11#include <linux/regmap.h>
  12
  13#include "pmc.h"
  14
  15#define SAM9X5_USB_DIV_SHIFT    8
  16#define SAM9X5_USB_MAX_DIV      0xf
  17
  18#define RM9200_USB_DIV_SHIFT    28
  19#define RM9200_USB_DIV_TAB_SIZE 4
  20
  21#define SAM9X5_USBS_MASK        GENMASK(0, 0)
  22#define SAM9X60_USBS_MASK       GENMASK(1, 0)
  23
  24struct at91sam9x5_clk_usb {
  25        struct clk_hw hw;
  26        struct regmap *regmap;
  27        u32 usbs_mask;
  28};
  29
  30#define to_at91sam9x5_clk_usb(hw) \
  31        container_of(hw, struct at91sam9x5_clk_usb, hw)
  32
  33struct at91rm9200_clk_usb {
  34        struct clk_hw hw;
  35        struct regmap *regmap;
  36        u32 divisors[4];
  37};
  38
  39#define to_at91rm9200_clk_usb(hw) \
  40        container_of(hw, struct at91rm9200_clk_usb, hw)
  41
  42static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw,
  43                                                    unsigned long parent_rate)
  44{
  45        struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
  46        unsigned int usbr;
  47        u8 usbdiv;
  48
  49        regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
  50        usbdiv = (usbr & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT;
  51
  52        return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1));
  53}
  54
  55static int at91sam9x5_clk_usb_determine_rate(struct clk_hw *hw,
  56                                             struct clk_rate_request *req)
  57{
  58        struct clk_hw *parent;
  59        long best_rate = -EINVAL;
  60        unsigned long tmp_rate;
  61        int best_diff = -1;
  62        int tmp_diff;
  63        int i;
  64
  65        for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
  66                int div;
  67
  68                parent = clk_hw_get_parent_by_index(hw, i);
  69                if (!parent)
  70                        continue;
  71
  72                for (div = 1; div < SAM9X5_USB_MAX_DIV + 2; div++) {
  73                        unsigned long tmp_parent_rate;
  74
  75                        tmp_parent_rate = req->rate * div;
  76                        tmp_parent_rate = clk_hw_round_rate(parent,
  77                                                           tmp_parent_rate);
  78                        tmp_rate = DIV_ROUND_CLOSEST(tmp_parent_rate, div);
  79                        if (tmp_rate < req->rate)
  80                                tmp_diff = req->rate - tmp_rate;
  81                        else
  82                                tmp_diff = tmp_rate - req->rate;
  83
  84                        if (best_diff < 0 || best_diff > tmp_diff) {
  85                                best_rate = tmp_rate;
  86                                best_diff = tmp_diff;
  87                                req->best_parent_rate = tmp_parent_rate;
  88                                req->best_parent_hw = parent;
  89                        }
  90
  91                        if (!best_diff || tmp_rate < req->rate)
  92                                break;
  93                }
  94
  95                if (!best_diff)
  96                        break;
  97        }
  98
  99        if (best_rate < 0)
 100                return best_rate;
 101
 102        req->rate = best_rate;
 103        return 0;
 104}
 105
 106static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
 107{
 108        struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
 109
 110        if (index > 1)
 111                return -EINVAL;
 112
 113        regmap_update_bits(usb->regmap, AT91_PMC_USB, usb->usbs_mask, index);
 114
 115        return 0;
 116}
 117
 118static u8 at91sam9x5_clk_usb_get_parent(struct clk_hw *hw)
 119{
 120        struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
 121        unsigned int usbr;
 122
 123        regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
 124
 125        return usbr & usb->usbs_mask;
 126}
 127
 128static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
 129                                       unsigned long parent_rate)
 130{
 131        struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
 132        unsigned long div;
 133
 134        if (!rate)
 135                return -EINVAL;
 136
 137        div = DIV_ROUND_CLOSEST(parent_rate, rate);
 138        if (div > SAM9X5_USB_MAX_DIV + 1 || !div)
 139                return -EINVAL;
 140
 141        regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_OHCIUSBDIV,
 142                           (div - 1) << SAM9X5_USB_DIV_SHIFT);
 143
 144        return 0;
 145}
 146
 147static const struct clk_ops at91sam9x5_usb_ops = {
 148        .recalc_rate = at91sam9x5_clk_usb_recalc_rate,
 149        .determine_rate = at91sam9x5_clk_usb_determine_rate,
 150        .get_parent = at91sam9x5_clk_usb_get_parent,
 151        .set_parent = at91sam9x5_clk_usb_set_parent,
 152        .set_rate = at91sam9x5_clk_usb_set_rate,
 153};
 154
 155static int at91sam9n12_clk_usb_enable(struct clk_hw *hw)
 156{
 157        struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
 158
 159        regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS,
 160                           AT91_PMC_USBS);
 161
 162        return 0;
 163}
 164
 165static void at91sam9n12_clk_usb_disable(struct clk_hw *hw)
 166{
 167        struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
 168
 169        regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 0);
 170}
 171
 172static int at91sam9n12_clk_usb_is_enabled(struct clk_hw *hw)
 173{
 174        struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
 175        unsigned int usbr;
 176
 177        regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
 178
 179        return usbr & AT91_PMC_USBS;
 180}
 181
 182static const struct clk_ops at91sam9n12_usb_ops = {
 183        .enable = at91sam9n12_clk_usb_enable,
 184        .disable = at91sam9n12_clk_usb_disable,
 185        .is_enabled = at91sam9n12_clk_usb_is_enabled,
 186        .recalc_rate = at91sam9x5_clk_usb_recalc_rate,
 187        .determine_rate = at91sam9x5_clk_usb_determine_rate,
 188        .set_rate = at91sam9x5_clk_usb_set_rate,
 189};
 190
 191static struct clk_hw * __init
 192_at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
 193                             const char **parent_names, u8 num_parents,
 194                             u32 usbs_mask)
 195{
 196        struct at91sam9x5_clk_usb *usb;
 197        struct clk_hw *hw;
 198        struct clk_init_data init;
 199        int ret;
 200
 201        usb = kzalloc(sizeof(*usb), GFP_KERNEL);
 202        if (!usb)
 203                return ERR_PTR(-ENOMEM);
 204
 205        init.name = name;
 206        init.ops = &at91sam9x5_usb_ops;
 207        init.parent_names = parent_names;
 208        init.num_parents = num_parents;
 209        init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
 210                     CLK_SET_RATE_PARENT;
 211
 212        usb->hw.init = &init;
 213        usb->regmap = regmap;
 214        usb->usbs_mask = SAM9X5_USBS_MASK;
 215
 216        hw = &usb->hw;
 217        ret = clk_hw_register(NULL, &usb->hw);
 218        if (ret) {
 219                kfree(usb);
 220                hw = ERR_PTR(ret);
 221        }
 222
 223        return hw;
 224}
 225
 226struct clk_hw * __init
 227at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
 228                            const char **parent_names, u8 num_parents)
 229{
 230        return _at91sam9x5_clk_register_usb(regmap, name, parent_names,
 231                                            num_parents, SAM9X5_USBS_MASK);
 232}
 233
 234struct clk_hw * __init
 235sam9x60_clk_register_usb(struct regmap *regmap, const char *name,
 236                         const char **parent_names, u8 num_parents)
 237{
 238        return _at91sam9x5_clk_register_usb(regmap, name, parent_names,
 239                                            num_parents, SAM9X60_USBS_MASK);
 240}
 241
 242struct clk_hw * __init
 243at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name,
 244                             const char *parent_name)
 245{
 246        struct at91sam9x5_clk_usb *usb;
 247        struct clk_hw *hw;
 248        struct clk_init_data init;
 249        int ret;
 250
 251        usb = kzalloc(sizeof(*usb), GFP_KERNEL);
 252        if (!usb)
 253                return ERR_PTR(-ENOMEM);
 254
 255        init.name = name;
 256        init.ops = &at91sam9n12_usb_ops;
 257        init.parent_names = &parent_name;
 258        init.num_parents = 1;
 259        init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT;
 260
 261        usb->hw.init = &init;
 262        usb->regmap = regmap;
 263
 264        hw = &usb->hw;
 265        ret = clk_hw_register(NULL, &usb->hw);
 266        if (ret) {
 267                kfree(usb);
 268                hw = ERR_PTR(ret);
 269        }
 270
 271        return hw;
 272}
 273
 274static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk_hw *hw,
 275                                                    unsigned long parent_rate)
 276{
 277        struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
 278        unsigned int pllbr;
 279        u8 usbdiv;
 280
 281        regmap_read(usb->regmap, AT91_CKGR_PLLBR, &pllbr);
 282
 283        usbdiv = (pllbr & AT91_PMC_USBDIV) >> RM9200_USB_DIV_SHIFT;
 284        if (usb->divisors[usbdiv])
 285                return parent_rate / usb->divisors[usbdiv];
 286
 287        return 0;
 288}
 289
 290static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
 291                                          unsigned long *parent_rate)
 292{
 293        struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
 294        struct clk_hw *parent = clk_hw_get_parent(hw);
 295        unsigned long bestrate = 0;
 296        int bestdiff = -1;
 297        unsigned long tmprate;
 298        int tmpdiff;
 299        int i = 0;
 300
 301        for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
 302                unsigned long tmp_parent_rate;
 303
 304                if (!usb->divisors[i])
 305                        continue;
 306
 307                tmp_parent_rate = rate * usb->divisors[i];
 308                tmp_parent_rate = clk_hw_round_rate(parent, tmp_parent_rate);
 309                tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]);
 310                if (tmprate < rate)
 311                        tmpdiff = rate - tmprate;
 312                else
 313                        tmpdiff = tmprate - rate;
 314
 315                if (bestdiff < 0 || bestdiff > tmpdiff) {
 316                        bestrate = tmprate;
 317                        bestdiff = tmpdiff;
 318                        *parent_rate = tmp_parent_rate;
 319                }
 320
 321                if (!bestdiff)
 322                        break;
 323        }
 324
 325        return bestrate;
 326}
 327
 328static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
 329                                       unsigned long parent_rate)
 330{
 331        int i;
 332        struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
 333        unsigned long div;
 334
 335        if (!rate)
 336                return -EINVAL;
 337
 338        div = DIV_ROUND_CLOSEST(parent_rate, rate);
 339
 340        for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
 341                if (usb->divisors[i] == div) {
 342                        regmap_update_bits(usb->regmap, AT91_CKGR_PLLBR,
 343                                           AT91_PMC_USBDIV,
 344                                           i << RM9200_USB_DIV_SHIFT);
 345
 346                        return 0;
 347                }
 348        }
 349
 350        return -EINVAL;
 351}
 352
 353static const struct clk_ops at91rm9200_usb_ops = {
 354        .recalc_rate = at91rm9200_clk_usb_recalc_rate,
 355        .round_rate = at91rm9200_clk_usb_round_rate,
 356        .set_rate = at91rm9200_clk_usb_set_rate,
 357};
 358
 359struct clk_hw * __init
 360at91rm9200_clk_register_usb(struct regmap *regmap, const char *name,
 361                            const char *parent_name, const u32 *divisors)
 362{
 363        struct at91rm9200_clk_usb *usb;
 364        struct clk_hw *hw;
 365        struct clk_init_data init;
 366        int ret;
 367
 368        usb = kzalloc(sizeof(*usb), GFP_KERNEL);
 369        if (!usb)
 370                return ERR_PTR(-ENOMEM);
 371
 372        init.name = name;
 373        init.ops = &at91rm9200_usb_ops;
 374        init.parent_names = &parent_name;
 375        init.num_parents = 1;
 376        init.flags = CLK_SET_RATE_PARENT;
 377
 378        usb->hw.init = &init;
 379        usb->regmap = regmap;
 380        memcpy(usb->divisors, divisors, sizeof(usb->divisors));
 381
 382        hw = &usb->hw;
 383        ret = clk_hw_register(NULL, &usb->hw);
 384        if (ret) {
 385                kfree(usb);
 386                hw = ERR_PTR(ret);
 387        }
 388
 389        return hw;
 390}
 391