brintos

brintos / linux-shallow public Read only

0
0
Text · 7.1 KiB · 39049f6 Raw
293 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Clock driver for Palmas device.4 *5 * Copyright (c) 2013, NVIDIA Corporation.6 * Copyright (c) 2013-2014 Texas Instruments, Inc.7 *8 * Author:	Laxman Dewangan <ldewangan@nvidia.com>9 *		Peter Ujfalusi <peter.ujfalusi@ti.com>10 */11 12#include <linux/clk.h>13#include <linux/clk-provider.h>14#include <linux/mfd/palmas.h>15#include <linux/module.h>16#include <linux/of.h>17#include <linux/platform_device.h>18#include <linux/slab.h>19 20#define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1	121#define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2	222#define PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP	323 24struct palmas_clk32k_desc {25	const char *clk_name;26	unsigned int control_reg;27	unsigned int enable_mask;28	unsigned int sleep_mask;29	unsigned int sleep_reqstr_id;30	int delay;31};32 33struct palmas_clock_info {34	struct device *dev;35	struct clk_hw hw;36	struct palmas *palmas;37	const struct palmas_clk32k_desc *clk_desc;38	int ext_control_pin;39};40 41static inline struct palmas_clock_info *to_palmas_clks_info(struct clk_hw *hw)42{43	return container_of(hw, struct palmas_clock_info, hw);44}45 46static unsigned long palmas_clks_recalc_rate(struct clk_hw *hw,47					     unsigned long parent_rate)48{49	return 32768;50}51 52static int palmas_clks_prepare(struct clk_hw *hw)53{54	struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);55	int ret;56 57	ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,58				 cinfo->clk_desc->control_reg,59				 cinfo->clk_desc->enable_mask,60				 cinfo->clk_desc->enable_mask);61	if (ret < 0)62		dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",63			cinfo->clk_desc->control_reg, ret);64	else if (cinfo->clk_desc->delay)65		udelay(cinfo->clk_desc->delay);66 67	return ret;68}69 70static void palmas_clks_unprepare(struct clk_hw *hw)71{72	struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);73	int ret;74 75	/*76	 * Clock can be disabled through external pin if it is externally77	 * controlled.78	 */79	if (cinfo->ext_control_pin)80		return;81 82	ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,83				 cinfo->clk_desc->control_reg,84				 cinfo->clk_desc->enable_mask, 0);85	if (ret < 0)86		dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",87			cinfo->clk_desc->control_reg, ret);88}89 90static int palmas_clks_is_prepared(struct clk_hw *hw)91{92	struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);93	int ret;94	u32 val;95 96	if (cinfo->ext_control_pin)97		return 1;98 99	ret = palmas_read(cinfo->palmas, PALMAS_RESOURCE_BASE,100			  cinfo->clk_desc->control_reg, &val);101	if (ret < 0) {102		dev_err(cinfo->dev, "Reg 0x%02x read failed, %d\n",103			cinfo->clk_desc->control_reg, ret);104		return ret;105	}106	return !!(val & cinfo->clk_desc->enable_mask);107}108 109static const struct clk_ops palmas_clks_ops = {110	.prepare	= palmas_clks_prepare,111	.unprepare	= palmas_clks_unprepare,112	.is_prepared	= palmas_clks_is_prepared,113	.recalc_rate	= palmas_clks_recalc_rate,114};115 116struct palmas_clks_of_match_data {117	struct clk_init_data init;118	const struct palmas_clk32k_desc desc;119};120 121static const struct palmas_clks_of_match_data palmas_of_clk32kg = {122	.init = {123		.name = "clk32kg",124		.ops = &palmas_clks_ops,125		.flags = CLK_IGNORE_UNUSED,126	},127	.desc = {128		.clk_name = "clk32kg",129		.control_reg = PALMAS_CLK32KG_CTRL,130		.enable_mask = PALMAS_CLK32KG_CTRL_MODE_ACTIVE,131		.sleep_mask = PALMAS_CLK32KG_CTRL_MODE_SLEEP,132		.sleep_reqstr_id = PALMAS_EXTERNAL_REQSTR_ID_CLK32KG,133		.delay = 200,134	},135};136 137static const struct palmas_clks_of_match_data palmas_of_clk32kgaudio = {138	.init = {139		.name = "clk32kgaudio",140		.ops = &palmas_clks_ops,141		.flags = CLK_IGNORE_UNUSED,142	},143	.desc = {144		.clk_name = "clk32kgaudio",145		.control_reg = PALMAS_CLK32KGAUDIO_CTRL,146		.enable_mask = PALMAS_CLK32KG_CTRL_MODE_ACTIVE,147		.sleep_mask = PALMAS_CLK32KG_CTRL_MODE_SLEEP,148		.sleep_reqstr_id = PALMAS_EXTERNAL_REQSTR_ID_CLK32KGAUDIO,149		.delay = 200,150	},151};152 153static const struct of_device_id palmas_clks_of_match[] = {154	{155		.compatible = "ti,palmas-clk32kg",156		.data = &palmas_of_clk32kg,157	},158	{159		.compatible = "ti,palmas-clk32kgaudio",160		.data = &palmas_of_clk32kgaudio,161	},162	{ },163};164MODULE_DEVICE_TABLE(of, palmas_clks_of_match);165 166static void palmas_clks_get_clk_data(struct platform_device *pdev,167				     struct palmas_clock_info *cinfo)168{169	struct device_node *node = pdev->dev.of_node;170	unsigned int prop;171	int ret;172 173	ret = of_property_read_u32(node, "ti,external-sleep-control",174				   &prop);175	if (ret)176		return;177 178	switch (prop) {179	case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1:180		prop = PALMAS_EXT_CONTROL_ENABLE1;181		break;182	case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2:183		prop = PALMAS_EXT_CONTROL_ENABLE2;184		break;185	case PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP:186		prop = PALMAS_EXT_CONTROL_NSLEEP;187		break;188	default:189		dev_warn(&pdev->dev, "%pOFn: Invalid ext control option: %u\n",190			 node, prop);191		prop = 0;192		break;193	}194	cinfo->ext_control_pin = prop;195}196 197static int palmas_clks_init_configure(struct palmas_clock_info *cinfo)198{199	int ret;200 201	ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,202				 cinfo->clk_desc->control_reg,203				 cinfo->clk_desc->sleep_mask, 0);204	if (ret < 0) {205		dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",206			cinfo->clk_desc->control_reg, ret);207		return ret;208	}209 210	if (cinfo->ext_control_pin) {211		ret = clk_prepare(cinfo->hw.clk);212		if (ret < 0) {213			dev_err(cinfo->dev, "Clock prep failed, %d\n", ret);214			return ret;215		}216 217		ret = palmas_ext_control_req_config(cinfo->palmas,218					cinfo->clk_desc->sleep_reqstr_id,219					cinfo->ext_control_pin, true);220		if (ret < 0) {221			dev_err(cinfo->dev, "Ext config for %s failed, %d\n",222				cinfo->clk_desc->clk_name, ret);223			clk_unprepare(cinfo->hw.clk);224			return ret;225		}226	}227 228	return ret;229}230static int palmas_clks_probe(struct platform_device *pdev)231{232	struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);233	struct device_node *node = pdev->dev.of_node;234	const struct palmas_clks_of_match_data *match_data;235	struct palmas_clock_info *cinfo;236	int ret;237 238	match_data = of_device_get_match_data(&pdev->dev);239	if (!match_data)240		return 1;241 242	cinfo = devm_kzalloc(&pdev->dev, sizeof(*cinfo), GFP_KERNEL);243	if (!cinfo)244		return -ENOMEM;245 246	palmas_clks_get_clk_data(pdev, cinfo);247	platform_set_drvdata(pdev, cinfo);248 249	cinfo->dev = &pdev->dev;250	cinfo->palmas = palmas;251 252	cinfo->clk_desc = &match_data->desc;253	cinfo->hw.init = &match_data->init;254	ret = devm_clk_hw_register(&pdev->dev, &cinfo->hw);255	if (ret) {256		dev_err(&pdev->dev, "Fail to register clock %s, %d\n",257			match_data->desc.clk_name, ret);258		return ret;259	}260 261	ret = palmas_clks_init_configure(cinfo);262	if (ret < 0) {263		dev_err(&pdev->dev, "Clock config failed, %d\n", ret);264		return ret;265	}266 267	ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &cinfo->hw);268	if (ret < 0)269		dev_err(&pdev->dev, "Fail to add clock driver, %d\n", ret);270	return ret;271}272 273static void palmas_clks_remove(struct platform_device *pdev)274{275	of_clk_del_provider(pdev->dev.of_node);276}277 278static struct platform_driver palmas_clks_driver = {279	.driver = {280		.name = "palmas-clk",281		.of_match_table = palmas_clks_of_match,282	},283	.probe = palmas_clks_probe,284	.remove = palmas_clks_remove,285};286 287module_platform_driver(palmas_clks_driver);288 289MODULE_DESCRIPTION("Clock driver for Palmas Series Devices");290MODULE_ALIAS("platform:palmas-clk");291MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");292MODULE_LICENSE("GPL v2");293