brintos

brintos / linux-shallow public Read only

0
0
Text · 4.1 KiB · eab9d3c Raw
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