linux/drivers/clk/sunxi/clk-sun6i-ar100.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2014 Free Electrons
   4 *
   5 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
   6 *
   7 * Allwinner A31 AR100 clock driver
   8 */
   9
  10#include <linux/bitops.h>
  11#include <linux/clk-provider.h>
  12#include <linux/init.h>
  13#include <linux/of.h>
  14#include <linux/platform_device.h>
  15#include <linux/spinlock.h>
  16
  17#include "clk-factors.h"
  18
  19/**
  20 * sun6i_get_ar100_factors - Calculates factors p, m for AR100
  21 *
  22 * AR100 rate is calculated as follows
  23 * rate = (parent_rate >> p) / (m + 1);
  24 */
  25static void sun6i_get_ar100_factors(struct factors_request *req)
  26{
  27        unsigned long div;
  28        int shift;
  29
  30        /* clock only divides */
  31        if (req->rate > req->parent_rate)
  32                req->rate = req->parent_rate;
  33
  34        div = DIV_ROUND_UP(req->parent_rate, req->rate);
  35
  36        if (div < 32)
  37                shift = 0;
  38        else if (div >> 1 < 32)
  39                shift = 1;
  40        else if (div >> 2 < 32)
  41                shift = 2;
  42        else
  43                shift = 3;
  44
  45        div >>= shift;
  46
  47        if (div > 32)
  48                div = 32;
  49
  50        req->rate = (req->parent_rate >> shift) / div;
  51        req->m = div - 1;
  52        req->p = shift;
  53}
  54
  55static const struct clk_factors_config sun6i_ar100_config = {
  56        .mwidth = 5,
  57        .mshift = 8,
  58        .pwidth = 2,
  59        .pshift = 4,
  60};
  61
  62static const struct factors_data sun6i_ar100_data = {
  63        .mux = 16,
  64        .muxmask = GENMASK(1, 0),
  65        .table = &sun6i_ar100_config,
  66        .getter = sun6i_get_ar100_factors,
  67};
  68
  69static DEFINE_SPINLOCK(sun6i_ar100_lock);
  70
  71static int sun6i_a31_ar100_clk_probe(struct platform_device *pdev)
  72{
  73        struct device_node *np = pdev->dev.of_node;
  74        struct resource *r;
  75        void __iomem *reg;
  76        struct clk *clk;
  77
  78        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  79        reg = devm_ioremap_resource(&pdev->dev, r);
  80        if (IS_ERR(reg))
  81                return PTR_ERR(reg);
  82
  83        clk = sunxi_factors_register(np, &sun6i_ar100_data, &sun6i_ar100_lock,
  84                                     reg);
  85        if (!clk)
  86                return -ENOMEM;
  87
  88        platform_set_drvdata(pdev, clk);
  89
  90        return 0;
  91}
  92
  93static const struct of_device_id sun6i_a31_ar100_clk_dt_ids[] = {
  94        { .compatible = "allwinner,sun6i-a31-ar100-clk" },
  95        { /* sentinel */ }
  96};
  97
  98static struct platform_driver sun6i_a31_ar100_clk_driver = {
  99        .driver = {
 100                .name = "sun6i-a31-ar100-clk",
 101                .of_match_table = sun6i_a31_ar100_clk_dt_ids,
 102                .suppress_bind_attrs = true,
 103        },
 104        .probe = sun6i_a31_ar100_clk_probe,
 105};
 106builtin_platform_driver(sun6i_a31_ar100_clk_driver);
 107