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