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-si514.c (9741B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Driver for Silicon Labs Si514 Programmable Oscillator
      4 *
      5 * Copyright (C) 2015 Topic Embedded Products
      6 *
      7 * Author: Mike Looijmans <mike.looijmans@topic.nl>
      8 */
      9
     10#include <linux/clk-provider.h>
     11#include <linux/delay.h>
     12#include <linux/module.h>
     13#include <linux/i2c.h>
     14#include <linux/regmap.h>
     15#include <linux/slab.h>
     16
     17/* I2C registers */
     18#define SI514_REG_LP		0
     19#define SI514_REG_M_FRAC1	5
     20#define SI514_REG_M_FRAC2	6
     21#define SI514_REG_M_FRAC3	7
     22#define SI514_REG_M_INT_FRAC	8
     23#define SI514_REG_M_INT		9
     24#define SI514_REG_HS_DIV	10
     25#define SI514_REG_LS_HS_DIV	11
     26#define SI514_REG_OE_STATE	14
     27#define SI514_REG_RESET		128
     28#define SI514_REG_CONTROL	132
     29
     30/* Register values */
     31#define SI514_RESET_RST		BIT(7)
     32
     33#define SI514_CONTROL_FCAL	BIT(0)
     34#define SI514_CONTROL_OE	BIT(2)
     35
     36#define SI514_MIN_FREQ	    100000U
     37#define SI514_MAX_FREQ	 250000000U
     38
     39#define FXO		  31980000U
     40
     41#define FVCO_MIN	2080000000U
     42#define FVCO_MAX	2500000000U
     43
     44#define HS_DIV_MAX	1022
     45
     46struct clk_si514 {
     47	struct clk_hw hw;
     48	struct regmap *regmap;
     49	struct i2c_client *i2c_client;
     50};
     51#define to_clk_si514(_hw)	container_of(_hw, struct clk_si514, hw)
     52
     53/* Multiplier/divider settings */
     54struct clk_si514_muldiv {
     55	u32 m_frac;  /* 29-bit Fractional part of multiplier M */
     56	u8 m_int; /* Integer part of multiplier M, 65..78 */
     57	u8 ls_div_bits; /* 2nd divider, as 2^x */
     58	u16 hs_div; /* 1st divider, must be even and 10<=x<=1022 */
     59};
     60
     61/* Enables or disables the output driver */
     62static int si514_enable_output(struct clk_si514 *data, bool enable)
     63{
     64	return regmap_update_bits(data->regmap, SI514_REG_CONTROL,
     65		SI514_CONTROL_OE, enable ? SI514_CONTROL_OE : 0);
     66}
     67
     68static int si514_prepare(struct clk_hw *hw)
     69{
     70	struct clk_si514 *data = to_clk_si514(hw);
     71
     72	return si514_enable_output(data, true);
     73}
     74
     75static void si514_unprepare(struct clk_hw *hw)
     76{
     77	struct clk_si514 *data = to_clk_si514(hw);
     78
     79	si514_enable_output(data, false);
     80}
     81
     82static int si514_is_prepared(struct clk_hw *hw)
     83{
     84	struct clk_si514 *data = to_clk_si514(hw);
     85	unsigned int val;
     86	int err;
     87
     88	err = regmap_read(data->regmap, SI514_REG_CONTROL, &val);
     89	if (err < 0)
     90		return err;
     91
     92	return !!(val & SI514_CONTROL_OE);
     93}
     94
     95/* Retrieve clock multiplier and dividers from hardware */
     96static int si514_get_muldiv(struct clk_si514 *data,
     97	struct clk_si514_muldiv *settings)
     98{
     99	int err;
    100	u8 reg[7];
    101
    102	err = regmap_bulk_read(data->regmap, SI514_REG_M_FRAC1,
    103			reg, ARRAY_SIZE(reg));
    104	if (err)
    105		return err;
    106
    107	settings->m_frac = reg[0] | reg[1] << 8 | reg[2] << 16 |
    108			   (reg[3] & 0x1F) << 24;
    109	settings->m_int = (reg[4] & 0x3f) << 3 | reg[3] >> 5;
    110	settings->ls_div_bits = (reg[6] >> 4) & 0x07;
    111	settings->hs_div = (reg[6] & 0x03) << 8 | reg[5];
    112	return 0;
    113}
    114
    115static int si514_set_muldiv(struct clk_si514 *data,
    116	struct clk_si514_muldiv *settings)
    117{
    118	u8 lp;
    119	u8 reg[7];
    120	int err;
    121
    122	/* Calculate LP1/LP2 according to table 13 in the datasheet */
    123	/* 65.259980246 */
    124	if (settings->m_int < 65 ||
    125		(settings->m_int == 65 && settings->m_frac <= 139575831))
    126		lp = 0x22;
    127	/* 67.859763463 */
    128	else if (settings->m_int < 67 ||
    129		(settings->m_int == 67 && settings->m_frac <= 461581994))
    130		lp = 0x23;
    131	/* 72.937624981 */
    132	else if (settings->m_int < 72 ||
    133		(settings->m_int == 72 && settings->m_frac <= 503383578))
    134		lp = 0x33;
    135	/* 75.843265046 */
    136	else if (settings->m_int < 75 ||
    137		(settings->m_int == 75 && settings->m_frac <= 452724474))
    138		lp = 0x34;
    139	else
    140		lp = 0x44;
    141
    142	err = regmap_write(data->regmap, SI514_REG_LP, lp);
    143	if (err < 0)
    144		return err;
    145
    146	reg[0] = settings->m_frac;
    147	reg[1] = settings->m_frac >> 8;
    148	reg[2] = settings->m_frac >> 16;
    149	reg[3] = settings->m_frac >> 24 | settings->m_int << 5;
    150	reg[4] = settings->m_int >> 3;
    151	reg[5] = settings->hs_div;
    152	reg[6] = (settings->hs_div >> 8) | (settings->ls_div_bits << 4);
    153
    154	err = regmap_bulk_write(data->regmap, SI514_REG_HS_DIV, reg + 5, 2);
    155	if (err < 0)
    156		return err;
    157	/*
    158	 * Writing to SI514_REG_M_INT_FRAC triggers the clock change, so that
    159	 * must be written last
    160	 */
    161	return regmap_bulk_write(data->regmap, SI514_REG_M_FRAC1, reg, 5);
    162}
    163
    164/* Calculate divider settings for a given frequency */
    165static int si514_calc_muldiv(struct clk_si514_muldiv *settings,
    166	unsigned long frequency)
    167{
    168	u64 m;
    169	u32 ls_freq;
    170	u32 tmp;
    171	u8 res;
    172
    173	if ((frequency < SI514_MIN_FREQ) || (frequency > SI514_MAX_FREQ))
    174		return -EINVAL;
    175
    176	/* Determine the minimum value of LS_DIV and resulting target freq. */
    177	ls_freq = frequency;
    178	if (frequency >= (FVCO_MIN / HS_DIV_MAX))
    179		settings->ls_div_bits = 0;
    180	else {
    181		res = 1;
    182		tmp = 2 * HS_DIV_MAX;
    183		while (tmp <= (HS_DIV_MAX * 32)) {
    184			if ((frequency * tmp) >= FVCO_MIN)
    185				break;
    186			++res;
    187			tmp <<= 1;
    188		}
    189		settings->ls_div_bits = res;
    190		ls_freq = frequency << res;
    191	}
    192
    193	/* Determine minimum HS_DIV, round up to even number */
    194	settings->hs_div = DIV_ROUND_UP(FVCO_MIN >> 1, ls_freq) << 1;
    195
    196	/* M = LS_DIV x HS_DIV x frequency / F_XO (in fixed-point) */
    197	m = ((u64)(ls_freq * settings->hs_div) << 29) + (FXO / 2);
    198	do_div(m, FXO);
    199	settings->m_frac = (u32)m & (BIT(29) - 1);
    200	settings->m_int = (u32)(m >> 29);
    201
    202	return 0;
    203}
    204
    205/* Calculate resulting frequency given the register settings */
    206static unsigned long si514_calc_rate(struct clk_si514_muldiv *settings)
    207{
    208	u64 m = settings->m_frac | ((u64)settings->m_int << 29);
    209	u32 d = settings->hs_div * BIT(settings->ls_div_bits);
    210
    211	return ((u32)(((m * FXO) + (FXO / 2)) >> 29)) / d;
    212}
    213
    214static unsigned long si514_recalc_rate(struct clk_hw *hw,
    215		unsigned long parent_rate)
    216{
    217	struct clk_si514 *data = to_clk_si514(hw);
    218	struct clk_si514_muldiv settings;
    219	int err;
    220
    221	err = si514_get_muldiv(data, &settings);
    222	if (err) {
    223		dev_err(&data->i2c_client->dev, "unable to retrieve settings\n");
    224		return 0;
    225	}
    226
    227	return si514_calc_rate(&settings);
    228}
    229
    230static long si514_round_rate(struct clk_hw *hw, unsigned long rate,
    231		unsigned long *parent_rate)
    232{
    233	struct clk_si514_muldiv settings;
    234	int err;
    235
    236	if (!rate)
    237		return 0;
    238
    239	err = si514_calc_muldiv(&settings, rate);
    240	if (err)
    241		return err;
    242
    243	return si514_calc_rate(&settings);
    244}
    245
    246/*
    247 * Update output frequency for big frequency changes (> 1000 ppm).
    248 * The chip supports <1000ppm changes "on the fly", we haven't implemented
    249 * that here.
    250 */
    251static int si514_set_rate(struct clk_hw *hw, unsigned long rate,
    252		unsigned long parent_rate)
    253{
    254	struct clk_si514 *data = to_clk_si514(hw);
    255	struct clk_si514_muldiv settings;
    256	unsigned int old_oe_state;
    257	int err;
    258
    259	err = si514_calc_muldiv(&settings, rate);
    260	if (err)
    261		return err;
    262
    263	err = regmap_read(data->regmap, SI514_REG_CONTROL, &old_oe_state);
    264	if (err)
    265		return err;
    266
    267	si514_enable_output(data, false);
    268
    269	err = si514_set_muldiv(data, &settings);
    270	if (err < 0)
    271		return err; /* Undefined state now, best to leave disabled */
    272
    273	/* Trigger calibration */
    274	err = regmap_write(data->regmap, SI514_REG_CONTROL, SI514_CONTROL_FCAL);
    275	if (err < 0)
    276		return err;
    277
    278	/* Applying a new frequency can take up to 10ms */
    279	usleep_range(10000, 12000);
    280
    281	if (old_oe_state & SI514_CONTROL_OE)
    282		si514_enable_output(data, true);
    283
    284	return err;
    285}
    286
    287static const struct clk_ops si514_clk_ops = {
    288	.prepare = si514_prepare,
    289	.unprepare = si514_unprepare,
    290	.is_prepared = si514_is_prepared,
    291	.recalc_rate = si514_recalc_rate,
    292	.round_rate = si514_round_rate,
    293	.set_rate = si514_set_rate,
    294};
    295
    296static bool si514_regmap_is_volatile(struct device *dev, unsigned int reg)
    297{
    298	switch (reg) {
    299	case SI514_REG_CONTROL:
    300	case SI514_REG_RESET:
    301		return true;
    302	default:
    303		return false;
    304	}
    305}
    306
    307static bool si514_regmap_is_writeable(struct device *dev, unsigned int reg)
    308{
    309	switch (reg) {
    310	case SI514_REG_LP:
    311	case SI514_REG_M_FRAC1 ... SI514_REG_LS_HS_DIV:
    312	case SI514_REG_OE_STATE:
    313	case SI514_REG_RESET:
    314	case SI514_REG_CONTROL:
    315		return true;
    316	default:
    317		return false;
    318	}
    319}
    320
    321static const struct regmap_config si514_regmap_config = {
    322	.reg_bits = 8,
    323	.val_bits = 8,
    324	.cache_type = REGCACHE_RBTREE,
    325	.max_register = SI514_REG_CONTROL,
    326	.writeable_reg = si514_regmap_is_writeable,
    327	.volatile_reg = si514_regmap_is_volatile,
    328};
    329
    330static int si514_probe(struct i2c_client *client)
    331{
    332	struct clk_si514 *data;
    333	struct clk_init_data init;
    334	int err;
    335
    336	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
    337	if (!data)
    338		return -ENOMEM;
    339
    340	init.ops = &si514_clk_ops;
    341	init.flags = 0;
    342	init.num_parents = 0;
    343	data->hw.init = &init;
    344	data->i2c_client = client;
    345
    346	if (of_property_read_string(client->dev.of_node, "clock-output-names",
    347			&init.name))
    348		init.name = client->dev.of_node->name;
    349
    350	data->regmap = devm_regmap_init_i2c(client, &si514_regmap_config);
    351	if (IS_ERR(data->regmap)) {
    352		dev_err(&client->dev, "failed to allocate register map\n");
    353		return PTR_ERR(data->regmap);
    354	}
    355
    356	i2c_set_clientdata(client, data);
    357
    358	err = devm_clk_hw_register(&client->dev, &data->hw);
    359	if (err) {
    360		dev_err(&client->dev, "clock registration failed\n");
    361		return err;
    362	}
    363	err = of_clk_add_hw_provider(client->dev.of_node, of_clk_hw_simple_get,
    364				     &data->hw);
    365	if (err) {
    366		dev_err(&client->dev, "unable to add clk provider\n");
    367		return err;
    368	}
    369
    370	return 0;
    371}
    372
    373static int si514_remove(struct i2c_client *client)
    374{
    375	of_clk_del_provider(client->dev.of_node);
    376	return 0;
    377}
    378
    379static const struct i2c_device_id si514_id[] = {
    380	{ "si514", 0 },
    381	{ }
    382};
    383MODULE_DEVICE_TABLE(i2c, si514_id);
    384
    385static const struct of_device_id clk_si514_of_match[] = {
    386	{ .compatible = "silabs,si514" },
    387	{ },
    388};
    389MODULE_DEVICE_TABLE(of, clk_si514_of_match);
    390
    391static struct i2c_driver si514_driver = {
    392	.driver = {
    393		.name = "si514",
    394		.of_match_table = clk_si514_of_match,
    395	},
    396	.probe_new	= si514_probe,
    397	.remove		= si514_remove,
    398	.id_table	= si514_id,
    399};
    400module_i2c_driver(si514_driver);
    401
    402MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
    403MODULE_DESCRIPTION("Si514 driver");
    404MODULE_LICENSE("GPL");