Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Clock driver for twl device.
  4 *
  5 * inspired by the driver for the Palmas device
  6 */
  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              2
 15#define TWL6030_CFG_STATE_OFF   0x00
 16#define TWL6030_CFG_STATE_ON    0x01
 17#define TWL6030_CFG_STATE_MASK  0x03
 18
 19struct twl_clock_info {
 20	struct device *dev;
 21	u8 base;
 22	struct clk_hw hw;
 23};
 24
 25static inline int
 26twlclk_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 int
 38twlclk_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");