1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (C) 2018 ROHM Semiconductors 3 4 #include <linux/kernel.h> 5 #include <linux/module.h> 6 #include <linux/init.h> 7 #include <linux/err.h> 8 #include <linux/platform_device.h> 9 #include <linux/slab.h> 10 #include <linux/mfd/rohm-bd718x7.h> 11 #include <linux/clk-provider.h> 12 #include <linux/clkdev.h> 13 #include <linux/regmap.h> 14 15 struct bd718xx_clk { 16 struct clk_hw hw; 17 u8 reg; 18 u8 mask; 19 struct platform_device *pdev; 20 struct bd718xx *mfd; 21 }; 22 23 static int bd71837_clk_set(struct clk_hw *hw, int status) 24 { 25 struct bd718xx_clk *c = container_of(hw, struct bd718xx_clk, hw); 26 27 return regmap_update_bits(c->mfd->regmap, c->reg, c->mask, status); 28 } 29 30 static void bd71837_clk_disable(struct clk_hw *hw) 31 { 32 int rv; 33 struct bd718xx_clk *c = container_of(hw, struct bd718xx_clk, hw); 34 35 rv = bd71837_clk_set(hw, 0); 36 if (rv) 37 dev_dbg(&c->pdev->dev, "Failed to disable 32K clk (%d)\n", rv); 38 } 39 40 static int bd71837_clk_enable(struct clk_hw *hw) 41 { 42 return bd71837_clk_set(hw, 1); 43 } 44 45 static int bd71837_clk_is_enabled(struct clk_hw *hw) 46 { 47 int enabled; 48 int rval; 49 struct bd718xx_clk *c = container_of(hw, struct bd718xx_clk, hw); 50 51 rval = regmap_read(c->mfd->regmap, c->reg, &enabled); 52 53 if (rval) 54 return rval; 55 56 return enabled & c->mask; 57 } 58 59 static const struct clk_ops bd71837_clk_ops = { 60 .prepare = &bd71837_clk_enable, 61 .unprepare = &bd71837_clk_disable, 62 .is_prepared = &bd71837_clk_is_enabled, 63 }; 64 65 static int bd71837_clk_probe(struct platform_device *pdev) 66 { 67 struct bd718xx_clk *c; 68 int rval = -ENOMEM; 69 const char *parent_clk; 70 struct device *parent = pdev->dev.parent; 71 struct bd718xx *mfd = dev_get_drvdata(parent); 72 struct clk_init_data init = { 73 .name = "bd718xx-32k-out", 74 .ops = &bd71837_clk_ops, 75 }; 76 77 c = devm_kzalloc(&pdev->dev, sizeof(*c), GFP_KERNEL); 78 if (!c) 79 return -ENOMEM; 80 81 init.num_parents = 1; 82 parent_clk = of_clk_get_parent_name(parent->of_node, 0); 83 84 init.parent_names = &parent_clk; 85 if (!parent_clk) { 86 dev_err(&pdev->dev, "No parent clk found\n"); 87 return -EINVAL; 88 } 89 90 c->reg = BD718XX_REG_OUT32K; 91 c->mask = BD718XX_OUT32K_EN; 92 c->mfd = mfd; 93 c->pdev = pdev; 94 c->hw.init = &init; 95 96 of_property_read_string_index(parent->of_node, 97 "clock-output-names", 0, &init.name); 98 99 rval = devm_clk_hw_register(&pdev->dev, &c->hw); 100 if (rval) { 101 dev_err(&pdev->dev, "failed to register 32K clk"); 102 return rval; 103 } 104 rval = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get, 105 &c->hw); 106 if (rval) 107 dev_err(&pdev->dev, "adding clk provider failed\n"); 108 109 return rval; 110 } 111 112 static struct platform_driver bd71837_clk = { 113 .driver = { 114 .name = "bd718xx-clk", 115 }, 116 .probe = bd71837_clk_probe, 117 }; 118 119 module_platform_driver(bd71837_clk); 120 121 MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); 122 MODULE_DESCRIPTION("BD71837 chip clk driver"); 123 MODULE_LICENSE("GPL"); 124