162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drivers/mfd/si476x-prop.c -- Subroutines to access
462306a36Sopenharmony_ci * properties of si476x chips
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2012 Innovative Converged Devices(ICD)
762306a36Sopenharmony_ci * Copyright (C) 2013 Andrey Smirnov
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/mfd/si476x-core.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistruct si476x_property_range {
1662306a36Sopenharmony_ci	u16 low, high;
1762306a36Sopenharmony_ci};
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic bool si476x_core_element_is_in_array(u16 element,
2062306a36Sopenharmony_ci					    const u16 array[],
2162306a36Sopenharmony_ci					    size_t size)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	int i;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	for (i = 0; i < size; i++)
2662306a36Sopenharmony_ci		if (element == array[i])
2762306a36Sopenharmony_ci			return true;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	return false;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic bool si476x_core_element_is_in_range(u16 element,
3362306a36Sopenharmony_ci					    const struct si476x_property_range range[],
3462306a36Sopenharmony_ci					    size_t size)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	int i;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	for (i = 0; i < size; i++)
3962306a36Sopenharmony_ci		if (element <= range[i].high && element >= range[i].low)
4062306a36Sopenharmony_ci			return true;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return false;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic bool si476x_core_is_valid_property_a10(struct si476x_core *core,
4662306a36Sopenharmony_ci					      u16 property)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	static const u16 valid_properties[] = {
4962306a36Sopenharmony_ci		0x0000,
5062306a36Sopenharmony_ci		0x0500, 0x0501,
5162306a36Sopenharmony_ci		0x0600,
5262306a36Sopenharmony_ci		0x0709, 0x070C, 0x070D, 0x70E, 0x710,
5362306a36Sopenharmony_ci		0x0718,
5462306a36Sopenharmony_ci		0x1207, 0x1208,
5562306a36Sopenharmony_ci		0x2007,
5662306a36Sopenharmony_ci		0x2300,
5762306a36Sopenharmony_ci	};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	static const struct si476x_property_range valid_ranges[] = {
6062306a36Sopenharmony_ci		{ 0x0200, 0x0203 },
6162306a36Sopenharmony_ci		{ 0x0300, 0x0303 },
6262306a36Sopenharmony_ci		{ 0x0400, 0x0404 },
6362306a36Sopenharmony_ci		{ 0x0700, 0x0707 },
6462306a36Sopenharmony_ci		{ 0x1100, 0x1102 },
6562306a36Sopenharmony_ci		{ 0x1200, 0x1204 },
6662306a36Sopenharmony_ci		{ 0x1300, 0x1306 },
6762306a36Sopenharmony_ci		{ 0x2000, 0x2005 },
6862306a36Sopenharmony_ci		{ 0x2100, 0x2104 },
6962306a36Sopenharmony_ci		{ 0x2106, 0x2106 },
7062306a36Sopenharmony_ci		{ 0x2200, 0x220E },
7162306a36Sopenharmony_ci		{ 0x3100, 0x3104 },
7262306a36Sopenharmony_ci		{ 0x3207, 0x320F },
7362306a36Sopenharmony_ci		{ 0x3300, 0x3304 },
7462306a36Sopenharmony_ci		{ 0x3500, 0x3517 },
7562306a36Sopenharmony_ci		{ 0x3600, 0x3617 },
7662306a36Sopenharmony_ci		{ 0x3700, 0x3717 },
7762306a36Sopenharmony_ci		{ 0x4000, 0x4003 },
7862306a36Sopenharmony_ci	};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return	si476x_core_element_is_in_range(property, valid_ranges,
8162306a36Sopenharmony_ci						ARRAY_SIZE(valid_ranges)) ||
8262306a36Sopenharmony_ci		si476x_core_element_is_in_array(property, valid_properties,
8362306a36Sopenharmony_ci						ARRAY_SIZE(valid_properties));
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic bool si476x_core_is_valid_property_a20(struct si476x_core *core,
8762306a36Sopenharmony_ci					      u16 property)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	static const u16 valid_properties[] = {
9062306a36Sopenharmony_ci		0x071B,
9162306a36Sopenharmony_ci		0x1006,
9262306a36Sopenharmony_ci		0x2210,
9362306a36Sopenharmony_ci		0x3401,
9462306a36Sopenharmony_ci	};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	static const struct si476x_property_range valid_ranges[] = {
9762306a36Sopenharmony_ci		{ 0x2215, 0x2219 },
9862306a36Sopenharmony_ci	};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return	si476x_core_is_valid_property_a10(core, property) ||
10162306a36Sopenharmony_ci		si476x_core_element_is_in_range(property, valid_ranges,
10262306a36Sopenharmony_ci						ARRAY_SIZE(valid_ranges))  ||
10362306a36Sopenharmony_ci		si476x_core_element_is_in_array(property, valid_properties,
10462306a36Sopenharmony_ci						ARRAY_SIZE(valid_properties));
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic bool si476x_core_is_valid_property_a30(struct si476x_core *core,
10862306a36Sopenharmony_ci					      u16 property)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	static const u16 valid_properties[] = {
11162306a36Sopenharmony_ci		0x071C, 0x071D,
11262306a36Sopenharmony_ci		0x1007, 0x1008,
11362306a36Sopenharmony_ci		0x220F, 0x2214,
11462306a36Sopenharmony_ci		0x2301,
11562306a36Sopenharmony_ci		0x3105, 0x3106,
11662306a36Sopenharmony_ci		0x3402,
11762306a36Sopenharmony_ci	};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	static const struct si476x_property_range valid_ranges[] = {
12062306a36Sopenharmony_ci		{ 0x0405, 0x0411 },
12162306a36Sopenharmony_ci		{ 0x2008, 0x200B },
12262306a36Sopenharmony_ci		{ 0x2220, 0x2223 },
12362306a36Sopenharmony_ci		{ 0x3100, 0x3106 },
12462306a36Sopenharmony_ci	};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return	si476x_core_is_valid_property_a20(core, property) ||
12762306a36Sopenharmony_ci		si476x_core_element_is_in_range(property, valid_ranges,
12862306a36Sopenharmony_ci						ARRAY_SIZE(valid_ranges)) ||
12962306a36Sopenharmony_ci		si476x_core_element_is_in_array(property, valid_properties,
13062306a36Sopenharmony_ci						ARRAY_SIZE(valid_properties));
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_citypedef bool (*valid_property_pred_t) (struct si476x_core *, u16);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic bool si476x_core_is_valid_property(struct si476x_core *core,
13662306a36Sopenharmony_ci					  u16 property)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	static const valid_property_pred_t is_valid_property[] = {
13962306a36Sopenharmony_ci		[SI476X_REVISION_A10] = si476x_core_is_valid_property_a10,
14062306a36Sopenharmony_ci		[SI476X_REVISION_A20] = si476x_core_is_valid_property_a20,
14162306a36Sopenharmony_ci		[SI476X_REVISION_A30] = si476x_core_is_valid_property_a30,
14262306a36Sopenharmony_ci	};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	BUG_ON(core->revision > SI476X_REVISION_A30 ||
14562306a36Sopenharmony_ci	       core->revision == -1);
14662306a36Sopenharmony_ci	return is_valid_property[core->revision](core, property);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic bool si476x_core_is_readonly_property(struct si476x_core *core,
15162306a36Sopenharmony_ci					     u16 property)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	BUG_ON(core->revision > SI476X_REVISION_A30 ||
15462306a36Sopenharmony_ci	       core->revision == -1);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	switch (core->revision) {
15762306a36Sopenharmony_ci	case SI476X_REVISION_A10:
15862306a36Sopenharmony_ci		return (property == 0x3200);
15962306a36Sopenharmony_ci	case SI476X_REVISION_A20:
16062306a36Sopenharmony_ci		return (property == 0x1006 ||
16162306a36Sopenharmony_ci			property == 0x2210 ||
16262306a36Sopenharmony_ci			property == 0x3200);
16362306a36Sopenharmony_ci	case SI476X_REVISION_A30:
16462306a36Sopenharmony_ci		return false;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	return false;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic bool si476x_core_regmap_readable_register(struct device *dev,
17162306a36Sopenharmony_ci						 unsigned int reg)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
17462306a36Sopenharmony_ci	struct si476x_core *core = i2c_get_clientdata(client);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	return si476x_core_is_valid_property(core, (u16) reg);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic bool si476x_core_regmap_writable_register(struct device *dev,
18162306a36Sopenharmony_ci						 unsigned int reg)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
18462306a36Sopenharmony_ci	struct si476x_core *core = i2c_get_clientdata(client);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return si476x_core_is_valid_property(core, (u16) reg) &&
18762306a36Sopenharmony_ci		!si476x_core_is_readonly_property(core, (u16) reg);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic int si476x_core_regmap_write(void *context, unsigned int reg,
19262306a36Sopenharmony_ci				    unsigned int val)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	return si476x_core_cmd_set_property(context, reg, val);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int si476x_core_regmap_read(void *context, unsigned int reg,
19862306a36Sopenharmony_ci				   unsigned *val)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct si476x_core *core = context;
20162306a36Sopenharmony_ci	int err;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	err = si476x_core_cmd_get_property(core, reg);
20462306a36Sopenharmony_ci	if (err < 0)
20562306a36Sopenharmony_ci		return err;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	*val = err;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic const struct regmap_config si476x_regmap_config = {
21462306a36Sopenharmony_ci	.reg_bits = 16,
21562306a36Sopenharmony_ci	.val_bits = 16,
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	.max_register = 0x4003,
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	.writeable_reg = si476x_core_regmap_writable_register,
22062306a36Sopenharmony_ci	.readable_reg = si476x_core_regmap_readable_register,
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	.reg_read = si476x_core_regmap_read,
22362306a36Sopenharmony_ci	.reg_write = si476x_core_regmap_write,
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
22662306a36Sopenharmony_ci};
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistruct regmap *devm_regmap_init_si476x(struct si476x_core *core)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	return devm_regmap_init(&core->client->dev, NULL,
23162306a36Sopenharmony_ci				core, &si476x_regmap_config);
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_regmap_init_si476x);
234