{"id":815414,"url":"http://patchwork.ozlabs.org/api/1.2/patches/815414/?format=json","web_url":"http://patchwork.ozlabs.org/project/devicetree-bindings/patch/1505817286-12445-1-git-send-email-tirupath@codeaurora.org/","project":{"id":37,"url":"http://patchwork.ozlabs.org/api/1.2/projects/37/?format=json","name":"Devicetree Bindings","link_name":"devicetree-bindings","list_id":"devicetree.vger.kernel.org","list_email":"devicetree@vger.kernel.org","web_url":"","scm_url":"","webscm_url":"","list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<1505817286-12445-1-git-send-email-tirupath@codeaurora.org>","list_archive_url":null,"date":"2017-09-19T10:34:46","name":"[V5] clk: qcom: Add spmi_pmic clock divider support","commit_ref":null,"pull_url":null,"state":"not-applicable","archived":true,"hash":"dd65adcd48fb67e0a12f1b51499e4434ae28d802","submitter":{"id":71968,"url":"http://patchwork.ozlabs.org/api/1.2/people/71968/?format=json","name":"Tirupathi Reddy","email":"tirupath@codeaurora.org"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/devicetree-bindings/patch/1505817286-12445-1-git-send-email-tirupath@codeaurora.org/mbox/","series":[{"id":3829,"url":"http://patchwork.ozlabs.org/api/1.2/series/3829/?format=json","web_url":"http://patchwork.ozlabs.org/project/devicetree-bindings/list/?series=3829","date":"2017-09-19T10:34:46","name":"[V5] clk: qcom: Add spmi_pmic clock divider support","version":5,"mbox":"http://patchwork.ozlabs.org/series/3829/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/815414/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/815414/checks/","tags":{},"related":[],"headers":{"Return-Path":"<devicetree-owner@vger.kernel.org>","X-Original-To":"incoming-dt@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming-dt@bilbo.ozlabs.org","Authentication-Results":["ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=devicetree-owner@vger.kernel.org; receiver=<UNKNOWN>)","ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=codeaurora.org header.i=@codeaurora.org\n\theader.b=\"VAvow0SV\"; \n\tdkim=fail reason=\"signature verification failed\" (1024-bit key)\n\theader.d=codeaurora.org header.i=@codeaurora.org header.b=\"pKHoE2S5\"; \n\tdkim-atps=neutral","pdx-caf-mail.web.codeaurora.org;\n\tdmarc=none (p=none dis=none) header.from=codeaurora.org","pdx-caf-mail.web.codeaurora.org;\n\tspf=none smtp.mailfrom=tirupath@codeaurora.org"],"Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xxK5L6PJMz9sBZ\n\tfor <incoming-dt@patchwork.ozlabs.org>;\n\tTue, 19 Sep 2017 20:35:18 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1751348AbdISKfE (ORCPT\n\t<rfc822;incoming-dt@patchwork.ozlabs.org>);\n\tTue, 19 Sep 2017 06:35:04 -0400","from smtp.codeaurora.org ([198.145.29.96]:36082 \"EHLO\n\tsmtp.codeaurora.org\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S1751205AbdISKfC (ORCPT\n\t<rfc822; devicetree@vger.kernel.org>); Tue, 19 Sep 2017 06:35:02 -0400","by smtp.codeaurora.org (Postfix, from userid 1000)\n\tid E737960726; Tue, 19 Sep 2017 10:35:01 +0000 (UTC)","from tirupath-linux.qualcomm.com\n\t(blr-c-bdr-fw-01_globalnat_allzones-outside.qualcomm.com\n\t[103.229.19.19])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits))\n\t(No client certificate requested)\n\t(Authenticated sender: tirupath@smtp.codeaurora.org)\n\tby smtp.codeaurora.org (Postfix) with ESMTPSA id 6310F602B3;\n\tTue, 19 Sep 2017 10:34:56 +0000 (UTC)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org;\n\ts=default; t=1505817301;\n\tbh=LX0+R73dpGd+iBTzY25mTTdzj/B4DPr5MPLZ1Vyp0iQ=;\n\th=From:To:Cc:Subject:Date:From;\n\tb=VAvow0SV4ZEpLCwY5y9U/Du4ul+qAVdypkejJusSz4nNzsAN1IW+Sa8C/W+ObEE+P\n\t5F9kqe14J1gZk2B3SivNON+E3C1Hz+EWTgs7pQ7fieubKIJhbJpMaps1NYKoIwkLAV\n\tD1NGD98nFaxhWGfvEi/IMH7Ev6qlbn3LAlzIBMLg=","v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org;\n\ts=default; t=1505817299;\n\tbh=LX0+R73dpGd+iBTzY25mTTdzj/B4DPr5MPLZ1Vyp0iQ=;\n\th=From:To:Cc:Subject:Date:From;\n\tb=pKHoE2S5EHaPZRObytjW42Q0Do9OVi6S8wOgFSSNJ8GUPWq4JeEpuWWbxDjoS0hqQ\n\t3MMoidaY+JoukyECJXlr1lPTS3Ju4przDC1yMjnufC40Vnn8/uOyLkBIFkU6n+AEN4\n\tB8WhRCg7zT8aivBA5NUr1xsYAUy0CLOtv49QyPgs="],"X-Spam-Checker-Version":"SpamAssassin 3.4.0 (2014-02-07) on\n\tpdx-caf-mail.web.codeaurora.org","X-Spam-Level":"","X-Spam-Status":"No, score=-2.8 required=2.0 tests=ALL_TRUSTED,BAYES_00,\n\tDKIM_SIGNED,\n\tT_DKIM_INVALID autolearn=no autolearn_force=no version=3.4.0","DMARC-Filter":"OpenDMARC Filter v1.3.2 smtp.codeaurora.org 6310F602B3","From":"Tirupathi Reddy <tirupath@codeaurora.org>","To":"sboyd@codeaurora.org","Cc":"mturquette@baylibre.com, robh+dt@kernel.org, mark.rutland@arm.com,\n\tandy.gross@linaro.org, david.brown@linaro.org,\n\tlinux-clk@vger.kernel.org, devicetree@vger.kernel.org,\n\tlinux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,\n\tlinux-soc@vger.kernel.org, Tirupathi Reddy <tirupath@codeaurora.org>","Subject":"[PATCH V5] clk: qcom: Add spmi_pmic clock divider support","Date":"Tue, 19 Sep 2017 16:04:46 +0530","Message-Id":"<1505817286-12445-1-git-send-email-tirupath@codeaurora.org>","X-Mailer":"git-send-email 1.9.1","Sender":"devicetree-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<devicetree.vger.kernel.org>","X-Mailing-List":"devicetree@vger.kernel.org"},"content":"Clkdiv module provides a clock output on the PMIC with CXO as\nthe source. This clock can be routed through PMIC GPIOs. Add\na device driver to configure this clkdiv module.\n\nSigned-off-by: Tirupathi Reddy <tirupath@codeaurora.org>\n---\n .../bindings/clock/clk-spmi-pmic-div.txt           |  56 ++++\n drivers/clk/qcom/Kconfig                           |   9 +\n drivers/clk/qcom/Makefile                          |   1 +\n drivers/clk/qcom/clk-spmi-pmic-div.c               | 343 +++++++++++++++++++++\n 4 files changed, 409 insertions(+)\n create mode 100644 Documentation/devicetree/bindings/clock/clk-spmi-pmic-div.txt\n create mode 100644 drivers/clk/qcom/clk-spmi-pmic-div.c","diff":"diff --git a/Documentation/devicetree/bindings/clock/clk-spmi-pmic-div.txt b/Documentation/devicetree/bindings/clock/clk-spmi-pmic-div.txt\nnew file mode 100644\nindex 0000000..e37a136\n--- /dev/null\n+++ b/Documentation/devicetree/bindings/clock/clk-spmi-pmic-div.txt\n@@ -0,0 +1,56 @@\n+Qualcomm Technologies, Inc. SPMI PMIC clock divider (clkdiv)\n+\n+clkdiv configures the clock frequency of a set of outputs on the PMIC.\n+These clocks are typically wired through alternate functions on\n+gpio pins.\n+\n+=======================\n+Properties\n+=======================\n+\n+- compatible\n+\tUsage:      required\n+\tValue type: <string>\n+\tDefinition: must be one of:\n+\t\t    \"qcom,spmi-clkdiv\"\n+\t\t    \"qcom,pm8998-clkdiv\"\n+\n+- reg\n+\tUsage:      required\n+\tValue type: <prop-encoded-array>\n+\tDefinition: Addresses and sizes for the memory of this CLKDIV\n+\t\t    peripheral.\n+\n+- clocks:\n+\tUsage: required\n+\tValue type: <prop-encoded-array>\n+\tDefinition: reference to the xo clock.\n+\n+- clock-names:\n+\tUsage: required\n+\tValue type: <stringlist>\n+\tDefinition: must be \"xo\".\n+\n+- clock-cells:\n+\tUsage: required\n+\tValue type: <u32>\n+\tDefinition: shall contain 1.\n+\n+=======\n+Example\n+=======\n+\n+pm8998_clk_divs: qcom,clock@5b00 {\n+\tcompatible = \"qcom,pm8998-clkdiv\";\n+\treg = <0x5b00>;\n+\t#clock-cells = <1>;\n+\tclocks = <&xo_board>;\n+\tclock-names = \"xo\";\n+\n+\tassigned-clocks = <&pm8998_clk_divs 1>,\n+\t\t\t  <&pm8998_clk_divs 2>,\n+\t\t\t  <&pm8998_clk_divs 3>;\n+\tassigned-clock-rates = <9600000>,\n+\t\t\t       <9600000>,\n+\t\t\t       <9600000>;\n+};\ndiff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig\nindex 9f6c278..dd5b18e 100644\n--- a/drivers/clk/qcom/Kconfig\n+++ b/drivers/clk/qcom/Kconfig\n@@ -196,3 +196,12 @@ config MSM_MMCC_8996\n \t  Support for the multimedia clock controller on msm8996 devices.\n \t  Say Y if you want to support multimedia devices such as display,\n \t  graphics, video encode/decode, camera, etc.\n+\n+config SPMI_PMIC_CLKDIV\n+\ttristate \"spmi pmic clkdiv driver\"\n+\tdepends on (COMMON_CLK_QCOM && SPMI) || COMPILE_TEST\n+\thelp\n+\t  This driver supports the clkdiv functionality on the Qualcomm\n+\t  Technologies, Inc. SPMI PMIC. It configures the frequency of\n+\t  clkdiv outputs on the PMIC. These clocks are typically wired\n+\t  through alternate functions on gpio pins.\ndiff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile\nindex 3f3aff2..4d4c62b 100644\n--- a/drivers/clk/qcom/Makefile\n+++ b/drivers/clk/qcom/Makefile\n@@ -33,3 +33,4 @@ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o\n obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o\n obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o\n obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o\n+obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o\ndiff --git a/drivers/clk/qcom/clk-spmi-pmic-div.c b/drivers/clk/qcom/clk-spmi-pmic-div.c\nnew file mode 100644\nindex 0000000..af343ad\n--- /dev/null\n+++ b/drivers/clk/qcom/clk-spmi-pmic-div.c\n@@ -0,0 +1,343 @@\n+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License version 2 and\n+ * only version 2 as published by the Free Software Foundation.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ */\n+\n+#include <linux/bitops.h>\n+#include <linux/clk.h>\n+#include <linux/clk-provider.h>\n+#include <linux/delay.h>\n+#include <linux/err.h>\n+#include <linux/log2.h>\n+#include <linux/module.h>\n+#include <linux/of.h>\n+#include <linux/platform_device.h>\n+#include <linux/regmap.h>\n+#include <linux/slab.h>\n+#include <linux/types.h>\n+\n+#define REG_DIV_CTL1\t\t\t0x43\n+#define DIV_CTL1_DIV_FACTOR_MASK\tGENMASK(2, 0)\n+\n+#define REG_EN_CTL\t\t\t0x46\n+#define REG_EN_MASK\t\t\tBIT(7)\n+\n+#define ENABLE_DELAY_NS(cxo_ns, div)\t((2 + 3 * div) * cxo_ns)\n+#define DISABLE_DELAY_NS(cxo_ns, div)\t((3 * div) * cxo_ns)\n+\n+#define CLK_SPMI_PMIC_DIV_OFFSET\t1\n+\n+#define CLKDIV_XO_DIV_1_0\t\t0\n+#define CLKDIV_XO_DIV_1\t\t\t1\n+#define CLKDIV_XO_DIV_2\t\t\t2\n+#define CLKDIV_XO_DIV_4\t\t\t3\n+#define CLKDIV_XO_DIV_8\t\t\t4\n+#define CLKDIV_XO_DIV_16\t\t5\n+#define CLKDIV_XO_DIV_32\t\t6\n+#define CLKDIV_XO_DIV_64\t\t7\n+#define CLKDIV_MAX_ALLOWED\t\t8\n+\n+struct clkdiv {\n+\tstruct regmap\t\t*regmap;\n+\tu16\t\t\tbase;\n+\tspinlock_t\t\tlock;\n+\n+\t/* clock properties */\n+\tstruct clk_hw\t\thw;\n+\tunsigned int\t\tdiv_factor;\n+\tunsigned int\t\tcxo_period_ns;\n+};\n+\n+static inline struct clkdiv *to_clkdiv(struct clk_hw *hw)\n+{\n+\treturn container_of(hw, struct clkdiv, hw);\n+}\n+\n+static inline unsigned int div_factor_to_div(unsigned int div_factor)\n+{\n+\tif (div_factor == CLKDIV_XO_DIV_1_0)\n+\t\treturn 1;\n+\n+\treturn 1 << (div_factor - CLK_SPMI_PMIC_DIV_OFFSET);\n+}\n+\n+static inline unsigned int div_to_div_factor(unsigned int div)\n+{\n+\treturn min(ilog2(div) + CLK_SPMI_PMIC_DIV_OFFSET,\n+\t\t   CLKDIV_MAX_ALLOWED - 1);\n+}\n+\n+static bool is_spmi_pmic_clkdiv_enabled(struct clkdiv *clkdiv)\n+{\n+\tunsigned int val = 0;\n+\n+\tregmap_read(clkdiv->regmap, clkdiv->base + REG_EN_CTL,\n+\t\t\t &val);\n+\treturn (val & REG_EN_MASK) ? true : false;\n+}\n+\n+static int spmi_pmic_clkdiv_set_enable_state(struct clkdiv *clkdiv,\n+\t\t\tbool enable_state)\n+{\n+\tint rc;\n+\n+\trc = regmap_update_bits(clkdiv->regmap, clkdiv->base + REG_EN_CTL,\n+\t\t\t\tREG_EN_MASK,\n+\t\t\t\t(enable_state == true) ? REG_EN_MASK : 0);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tif (enable_state == true)\n+\t\tndelay(ENABLE_DELAY_NS(clkdiv->cxo_period_ns,\n+\t\t\t\tdiv_factor_to_div(clkdiv->div_factor)));\n+\telse\n+\t\tndelay(DISABLE_DELAY_NS(clkdiv->cxo_period_ns,\n+\t\t\t\tdiv_factor_to_div(clkdiv->div_factor)));\n+\n+\treturn rc;\n+}\n+\n+static int spmi_pmic_clkdiv_config_freq_div(struct clkdiv *clkdiv,\n+\t\t\tunsigned int div)\n+{\n+\tunsigned int div_factor;\n+\tunsigned long flags;\n+\tbool enabled;\n+\tint rc;\n+\n+\tdiv_factor = div_to_div_factor(div);\n+\n+\tspin_lock_irqsave(&clkdiv->lock, flags);\n+\n+\tenabled = is_spmi_pmic_clkdiv_enabled(clkdiv);\n+\tif (enabled) {\n+\t\trc = spmi_pmic_clkdiv_set_enable_state(clkdiv, false);\n+\t\tif (rc)\n+\t\t\tgoto fail;\n+\t}\n+\n+\trc = regmap_update_bits(clkdiv->regmap,\n+\t\t\t\tclkdiv->base + REG_DIV_CTL1,\n+\t\t\t\tDIV_CTL1_DIV_FACTOR_MASK, div_factor);\n+\tif (rc)\n+\t\tgoto fail;\n+\n+\tclkdiv->div_factor = div_factor;\n+\n+\tif (enabled)\n+\t\trc = spmi_pmic_clkdiv_set_enable_state(clkdiv, true);\n+\n+fail:\n+\tspin_unlock_irqrestore(&clkdiv->lock, flags);\n+\treturn rc;\n+}\n+\n+static int clk_spmi_pmic_div_enable(struct clk_hw *hw)\n+{\n+\tstruct clkdiv *clkdiv = to_clkdiv(hw);\n+\tunsigned long flags;\n+\tint rc;\n+\n+\tspin_lock_irqsave(&clkdiv->lock, flags);\n+\trc = spmi_pmic_clkdiv_set_enable_state(clkdiv, true);\n+\tspin_unlock_irqrestore(&clkdiv->lock, flags);\n+\n+\treturn rc;\n+}\n+\n+static void clk_spmi_pmic_div_disable(struct clk_hw *hw)\n+{\n+\tstruct clkdiv *clkdiv = to_clkdiv(hw);\n+\tunsigned long flags;\n+\n+\tspin_lock_irqsave(&clkdiv->lock, flags);\n+\tspmi_pmic_clkdiv_set_enable_state(clkdiv, false);\n+\tspin_unlock_irqrestore(&clkdiv->lock, flags);\n+}\n+\n+static long clk_spmi_pmic_div_round_rate(struct clk_hw *hw, unsigned long rate,\n+\t\t\tunsigned long *parent_rate)\n+{\n+\tunsigned long new_rate;\n+\tunsigned int div, div_factor;\n+\n+\tdiv = DIV_ROUND_UP(*parent_rate, rate);\n+\tdiv_factor = div_to_div_factor(div);\n+\tnew_rate = *parent_rate / div_factor_to_div(div_factor);\n+\n+\treturn new_rate;\n+}\n+\n+static unsigned long clk_spmi_pmic_div_recalc_rate(struct clk_hw *hw,\n+\t\t\tunsigned long parent_rate)\n+{\n+\tstruct clkdiv *clkdiv = to_clkdiv(hw);\n+\tunsigned long rate;\n+\n+\trate = parent_rate / div_factor_to_div(clkdiv->div_factor);\n+\n+\treturn rate;\n+}\n+\n+static int clk_spmi_pmic_div_set_rate(struct clk_hw *hw, unsigned long rate,\n+\t\t\tunsigned long parent_rate)\n+{\n+\tstruct clkdiv *clkdiv = to_clkdiv(hw);\n+\n+\treturn spmi_pmic_clkdiv_config_freq_div(clkdiv, parent_rate / rate);\n+}\n+\n+static const struct clk_ops clk_spmi_pmic_div_ops = {\n+\t.enable = clk_spmi_pmic_div_enable,\n+\t.disable = clk_spmi_pmic_div_disable,\n+\t.set_rate = clk_spmi_pmic_div_set_rate,\n+\t.recalc_rate = clk_spmi_pmic_div_recalc_rate,\n+\t.round_rate = clk_spmi_pmic_div_round_rate,\n+};\n+\n+struct spmi_pmic_div_clk_cc {\n+\tstruct clk_hw\t**div_clks;\n+\tint\t\tnclks;\n+};\n+\n+#define SPMI_PMIC_CLKDIV_MIN_INDEX\t1\n+\n+static struct clk_hw *spmi_pmic_div_clk_hw_get(struct of_phandle_args *clkspec,\n+\t\t\t\t      void *data)\n+{\n+\tstruct spmi_pmic_div_clk_cc *clk_cc = data;\n+\tunsigned int idx = (clkspec->args[0] - SPMI_PMIC_CLKDIV_MIN_INDEX);\n+\n+\tif (idx < 0 || idx >= clk_cc->nclks) {\n+\t\tpr_err(\"%s: index value %u is invalid; allowed range: [%d, %d]\\n\",\n+\t\t       __func__, clkspec->args[0], SPMI_PMIC_CLKDIV_MIN_INDEX,\n+\t\t       clk_cc->nclks);\n+\t\treturn ERR_PTR(-EINVAL);\n+\t}\n+\n+\treturn clk_cc->div_clks[idx];\n+}\n+\n+#define SPMI_PMIC_DIV_CLK_SIZE\t\t0x100\n+\n+static const struct of_device_id spmi_pmic_clkdiv_match_table[] = {\n+\t{\n+\t\t.compatible = \"qcom,spmi-clkdiv\",\n+\t\t.data =  (void *)(uintptr_t)1,\t\t/* Generic */\n+\t},\n+\t{\n+\t\t.compatible = \"qcom,pm8998-clkdiv\",\n+\t\t.data =  (void *)(uintptr_t)3,\t\t/* 3 div_clks */\n+\t},\n+\t{ /* sentinel */ }\n+};\n+MODULE_DEVICE_TABLE(of, spmi_pmic_clkdiv_match_table);\n+\n+static int spmi_pmic_clkdiv_probe(struct platform_device *pdev)\n+{\n+\tstruct spmi_pmic_div_clk_cc *clk_cc;\n+\tstruct clk_init_data init = {};\n+\tstruct clkdiv *clkdiv;\n+\tstruct clk *cxo;\n+\tstruct regmap *regmap;\n+\tstruct device *dev = &pdev->dev;\n+\tconst char *parent_name;\n+\tint nclks, i, rc, cxo_hz;\n+\tu32 start;\n+\n+\trc = of_property_read_u32(dev->of_node, \"reg\", &start);\n+\tif (rc < 0) {\n+\t\tdev_err(dev, \"reg property reading failed\\n\");\n+\t\treturn rc;\n+\t}\n+\n+\tregmap = dev_get_regmap(dev->parent, NULL);\n+\tif (!regmap) {\n+\t\tdev_err(dev, \"Couldn't get parent's regmap\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tnclks = (uintptr_t)of_match_node(spmi_pmic_clkdiv_match_table,\n+\t\t\t\t\t dev->of_node)->data;\n+\n+\tclkdiv = devm_kcalloc(dev, nclks, sizeof(*clkdiv), GFP_KERNEL);\n+\tif (!clkdiv)\n+\t\treturn -ENOMEM;\n+\n+\tclk_cc = devm_kzalloc(&pdev->dev, sizeof(*clk_cc), GFP_KERNEL);\n+\tif (!clk_cc)\n+\t\treturn -ENOMEM;\n+\n+\tclk_cc->div_clks = devm_kcalloc(&pdev->dev, nclks,\n+\t\t\t\t\tsizeof(*clk_cc->div_clks), GFP_KERNEL);\n+\tif (!clk_cc->div_clks)\n+\t\treturn -ENOMEM;\n+\n+\tcxo = clk_get(dev, \"xo\");\n+\tif (IS_ERR(cxo)) {\n+\t\trc = PTR_ERR(cxo);\n+\t\tif (rc != -EPROBE_DEFER)\n+\t\t\tdev_err(dev, \"failed to get xo clock\");\n+\t\treturn rc;\n+\t}\n+\tcxo_hz = clk_get_rate(cxo);\n+\tclk_put(cxo);\n+\n+\tparent_name = of_clk_get_parent_name(dev->of_node, 0);\n+\tif (!parent_name) {\n+\t\tdev_err(dev, \"missing parent clock\\n\");\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tinit.parent_names = &parent_name;\n+\tinit.num_parents = parent_name ? 1 : 0;\n+\tinit.ops = &clk_spmi_pmic_div_ops;\n+\tinit.flags = 0;\n+\n+\tfor (i = 0; i < nclks; i++) {\n+\t\tspin_lock_init(&clkdiv[i].lock);\n+\t\tclkdiv[i].base = start + i * SPMI_PMIC_DIV_CLK_SIZE;\n+\t\tclkdiv[i].regmap = regmap;\n+\t\tclkdiv[i].cxo_period_ns = NSEC_PER_SEC / cxo_hz;\n+\t\tinit.name = kasprintf(GFP_KERNEL, \"div_clk%d\", i + 1);\n+\t\tclkdiv[i].hw.init = &init;\n+\t\trc = devm_clk_hw_register(dev, &clkdiv[i].hw);\n+\t\tkfree(init.name); /* clock framework made a copy of the name */\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\n+\t\tclk_cc->div_clks[i] = &clkdiv[i].hw;\n+\t}\n+\n+\tclk_cc->nclks = nclks;\n+\trc = of_clk_add_hw_provider(pdev->dev.of_node, spmi_pmic_div_clk_hw_get,\n+\t\t\t\t    clk_cc);\n+\treturn rc;\n+}\n+\n+static int spmi_pmic_clkdiv_remove(struct platform_device *pdev)\n+{\n+\tof_clk_del_provider(pdev->dev.of_node);\n+\n+\treturn 0;\n+}\n+\n+static struct platform_driver spmi_pmic_clkdiv_driver = {\n+\t.driver\t\t= {\n+\t\t.name\t= \"qcom,spmi-pmic-clkdiv\",\n+\t\t.of_match_table = spmi_pmic_clkdiv_match_table,\n+\t},\n+\t.probe\t\t= spmi_pmic_clkdiv_probe,\n+\t.remove\t\t= spmi_pmic_clkdiv_remove,\n+};\n+module_platform_driver(spmi_pmic_clkdiv_driver);\n+\n+MODULE_DESCRIPTION(\"QCOM SPMI PMIC clkdiv driver\");\n+MODULE_LICENSE(\"GPL v2\");\n","prefixes":["V5"]}