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-s2mps11.c (7032B)


      1// SPDX-License-Identifier: GPL-2.0+
      2//
      3// clk-s2mps11.c - Clock driver for S2MPS11.
      4//
      5// Copyright (C) 2013,2014 Samsung Electornics
      6
      7#include <linux/module.h>
      8#include <linux/err.h>
      9#include <linux/of.h>
     10#include <linux/clkdev.h>
     11#include <linux/regmap.h>
     12#include <linux/clk-provider.h>
     13#include <linux/platform_device.h>
     14#include <linux/mfd/samsung/s2mps11.h>
     15#include <linux/mfd/samsung/s2mps13.h>
     16#include <linux/mfd/samsung/s2mps14.h>
     17#include <linux/mfd/samsung/s5m8767.h>
     18#include <linux/mfd/samsung/core.h>
     19
     20#include <dt-bindings/clock/samsung,s2mps11.h>
     21
     22struct s2mps11_clk {
     23	struct sec_pmic_dev *iodev;
     24	struct device_node *clk_np;
     25	struct clk_hw hw;
     26	struct clk *clk;
     27	struct clk_lookup *lookup;
     28	u32 mask;
     29	unsigned int reg;
     30};
     31
     32static struct s2mps11_clk *to_s2mps11_clk(struct clk_hw *hw)
     33{
     34	return container_of(hw, struct s2mps11_clk, hw);
     35}
     36
     37static int s2mps11_clk_prepare(struct clk_hw *hw)
     38{
     39	struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
     40
     41	return regmap_update_bits(s2mps11->iodev->regmap_pmic,
     42				 s2mps11->reg,
     43				 s2mps11->mask, s2mps11->mask);
     44}
     45
     46static void s2mps11_clk_unprepare(struct clk_hw *hw)
     47{
     48	struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
     49
     50	regmap_update_bits(s2mps11->iodev->regmap_pmic, s2mps11->reg,
     51			   s2mps11->mask, ~s2mps11->mask);
     52}
     53
     54static int s2mps11_clk_is_prepared(struct clk_hw *hw)
     55{
     56	int ret;
     57	u32 val;
     58	struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
     59
     60	ret = regmap_read(s2mps11->iodev->regmap_pmic,
     61				s2mps11->reg, &val);
     62	if (ret < 0)
     63		return -EINVAL;
     64
     65	return val & s2mps11->mask;
     66}
     67
     68static unsigned long s2mps11_clk_recalc_rate(struct clk_hw *hw,
     69					     unsigned long parent_rate)
     70{
     71	return 32768;
     72}
     73
     74static const struct clk_ops s2mps11_clk_ops = {
     75	.prepare	= s2mps11_clk_prepare,
     76	.unprepare	= s2mps11_clk_unprepare,
     77	.is_prepared	= s2mps11_clk_is_prepared,
     78	.recalc_rate	= s2mps11_clk_recalc_rate,
     79};
     80
     81/* This s2mps11_clks_init tructure is common to s2mps11, s2mps13 and s2mps14 */
     82static struct clk_init_data s2mps11_clks_init[S2MPS11_CLKS_NUM] = {
     83	[S2MPS11_CLK_AP] = {
     84		.name = "s2mps11_ap",
     85		.ops = &s2mps11_clk_ops,
     86	},
     87	[S2MPS11_CLK_CP] = {
     88		.name = "s2mps11_cp",
     89		.ops = &s2mps11_clk_ops,
     90	},
     91	[S2MPS11_CLK_BT] = {
     92		.name = "s2mps11_bt",
     93		.ops = &s2mps11_clk_ops,
     94	},
     95};
     96
     97static struct device_node *s2mps11_clk_parse_dt(struct platform_device *pdev,
     98		struct clk_init_data *clks_init)
     99{
    100	struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
    101	struct device_node *clk_np;
    102	int i;
    103
    104	if (!iodev->dev->of_node)
    105		return ERR_PTR(-EINVAL);
    106
    107	clk_np = of_get_child_by_name(iodev->dev->of_node, "clocks");
    108	if (!clk_np) {
    109		dev_err(&pdev->dev, "could not find clock sub-node\n");
    110		return ERR_PTR(-EINVAL);
    111	}
    112
    113	for (i = 0; i < S2MPS11_CLKS_NUM; i++)
    114		of_property_read_string_index(clk_np, "clock-output-names", i,
    115				&clks_init[i].name);
    116
    117	return clk_np;
    118}
    119
    120static int s2mps11_clk_probe(struct platform_device *pdev)
    121{
    122	struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
    123	struct s2mps11_clk *s2mps11_clks;
    124	struct clk_hw_onecell_data *clk_data;
    125	unsigned int s2mps11_reg;
    126	int i, ret = 0;
    127	enum sec_device_type hwid = platform_get_device_id(pdev)->driver_data;
    128
    129	s2mps11_clks = devm_kcalloc(&pdev->dev, S2MPS11_CLKS_NUM,
    130				sizeof(*s2mps11_clks), GFP_KERNEL);
    131	if (!s2mps11_clks)
    132		return -ENOMEM;
    133
    134	clk_data = devm_kzalloc(&pdev->dev,
    135				struct_size(clk_data, hws, S2MPS11_CLKS_NUM),
    136				GFP_KERNEL);
    137	if (!clk_data)
    138		return -ENOMEM;
    139
    140	switch (hwid) {
    141	case S2MPS11X:
    142		s2mps11_reg = S2MPS11_REG_RTC_CTRL;
    143		break;
    144	case S2MPS13X:
    145		s2mps11_reg = S2MPS13_REG_RTCCTRL;
    146		break;
    147	case S2MPS14X:
    148		s2mps11_reg = S2MPS14_REG_RTCCTRL;
    149		break;
    150	case S5M8767X:
    151		s2mps11_reg = S5M8767_REG_CTRL1;
    152		break;
    153	default:
    154		dev_err(&pdev->dev, "Invalid device type\n");
    155		return -EINVAL;
    156	}
    157
    158	/* Store clocks of_node in first element of s2mps11_clks array */
    159	s2mps11_clks->clk_np = s2mps11_clk_parse_dt(pdev, s2mps11_clks_init);
    160	if (IS_ERR(s2mps11_clks->clk_np))
    161		return PTR_ERR(s2mps11_clks->clk_np);
    162
    163	for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
    164		if (i == S2MPS11_CLK_CP && hwid == S2MPS14X)
    165			continue; /* Skip clocks not present in some devices */
    166		s2mps11_clks[i].iodev = iodev;
    167		s2mps11_clks[i].hw.init = &s2mps11_clks_init[i];
    168		s2mps11_clks[i].mask = 1 << i;
    169		s2mps11_clks[i].reg = s2mps11_reg;
    170
    171		s2mps11_clks[i].clk = devm_clk_register(&pdev->dev,
    172							&s2mps11_clks[i].hw);
    173		if (IS_ERR(s2mps11_clks[i].clk)) {
    174			dev_err(&pdev->dev, "Fail to register : %s\n",
    175						s2mps11_clks_init[i].name);
    176			ret = PTR_ERR(s2mps11_clks[i].clk);
    177			goto err_reg;
    178		}
    179
    180		s2mps11_clks[i].lookup = clkdev_hw_create(&s2mps11_clks[i].hw,
    181					s2mps11_clks_init[i].name, NULL);
    182		if (!s2mps11_clks[i].lookup) {
    183			ret = -ENOMEM;
    184			goto err_reg;
    185		}
    186		clk_data->hws[i] = &s2mps11_clks[i].hw;
    187	}
    188
    189	clk_data->num = S2MPS11_CLKS_NUM;
    190	of_clk_add_hw_provider(s2mps11_clks->clk_np, of_clk_hw_onecell_get,
    191			       clk_data);
    192
    193	platform_set_drvdata(pdev, s2mps11_clks);
    194
    195	return ret;
    196
    197err_reg:
    198	of_node_put(s2mps11_clks[0].clk_np);
    199	while (--i >= 0)
    200		clkdev_drop(s2mps11_clks[i].lookup);
    201
    202	return ret;
    203}
    204
    205static int s2mps11_clk_remove(struct platform_device *pdev)
    206{
    207	struct s2mps11_clk *s2mps11_clks = platform_get_drvdata(pdev);
    208	int i;
    209
    210	of_clk_del_provider(s2mps11_clks[0].clk_np);
    211	/* Drop the reference obtained in s2mps11_clk_parse_dt */
    212	of_node_put(s2mps11_clks[0].clk_np);
    213
    214	for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
    215		/* Skip clocks not present on S2MPS14 */
    216		if (!s2mps11_clks[i].lookup)
    217			continue;
    218		clkdev_drop(s2mps11_clks[i].lookup);
    219	}
    220
    221	return 0;
    222}
    223
    224static const struct platform_device_id s2mps11_clk_id[] = {
    225	{ "s2mps11-clk", S2MPS11X},
    226	{ "s2mps13-clk", S2MPS13X},
    227	{ "s2mps14-clk", S2MPS14X},
    228	{ "s5m8767-clk", S5M8767X},
    229	{ },
    230};
    231MODULE_DEVICE_TABLE(platform, s2mps11_clk_id);
    232
    233#ifdef CONFIG_OF
    234/*
    235 * Device is instantiated through parent MFD device and device matching is done
    236 * through platform_device_id.
    237 *
    238 * However if device's DT node contains proper clock compatible and driver is
    239 * built as a module, then the *module* matching will be done trough DT aliases.
    240 * This requires of_device_id table.  In the same time this will not change the
    241 * actual *device* matching so do not add .of_match_table.
    242 */
    243static const struct of_device_id s2mps11_dt_match[] __used = {
    244	{
    245		.compatible = "samsung,s2mps11-clk",
    246		.data = (void *)S2MPS11X,
    247	}, {
    248		.compatible = "samsung,s2mps13-clk",
    249		.data = (void *)S2MPS13X,
    250	}, {
    251		.compatible = "samsung,s2mps14-clk",
    252		.data = (void *)S2MPS14X,
    253	}, {
    254		.compatible = "samsung,s5m8767-clk",
    255		.data = (void *)S5M8767X,
    256	}, {
    257		/* Sentinel */
    258	},
    259};
    260MODULE_DEVICE_TABLE(of, s2mps11_dt_match);
    261#endif
    262
    263static struct platform_driver s2mps11_clk_driver = {
    264	.driver = {
    265		.name  = "s2mps11-clk",
    266	},
    267	.probe = s2mps11_clk_probe,
    268	.remove = s2mps11_clk_remove,
    269	.id_table = s2mps11_clk_id,
    270};
    271module_platform_driver(s2mps11_clk_driver);
    272
    273MODULE_DESCRIPTION("S2MPS11 Clock Driver");
    274MODULE_AUTHOR("Yadwinder Singh Brar <yadi.brar@samsung.com>");
    275MODULE_LICENSE("GPL");