cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

clk-palmas.c (7320B)


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