linux/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c
<<
>>
Prefs
   1/*
   2 * Xilinx 'Clocking Wizard' driver
   3 *
   4 *  Copyright (C) 2013 - 2014 Xilinx
   5 *
   6 *  Sören Brinkmann <soren.brinkmann@xilinx.com>
   7 *
   8 * This program is free software: you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License v2 as published by
  10 * the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  19 */
  20
  21#include <linux/platform_device.h>
  22#include <linux/clk.h>
  23#include <linux/clk-provider.h>
  24#include <linux/slab.h>
  25#include <linux/io.h>
  26#include <linux/of.h>
  27#include <linux/module.h>
  28#include <linux/err.h>
  29
  30#define WZRD_NUM_OUTPUTS        7
  31#define WZRD_ACLK_MAX_FREQ      250000000UL
  32
  33#define WZRD_CLK_CFG_REG(n)     (0x200 + 4 * (n))
  34
  35#define WZRD_CLkOUT0_FRAC_EN    BIT(18)
  36#define WZRD_CLkFBOUT_FRAC_EN   BIT(26)
  37
  38#define WZRD_CLKFBOUT_MULT_SHIFT        8
  39#define WZRD_CLKFBOUT_MULT_MASK         (0xff << WZRD_CLKFBOUT_MULT_SHIFT)
  40#define WZRD_DIVCLK_DIVIDE_SHIFT        0
  41#define WZRD_DIVCLK_DIVIDE_MASK         (0xff << WZRD_DIVCLK_DIVIDE_SHIFT)
  42#define WZRD_CLKOUT_DIVIDE_SHIFT        0
  43#define WZRD_CLKOUT_DIVIDE_MASK         (0xff << WZRD_DIVCLK_DIVIDE_SHIFT)
  44
  45enum clk_wzrd_int_clks {
  46        wzrd_clk_mul,
  47        wzrd_clk_mul_div,
  48        wzrd_clk_int_max
  49};
  50
  51/**
  52 * struct clk_wzrd:
  53 * @clk_data:           Clock data
  54 * @nb:                 Notifier block
  55 * @base:               Memory base
  56 * @clk_in1:            Handle to input clock 'clk_in1'
  57 * @axi_clk:            Handle to input clock 's_axi_aclk'
  58 * @clks_internal:      Internal clocks
  59 * @clkout:             Output clocks
  60 * @speed_grade:        Speed grade of the device
  61 * @suspended:          Flag indicating power state of the device
  62 */
  63struct clk_wzrd {
  64        struct clk_onecell_data clk_data;
  65        struct notifier_block nb;
  66        void __iomem *base;
  67        struct clk *clk_in1;
  68        struct clk *axi_clk;
  69        struct clk *clks_internal[wzrd_clk_int_max];
  70        struct clk *clkout[WZRD_NUM_OUTPUTS];
  71        int speed_grade;
  72        bool suspended;
  73};
  74#define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb)
  75
  76/* maximum frequencies for input/output clocks per speed grade */
  77static const unsigned long clk_wzrd_max_freq[] = {
  78        800000000UL,
  79        933000000UL,
  80        1066000000UL
  81};
  82
  83static int clk_wzrd_clk_notifier(struct notifier_block *nb, unsigned long event,
  84                                 void *data)
  85{
  86        unsigned long max;
  87        struct clk_notifier_data *ndata = data;
  88        struct clk_wzrd *clk_wzrd = to_clk_wzrd(nb);
  89
  90        if (clk_wzrd->suspended)
  91                return NOTIFY_OK;
  92
  93        if (ndata->clk == clk_wzrd->clk_in1)
  94                max = clk_wzrd_max_freq[clk_wzrd->speed_grade - 1];
  95        else if (ndata->clk == clk_wzrd->axi_clk)
  96                max = WZRD_ACLK_MAX_FREQ;
  97        else
  98                return NOTIFY_DONE;     /* should never happen */
  99
 100        switch (event) {
 101        case PRE_RATE_CHANGE:
 102                if (ndata->new_rate > max)
 103                        return NOTIFY_BAD;
 104                return NOTIFY_OK;
 105        case POST_RATE_CHANGE:
 106        case ABORT_RATE_CHANGE:
 107        default:
 108                return NOTIFY_DONE;
 109        }
 110}
 111
 112static int __maybe_unused clk_wzrd_suspend(struct device *dev)
 113{
 114        struct clk_wzrd *clk_wzrd = dev_get_drvdata(dev);
 115
 116        clk_disable_unprepare(clk_wzrd->axi_clk);
 117        clk_wzrd->suspended = true;
 118
 119        return 0;
 120}
 121
 122static int __maybe_unused clk_wzrd_resume(struct device *dev)
 123{
 124        int ret;
 125        struct clk_wzrd *clk_wzrd = dev_get_drvdata(dev);
 126
 127        ret = clk_prepare_enable(clk_wzrd->axi_clk);
 128        if (ret) {
 129                dev_err(dev, "unable to enable s_axi_aclk\n");
 130                return ret;
 131        }
 132
 133        clk_wzrd->suspended = false;
 134
 135        return 0;
 136}
 137
 138static SIMPLE_DEV_PM_OPS(clk_wzrd_dev_pm_ops, clk_wzrd_suspend,
 139                         clk_wzrd_resume);
 140
 141static int clk_wzrd_probe(struct platform_device *pdev)
 142{
 143        int i, ret;
 144        u32 reg;
 145        unsigned long rate;
 146        const char *clk_name;
 147        struct clk_wzrd *clk_wzrd;
 148        struct resource *mem;
 149        struct device_node *np = pdev->dev.of_node;
 150
 151        clk_wzrd = devm_kzalloc(&pdev->dev, sizeof(*clk_wzrd), GFP_KERNEL);
 152        if (!clk_wzrd)
 153                return -ENOMEM;
 154        platform_set_drvdata(pdev, clk_wzrd);
 155
 156        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 157        clk_wzrd->base = devm_ioremap_resource(&pdev->dev, mem);
 158        if (IS_ERR(clk_wzrd->base))
 159                return PTR_ERR(clk_wzrd->base);
 160
 161        ret = of_property_read_u32(np, "speed-grade", &clk_wzrd->speed_grade);
 162        if (!ret) {
 163                if (clk_wzrd->speed_grade < 1 || clk_wzrd->speed_grade > 3) {
 164                        dev_warn(&pdev->dev, "invalid speed grade '%d'\n",
 165                                 clk_wzrd->speed_grade);
 166                        clk_wzrd->speed_grade = 0;
 167                }
 168        }
 169
 170        clk_wzrd->clk_in1 = devm_clk_get(&pdev->dev, "clk_in1");
 171        if (IS_ERR(clk_wzrd->clk_in1)) {
 172                if (clk_wzrd->clk_in1 != ERR_PTR(-EPROBE_DEFER))
 173                        dev_err(&pdev->dev, "clk_in1 not found\n");
 174                return PTR_ERR(clk_wzrd->clk_in1);
 175        }
 176
 177        clk_wzrd->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
 178        if (IS_ERR(clk_wzrd->axi_clk)) {
 179                if (clk_wzrd->axi_clk != ERR_PTR(-EPROBE_DEFER))
 180                        dev_err(&pdev->dev, "s_axi_aclk not found\n");
 181                return PTR_ERR(clk_wzrd->axi_clk);
 182        }
 183        ret = clk_prepare_enable(clk_wzrd->axi_clk);
 184        if (ret) {
 185                dev_err(&pdev->dev, "enabling s_axi_aclk failed\n");
 186                return ret;
 187        }
 188        rate = clk_get_rate(clk_wzrd->axi_clk);
 189        if (rate > WZRD_ACLK_MAX_FREQ) {
 190                dev_err(&pdev->dev, "s_axi_aclk frequency (%lu) too high\n",
 191                        rate);
 192                ret = -EINVAL;
 193                goto err_disable_clk;
 194        }
 195
 196        /* we don't support fractional div/mul yet */
 197        reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) &
 198                    WZRD_CLkFBOUT_FRAC_EN;
 199        reg |= readl(clk_wzrd->base + WZRD_CLK_CFG_REG(2)) &
 200                     WZRD_CLkOUT0_FRAC_EN;
 201        if (reg)
 202                dev_warn(&pdev->dev, "fractional div/mul not supported\n");
 203
 204        /* register multiplier */
 205        reg = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) &
 206                     WZRD_CLKFBOUT_MULT_MASK) >> WZRD_CLKFBOUT_MULT_SHIFT;
 207        clk_name = kasprintf(GFP_KERNEL, "%s_mul", dev_name(&pdev->dev));
 208        if (!clk_name) {
 209                ret = -ENOMEM;
 210                goto err_disable_clk;
 211        }
 212        clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor(
 213                        &pdev->dev, clk_name,
 214                        __clk_get_name(clk_wzrd->clk_in1),
 215                        0, reg, 1);
 216        kfree(clk_name);
 217        if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul])) {
 218                dev_err(&pdev->dev, "unable to register fixed-factor clock\n");
 219                ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul]);
 220                goto err_disable_clk;
 221        }
 222
 223        /* register div */
 224        reg = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) &
 225                        WZRD_DIVCLK_DIVIDE_MASK) >> WZRD_DIVCLK_DIVIDE_SHIFT;
 226        clk_name = kasprintf(GFP_KERNEL, "%s_mul_div", dev_name(&pdev->dev));
 227        if (!clk_name) {
 228                ret = -ENOMEM;
 229                goto err_rm_int_clk;
 230        }
 231
 232        clk_wzrd->clks_internal[wzrd_clk_mul_div] = clk_register_fixed_factor(
 233                        &pdev->dev, clk_name,
 234                        __clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]),
 235                        0, 1, reg);
 236        if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div])) {
 237                dev_err(&pdev->dev, "unable to register divider clock\n");
 238                ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div]);
 239                goto err_rm_int_clk;
 240        }
 241
 242        /* register div per output */
 243        for (i = WZRD_NUM_OUTPUTS - 1; i >= 0 ; i--) {
 244                const char *clkout_name;
 245
 246                if (of_property_read_string_index(np, "clock-output-names", i,
 247                                                  &clkout_name)) {
 248                        dev_err(&pdev->dev,
 249                                "clock output name not specified\n");
 250                        ret = -EINVAL;
 251                        goto err_rm_int_clks;
 252                }
 253                reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(2) + i * 12);
 254                reg &= WZRD_CLKOUT_DIVIDE_MASK;
 255                reg >>= WZRD_CLKOUT_DIVIDE_SHIFT;
 256                clk_wzrd->clkout[i] = clk_register_fixed_factor(&pdev->dev,
 257                                clkout_name, clk_name, 0, 1, reg);
 258                if (IS_ERR(clk_wzrd->clkout[i])) {
 259                        int j;
 260
 261                        for (j = i + 1; j < WZRD_NUM_OUTPUTS; j++)
 262                                clk_unregister(clk_wzrd->clkout[j]);
 263                        dev_err(&pdev->dev,
 264                                "unable to register divider clock\n");
 265                        ret = PTR_ERR(clk_wzrd->clkout[i]);
 266                        goto err_rm_int_clks;
 267                }
 268        }
 269
 270        kfree(clk_name);
 271
 272        clk_wzrd->clk_data.clks = clk_wzrd->clkout;
 273        clk_wzrd->clk_data.clk_num = ARRAY_SIZE(clk_wzrd->clkout);
 274        of_clk_add_provider(np, of_clk_src_onecell_get, &clk_wzrd->clk_data);
 275
 276        if (clk_wzrd->speed_grade) {
 277                clk_wzrd->nb.notifier_call = clk_wzrd_clk_notifier;
 278
 279                ret = clk_notifier_register(clk_wzrd->clk_in1,
 280                                            &clk_wzrd->nb);
 281                if (ret)
 282                        dev_warn(&pdev->dev,
 283                                 "unable to register clock notifier\n");
 284
 285                ret = clk_notifier_register(clk_wzrd->axi_clk, &clk_wzrd->nb);
 286                if (ret)
 287                        dev_warn(&pdev->dev,
 288                                 "unable to register clock notifier\n");
 289        }
 290
 291        return 0;
 292
 293err_rm_int_clks:
 294        clk_unregister(clk_wzrd->clks_internal[1]);
 295err_rm_int_clk:
 296        kfree(clk_name);
 297        clk_unregister(clk_wzrd->clks_internal[0]);
 298err_disable_clk:
 299        clk_disable_unprepare(clk_wzrd->axi_clk);
 300
 301        return ret;
 302}
 303
 304static int clk_wzrd_remove(struct platform_device *pdev)
 305{
 306        int i;
 307        struct clk_wzrd *clk_wzrd = platform_get_drvdata(pdev);
 308
 309        of_clk_del_provider(pdev->dev.of_node);
 310
 311        for (i = 0; i < WZRD_NUM_OUTPUTS; i++)
 312                clk_unregister(clk_wzrd->clkout[i]);
 313        for (i = 0; i < wzrd_clk_int_max; i++)
 314                clk_unregister(clk_wzrd->clks_internal[i]);
 315
 316        if (clk_wzrd->speed_grade) {
 317                clk_notifier_unregister(clk_wzrd->axi_clk, &clk_wzrd->nb);
 318                clk_notifier_unregister(clk_wzrd->clk_in1, &clk_wzrd->nb);
 319        }
 320
 321        clk_disable_unprepare(clk_wzrd->axi_clk);
 322
 323        return 0;
 324}
 325
 326static const struct of_device_id clk_wzrd_ids[] = {
 327        { .compatible = "xlnx,clocking-wizard" },
 328        { },
 329};
 330MODULE_DEVICE_TABLE(of, clk_wzrd_ids);
 331
 332static struct platform_driver clk_wzrd_driver = {
 333        .driver = {
 334                .name = "clk-wizard",
 335                .of_match_table = clk_wzrd_ids,
 336                .pm = &clk_wzrd_dev_pm_ops,
 337        },
 338        .probe = clk_wzrd_probe,
 339        .remove = clk_wzrd_remove,
 340};
 341module_platform_driver(clk_wzrd_driver);
 342
 343MODULE_LICENSE("GPL");
 344MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com");
 345MODULE_DESCRIPTION("Driver for the Xilinx Clocking Wizard IP core");
 346