]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - drivers/clk/bcm/clk-bcm2835.c
clk-bcm2835: Read max core clock from firmware
[mirror_ubuntu-zesty-kernel.git] / drivers / clk / bcm / clk-bcm2835.c
index 0d14409097e777ce4546de30e9278fdebf74ec44..41dccc4ebe6f367054822aed11cb0cd4d985c21a 100644 (file)
@@ -44,6 +44,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <dt-bindings/clock/bcm2835.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
 
 #define CM_PASSWORD            0x5a000000
 
 #define LOCK_TIMEOUT_NS                100000000
 #define BCM2835_MAX_FB_RATE    1750000000u
 
+#define VCMSG_ID_CORE_CLOCK     4
+
 struct bcm2835_cprman {
        struct device *dev;
        void __iomem *regs;
+       struct rpi_firmware *fw;
        spinlock_t regs_lock; /* spinlock for all clocks */
        const char *osc_name;
 
@@ -535,8 +539,10 @@ static unsigned long bcm2835_pll_get_rate(struct clk_hw *hw,
        using_prediv = cprman_read(cprman, data->ana_reg_base + 4) &
                data->ana->fb_prediv_mask;
 
-       if (using_prediv)
+       if (using_prediv) {
                ndiv *= 2;
+               fdiv *= 2;
+       }
 
        return bcm2835_pll_rate_from_divisors(parent_rate, ndiv, fdiv, pdiv);
 }
@@ -934,6 +940,30 @@ static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw,
        return bcm2835_clock_rate_from_divisor(clock, parent_rate, div);
 }
 
+static unsigned long bcm2835_clock_get_rate_vpu(struct clk_hw *hw,
+                                               unsigned long parent_rate)
+{
+       struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
+       struct bcm2835_cprman *cprman = clock->cprman;
+
+       if (cprman->fw) {
+               struct {
+                       u32 id;
+                       u32 val;
+               } packet;
+
+               packet.id = VCMSG_ID_CORE_CLOCK;
+               packet.val = 0;
+
+               if (!rpi_firmware_property(cprman->fw,
+                                          RPI_FIRMWARE_GET_MAX_CLOCK_RATE,
+                                          &packet, sizeof(packet)))
+                       return packet.val;
+       }
+
+       return bcm2835_clock_get_rate(hw, parent_rate);
+}
+
 static void bcm2835_clock_wait_busy(struct bcm2835_clock *clock)
 {
        struct bcm2835_cprman *cprman = clock->cprman;
@@ -1190,7 +1220,7 @@ static int bcm2835_vpu_clock_is_on(struct clk_hw *hw)
  */
 static const struct clk_ops bcm2835_vpu_clock_clk_ops = {
        .is_prepared = bcm2835_vpu_clock_is_on,
-       .recalc_rate = bcm2835_clock_get_rate,
+       .recalc_rate = bcm2835_clock_get_rate_vpu,
        .set_rate = bcm2835_clock_set_rate,
        .determine_rate = bcm2835_clock_determine_rate,
        .set_parent = bcm2835_clock_set_parent,
@@ -1198,6 +1228,8 @@ static const struct clk_ops bcm2835_vpu_clock_clk_ops = {
        .debug_init = bcm2835_clock_debug_init,
 };
 
+static bool bcm2835_clk_is_claimed(const char *name);
+
 static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman,
                                           const struct bcm2835_pll_data *data)
 {
@@ -1214,6 +1246,9 @@ static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman,
        init.ops = &bcm2835_pll_clk_ops;
        init.flags = CLK_IGNORE_UNUSED;
 
+       if (!bcm2835_clk_is_claimed(data->name))
+               init.flags |= CLK_IS_CRITICAL;
+
        pll = kzalloc(sizeof(*pll), GFP_KERNEL);
        if (!pll)
                return NULL;
@@ -1266,6 +1301,13 @@ bcm2835_register_pll_divider(struct bcm2835_cprman *cprman,
        divider->div.hw.init = &init;
        divider->div.table = NULL;
 
+       if (!(cprman_read(cprman, data->cm_reg) & data->hold_mask)) {
+               if (!bcm2835_clk_is_claimed(data->source_pll))
+                       init.flags |= CLK_IS_CRITICAL;
+               if (!bcm2835_clk_is_claimed(data->name))
+                       divider->div.flags |= CLK_IS_CRITICAL;
+       }
+
        divider->cprman = cprman;
        divider->data = data;
 
@@ -1896,6 +1938,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
                .ctl_reg = CM_PERIICTL),
 };
 
+static bool bcm2835_clk_claimed[ARRAY_SIZE(clk_desc_array)];
+
 /*
  * Permanently take a reference on the parent of the SDRAM clock.
  *
@@ -1915,6 +1959,19 @@ static int bcm2835_mark_sdc_parent_critical(struct clk *sdc)
        return clk_prepare_enable(parent);
 }
 
+static bool bcm2835_clk_is_claimed(const char *name)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(clk_desc_array); i++) {
+               const char *clk_name = *(const char **)(clk_desc_array[i].data);
+               if (!strcmp(name, clk_name))
+                   return bcm2835_clk_claimed[i];
+       }
+
+       return false;
+}
+
 static int bcm2835_clk_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -1923,7 +1980,9 @@ static int bcm2835_clk_probe(struct platform_device *pdev)
        struct resource *res;
        const struct bcm2835_clk_desc *desc;
        const size_t asize = ARRAY_SIZE(clk_desc_array);
+       struct device_node *fw_node;
        size_t i;
+       u32 clk_id;
        int ret;
 
        cprman = devm_kzalloc(dev, sizeof(*cprman) +
@@ -1939,6 +1998,21 @@ static int bcm2835_clk_probe(struct platform_device *pdev)
        if (IS_ERR(cprman->regs))
                return PTR_ERR(cprman->regs);
 
+       fw_node = of_parse_phandle(dev->of_node, "firmware", 0);
+       if (fw_node) {
+               struct rpi_firmware *fw = rpi_firmware_get(NULL);
+               if (!fw)
+                       return -EPROBE_DEFER;
+               cprman->fw = fw;
+       }
+
+       memset(bcm2835_clk_claimed, 0, sizeof(bcm2835_clk_claimed));
+       for (i = 0;
+            !of_property_read_u32_index(pdev->dev.of_node, "claim-clocks",
+                                        i, &clk_id);
+            i++)
+               bcm2835_clk_claimed[clk_id]= true;
+
        cprman->osc_name = of_clk_get_parent_name(dev->of_node, 0);
        if (!cprman->osc_name)
                return -ENODEV;
@@ -1958,8 +2032,15 @@ static int bcm2835_clk_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
+       ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
                                      &cprman->onecell);
+       if (ret)
+               return ret;
+
+       /* note that we have registered all the clocks */
+       dev_dbg(dev, "registered %d clocks\n", asize);
+
+       return 0;
 }
 
 static const struct of_device_id bcm2835_clk_of_match[] = {
@@ -1976,7 +2057,11 @@ static struct platform_driver bcm2835_clk_driver = {
        .probe          = bcm2835_clk_probe,
 };
 
-builtin_platform_driver(bcm2835_clk_driver);
+static int __init __bcm2835_clk_driver_init(void)
+{
+       return platform_driver_register(&bcm2835_clk_driver);
+}
+core_initcall(__bcm2835_clk_driver_init);
 
 MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
 MODULE_DESCRIPTION("BCM2835 clock driver");