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