198 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Clock driver for twl device.4 *5 * inspired by the driver for the Palmas device6 */7 8#include <linux/clk-provider.h>9#include <linux/mfd/twl.h>10#include <linux/module.h>11#include <linux/platform_device.h>12#include <linux/slab.h>13 14#define VREG_STATE 215#define TWL6030_CFG_STATE_OFF 0x0016#define TWL6030_CFG_STATE_ON 0x0117#define TWL6030_CFG_STATE_MASK 0x0318 19struct twl_clock_info {20 struct device *dev;21 u8 base;22 struct clk_hw hw;23};24 25static inline int26twlclk_read(struct twl_clock_info *info, unsigned int slave_subgp,27 unsigned int offset)28{29 u8 value;30 int status;31 32 status = twl_i2c_read_u8(slave_subgp, &value,33 info->base + offset);34 return (status < 0) ? status : value;35}36 37static inline int38twlclk_write(struct twl_clock_info *info, unsigned int slave_subgp,39 unsigned int offset, u8 value)40{41 return twl_i2c_write_u8(slave_subgp, value,42 info->base + offset);43}44 45static inline struct twl_clock_info *to_twl_clks_info(struct clk_hw *hw)46{47 return container_of(hw, struct twl_clock_info, hw);48}49 50static unsigned long twl_clks_recalc_rate(struct clk_hw *hw,51 unsigned long parent_rate)52{53 return 32768;54}55 56static int twl6032_clks_prepare(struct clk_hw *hw)57{58 struct twl_clock_info *cinfo = to_twl_clks_info(hw);59 int ret;60 61 ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,62 TWL6030_CFG_STATE_ON);63 if (ret < 0)64 dev_err(cinfo->dev, "clk prepare failed\n");65 66 return ret;67}68 69static void twl6032_clks_unprepare(struct clk_hw *hw)70{71 struct twl_clock_info *cinfo = to_twl_clks_info(hw);72 int ret;73 74 ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE,75 TWL6030_CFG_STATE_OFF);76 if (ret < 0)77 dev_err(cinfo->dev, "clk unprepare failed\n");78}79 80static int twl6032_clks_is_prepared(struct clk_hw *hw)81{82 struct twl_clock_info *cinfo = to_twl_clks_info(hw);83 int val;84 85 val = twlclk_read(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE);86 if (val < 0) {87 dev_err(cinfo->dev, "clk read failed\n");88 return val;89 }90 91 val &= TWL6030_CFG_STATE_MASK;92 93 return val == TWL6030_CFG_STATE_ON;94}95 96static const struct clk_ops twl6032_clks_ops = {97 .prepare = twl6032_clks_prepare,98 .unprepare = twl6032_clks_unprepare,99 .is_prepared = twl6032_clks_is_prepared,100 .recalc_rate = twl_clks_recalc_rate,101};102 103struct twl_clks_data {104 struct clk_init_data init;105 u8 base;106};107 108static const struct twl_clks_data twl6032_clks[] = {109 {110 .init = {111 .name = "clk32kg",112 .ops = &twl6032_clks_ops,113 .flags = CLK_IGNORE_UNUSED,114 },115 .base = 0x8C,116 },117 {118 .init = {119 .name = "clk32kaudio",120 .ops = &twl6032_clks_ops,121 .flags = CLK_IGNORE_UNUSED,122 },123 .base = 0x8F,124 },125 {126 /* sentinel */127 }128};129 130static int twl_clks_probe(struct platform_device *pdev)131{132 struct clk_hw_onecell_data *clk_data;133 const struct twl_clks_data *hw_data;134 135 struct twl_clock_info *cinfo;136 int ret;137 int i;138 int count;139 140 hw_data = twl6032_clks;141 for (count = 0; hw_data[count].init.name; count++)142 ;143 144 clk_data = devm_kzalloc(&pdev->dev,145 struct_size(clk_data, hws, count),146 GFP_KERNEL);147 if (!clk_data)148 return -ENOMEM;149 150 clk_data->num = count;151 cinfo = devm_kcalloc(&pdev->dev, count, sizeof(*cinfo), GFP_KERNEL);152 if (!cinfo)153 return -ENOMEM;154 155 for (i = 0; i < count; i++) {156 cinfo[i].base = hw_data[i].base;157 cinfo[i].dev = &pdev->dev;158 cinfo[i].hw.init = &hw_data[i].init;159 ret = devm_clk_hw_register(&pdev->dev, &cinfo[i].hw);160 if (ret) {161 return dev_err_probe(&pdev->dev, ret,162 "Fail to register clock %s\n",163 hw_data[i].init.name);164 }165 clk_data->hws[i] = &cinfo[i].hw;166 }167 168 ret = devm_of_clk_add_hw_provider(&pdev->dev,169 of_clk_hw_onecell_get, clk_data);170 if (ret < 0)171 return dev_err_probe(&pdev->dev, ret,172 "Fail to add clock driver\n");173 174 return 0;175}176 177static const struct platform_device_id twl_clks_id[] = {178 {179 .name = "twl6032-clk",180 }, {181 /* sentinel */182 }183};184MODULE_DEVICE_TABLE(platform, twl_clks_id);185 186static struct platform_driver twl_clks_driver = {187 .driver = {188 .name = "twl-clk",189 },190 .probe = twl_clks_probe,191 .id_table = twl_clks_id,192};193 194module_platform_driver(twl_clks_driver);195 196MODULE_DESCRIPTION("Clock driver for TWL Series Devices");197MODULE_LICENSE("GPL");198