]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
clk-bcm2835: Read max core clock from firmware
authorPhil Elwell <phil@raspberrypi.org>
Mon, 6 Mar 2017 09:06:18 +0000 (09:06 +0000)
committerKleber Sacilotto de Souza <kleber.souza@canonical.com>
Tue, 19 Sep 2017 10:07:53 +0000 (12:07 +0200)
The VPU is responsible for managing the core clock, usually under
direction from the bcm2835-cpufreq driver but not via the clk-bcm2835
driver. Since the core frequency can change without warning, it is
safer to report the maximum clock rate to users of the core clock -
I2C, SPI and the mini UART - to err on the safe side when calculating
clock divisors.

If the DT node for the clock driver includes a reference to the
firmware node, use the firmware API to query the maximum core clock
instead of reading the divider registers.

Prior to this patch, a "100KHz" I2C bus was sometimes clocked at about
160KHz. In particular, switching to the 4.9 kernel was likely to break
SenseHAT usage on a Pi3.

Signed-off-by: Phil Elwell <phil@raspberrypi.org>
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
arch/arm/boot/dts/bcm2708-rpi.dtsi
drivers/clk/bcm/clk-bcm2835.c

index 055090ace687b94e4d25de65d1b8fbf7f730be9e..ef14e9ac6cd2092efb1681682dd2d3c52b8abfd5 100644 (file)
 &usb {
        power-domains = <&power RPI_POWER_DOMAIN_USB>;
 };
+
+&clocks {
+       firmware = <&firmware>;
+};
index ef0d05c58573f62de3632a920dfc19888c019d18..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;
 
@@ -936,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;
@@ -1192,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,
@@ -1952,6 +1980,7 @@ 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;
@@ -1969,6 +1998,14 @@ 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",