return &raspberrypi_clk_pllb_arm.hw;
 }
 
+static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw,
+                                             struct clk_rate_request *req)
+{
+       /*
+        * The firmware will do the rounding but that isn't part of
+        * the interface with the firmware, so we just do our best
+        * here.
+        */
+       req->rate = clamp(req->rate, req->min_rate, req->max_rate);
+       return 0;
+}
+
+static const struct clk_ops raspberrypi_firmware_clk_ops = {
+       .is_prepared    = raspberrypi_fw_is_prepared,
+       .recalc_rate    = raspberrypi_fw_get_rate,
+       .determine_rate = raspberrypi_fw_dumb_determine_rate,
+       .set_rate       = raspberrypi_fw_set_rate,
+};
+
+static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi,
+                                              unsigned int parent,
+                                              unsigned int id)
+{
+       struct raspberrypi_clk_data *data;
+       struct clk_init_data init = {};
+       u32 min_rate, max_rate;
+       int ret;
+
+       if (id == RPI_FIRMWARE_ARM_CLK_ID) {
+               struct clk_hw *hw;
+
+               hw = raspberrypi_register_pllb(rpi);
+               if (IS_ERR(hw)) {
+                       dev_err(rpi->dev, "Failed to initialize pllb, %ld\n",
+                               PTR_ERR(hw));
+                       return hw;
+               }
+
+               return raspberrypi_register_pllb_arm(rpi);
+       }
+
+       data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return ERR_PTR(-ENOMEM);
+       data->rpi = rpi;
+       data->id = id;
+
+       init.name = devm_kasprintf(rpi->dev, GFP_KERNEL, "fw-clk-%u", id);
+       init.ops = &raspberrypi_firmware_clk_ops;
+       init.flags = CLK_GET_RATE_NOCACHE;
+
+       data->hw.init = &init;
+
+       ret = raspberrypi_clock_property(rpi->firmware, data,
+                                        RPI_FIRMWARE_GET_MIN_CLOCK_RATE,
+                                        &min_rate);
+       if (ret) {
+               dev_err(rpi->dev, "Failed to get clock %d min freq: %d",
+                       id, ret);
+               return ERR_PTR(ret);
+       }
+
+       ret = raspberrypi_clock_property(rpi->firmware, data,
+                                        RPI_FIRMWARE_GET_MAX_CLOCK_RATE,
+                                        &max_rate);
+       if (ret) {
+               dev_err(rpi->dev, "Failed to get clock %d max freq: %d\n",
+                       id, ret);
+               return ERR_PTR(ret);
+       }
+
+       ret = devm_clk_hw_register(rpi->dev, &data->hw);
+       if (ret)
+               return ERR_PTR(ret);
+
+       clk_hw_set_rate_range(&data->hw, min_rate, max_rate);
+
+       if (id == RPI_FIRMWARE_ARM_CLK_ID) {
+               ret = devm_clk_hw_register_clkdev(rpi->dev, &data->hw,
+                                                 NULL, "cpu0");
+               if (ret) {
+                       dev_err(rpi->dev, "Failed to initialize clkdev\n");
+                       return ERR_PTR(ret);
+               }
+       }
+
+       return &data->hw;
+}
+
+struct rpi_firmware_get_clocks_response {
+       u32 parent;
+       u32 id;
+};
+
+static int raspberrypi_discover_clocks(struct raspberrypi_clk *rpi,
+                                      struct clk_hw_onecell_data *data)
+{
+       struct rpi_firmware_get_clocks_response *clks;
+       int ret;
+
+       clks = devm_kcalloc(rpi->dev,
+                           sizeof(*clks), RPI_FIRMWARE_NUM_CLK_ID,
+                           GFP_KERNEL);
+       if (!clks)
+               return -ENOMEM;
+
+       ret = rpi_firmware_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCKS,
+                                   clks,
+                                   sizeof(*clks) * RPI_FIRMWARE_NUM_CLK_ID);
+       if (ret)
+               return ret;
+
+       while (clks->id) {
+               struct clk_hw *hw;
+
+               switch (clks->id) {
+               case RPI_FIRMWARE_ARM_CLK_ID:
+               case RPI_FIRMWARE_CORE_CLK_ID:
+               case RPI_FIRMWARE_M2MC_CLK_ID:
+               case RPI_FIRMWARE_V3D_CLK_ID:
+                       hw = raspberrypi_clk_register(rpi, clks->parent,
+                                                     clks->id);
+                       if (IS_ERR(hw))
+                               return PTR_ERR(hw);
+
+                       data->hws[clks->id] = hw;
+                       data->num = clks->id + 1;
+                       fallthrough;
+
+               default:
+                       clks++;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
 static int raspberrypi_clk_probe(struct platform_device *pdev)
 {
        struct clk_hw_onecell_data *clk_data;
        struct device *dev = &pdev->dev;
        struct rpi_firmware *firmware;
        struct raspberrypi_clk *rpi;
-       struct clk_hw *hw;
        int ret;
 
        /*
        if (!clk_data)
                return -ENOMEM;
 
-       hw = raspberrypi_register_pllb(rpi);
-       if (IS_ERR(hw)) {
-               dev_err(dev, "Failed to initialize pllb, %ld\n", PTR_ERR(hw));
-               return PTR_ERR(hw);
-       }
-
-       hw = raspberrypi_register_pllb_arm(rpi);
-       if (IS_ERR(hw))
-               return PTR_ERR(hw);
-       clk_data->hws[RPI_FIRMWARE_ARM_CLK_ID] = hw;
-       clk_data->num = RPI_FIRMWARE_ARM_CLK_ID + 1;
+       ret = raspberrypi_discover_clocks(rpi, clk_data);
+       if (ret)
+               return ret;
 
        ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
                                          clk_data);