--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Clock driver for twl device.
+ *
+ * inspired by the driver for the Palmas device
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mfd/twl.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define VREG_STATE              2
+#define TWL6030_CFG_STATE_OFF   0x00
+#define TWL6030_CFG_STATE_ON    0x01
+#define TWL6030_CFG_STATE_MASK  0x03
+
+struct twl_clock_info {
+       struct device *dev;
+       u8 base;
+       struct clk_hw hw;
+};
+
+static inline int
+twlclk_read(struct twl_clock_info *info, unsigned int slave_subgp,
+           unsigned int offset)
+{
+       u8 value;
+       int status;
+
+       status = twl_i2c_read_u8(slave_subgp, &value,
+                                info->base + offset);
+       return (status < 0) ? status : value;
+}
+
+static inline int
+twlclk_write(struct twl_clock_info *info, unsigned int slave_subgp,
+            unsigned int offset, u8 value)
+{
+       return twl_i2c_write_u8(slave_subgp, value,
+                               info->base + offset);
+}
+
+static inline struct twl_clock_info *to_twl_clks_info(struct clk_hw *hw)
+{
+       return container_of(hw, struct twl_clock_info, hw);
+}
+
+static unsigned long twl_clks_recalc_rate(struct clk_hw *hw,
+                                         unsigned long parent_rate)
+{
+       return 32768;
+}
+
+static int twl6032_clks_prepare(struct clk_hw *hw)
+{
+       struct twl_clock_info *cinfo = to_twl_clks_info(hw);
+       int ret;
+
+       ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
+                          TWL6030_CFG_STATE_ON);
+       if (ret < 0)
+               dev_err(cinfo->dev, "clk prepare failed\n");
+
+       return ret;
+}
+
+static void twl6032_clks_unprepare(struct clk_hw *hw)
+{
+       struct twl_clock_info *cinfo = to_twl_clks_info(hw);
+       int ret;
+
+       ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,
+                          TWL6030_CFG_STATE_OFF);
+       if (ret < 0)
+               dev_err(cinfo->dev, "clk unprepare failed\n");
+}
+
+static int twl6032_clks_is_prepared(struct clk_hw *hw)
+{
+       struct twl_clock_info *cinfo = to_twl_clks_info(hw);
+       int val;
+
+       val = twlclk_read(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE);
+       if (val < 0) {
+               dev_err(cinfo->dev, "clk read failed\n");
+               return val;
+       }
+
+       val &= TWL6030_CFG_STATE_MASK;
+
+       return val == TWL6030_CFG_STATE_ON;
+}
+
+static const struct clk_ops twl6032_clks_ops = {
+       .prepare        = twl6032_clks_prepare,
+       .unprepare      = twl6032_clks_unprepare,
+       .is_prepared    = twl6032_clks_is_prepared,
+       .recalc_rate    = twl_clks_recalc_rate,
+};
+
+struct twl_clks_data {
+       struct clk_init_data init;
+       u8 base;
+};
+
+static const struct twl_clks_data twl6032_clks[] = {
+       {
+               .init = {
+                       .name = "clk32kg",
+                       .ops = &twl6032_clks_ops,
+                       .flags = CLK_IGNORE_UNUSED,
+               },
+               .base = 0x8C,
+       },
+       {
+               .init = {
+                       .name = "clk32kaudio",
+                       .ops = &twl6032_clks_ops,
+                       .flags = CLK_IGNORE_UNUSED,
+               },
+               .base = 0x8F,
+       },
+       {
+               /* sentinel */
+       }
+};
+
+static int twl_clks_probe(struct platform_device *pdev)
+{
+       struct clk_hw_onecell_data *clk_data;
+       const struct twl_clks_data *hw_data;
+
+       struct twl_clock_info *cinfo;
+       int ret;
+       int i;
+       int count;
+
+       hw_data = twl6032_clks;
+       for (count = 0; hw_data[count].init.name; count++)
+               ;
+
+       clk_data = devm_kzalloc(&pdev->dev,
+                               struct_size(clk_data, hws, count),
+                               GFP_KERNEL);
+       if (!clk_data)
+               return -ENOMEM;
+
+       clk_data->num = count;
+       cinfo = devm_kcalloc(&pdev->dev, count, sizeof(*cinfo), GFP_KERNEL);
+       if (!cinfo)
+               return -ENOMEM;
+
+       for (i = 0; i < count; i++) {
+               cinfo[i].base = hw_data[i].base;
+               cinfo[i].dev = &pdev->dev;
+               cinfo[i].hw.init = &hw_data[i].init;
+               ret = devm_clk_hw_register(&pdev->dev, &cinfo[i].hw);
+               if (ret) {
+                       return dev_err_probe(&pdev->dev, ret,
+                                            "Fail to register clock %s\n",
+                                            hw_data[i].init.name);
+               }
+               clk_data->hws[i] = &cinfo[i].hw;
+       }
+
+       ret = devm_of_clk_add_hw_provider(&pdev->dev,
+                                         of_clk_hw_onecell_get, clk_data);
+       if (ret < 0)
+               return dev_err_probe(&pdev->dev, ret,
+                                    "Fail to add clock driver\n");
+
+       return 0;
+}
+
+static const struct platform_device_id twl_clks_id[] = {
+       {
+               .name = "twl6032-clk",
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(platform, twl_clks_id);
+
+static struct platform_driver twl_clks_driver = {
+       .driver = {
+               .name = "twl-clk",
+       },
+       .probe = twl_clks_probe,
+       .id_table = twl_clks_id,
+};
+
+module_platform_driver(twl_clks_driver);
+
+MODULE_DESCRIPTION("Clock driver for TWL Series Devices");
+MODULE_LICENSE("GPL");