xref: /openbmc/linux/drivers/mfd/si476x-prop.c (revision 8e8e69d6)
18e8e69d6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
237955069SAndrey Smirnov /*
337955069SAndrey Smirnov  * drivers/mfd/si476x-prop.c -- Subroutines to access
437955069SAndrey Smirnov  * properties of si476x chips
537955069SAndrey Smirnov  *
637955069SAndrey Smirnov  * Copyright (C) 2012 Innovative Converged Devices(ICD)
737955069SAndrey Smirnov  * Copyright (C) 2013 Andrey Smirnov
837955069SAndrey Smirnov  *
937955069SAndrey Smirnov  * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
1037955069SAndrey Smirnov  */
1137955069SAndrey Smirnov #include <linux/module.h>
1237955069SAndrey Smirnov 
1337955069SAndrey Smirnov #include <linux/mfd/si476x-core.h>
1437955069SAndrey Smirnov 
1537955069SAndrey Smirnov struct si476x_property_range {
1637955069SAndrey Smirnov 	u16 low, high;
1737955069SAndrey Smirnov };
1837955069SAndrey Smirnov 
si476x_core_element_is_in_array(u16 element,const u16 array[],size_t size)1937955069SAndrey Smirnov static bool si476x_core_element_is_in_array(u16 element,
2037955069SAndrey Smirnov 					    const u16 array[],
2137955069SAndrey Smirnov 					    size_t size)
2237955069SAndrey Smirnov {
2337955069SAndrey Smirnov 	int i;
2437955069SAndrey Smirnov 
2537955069SAndrey Smirnov 	for (i = 0; i < size; i++)
2637955069SAndrey Smirnov 		if (element == array[i])
2737955069SAndrey Smirnov 			return true;
2837955069SAndrey Smirnov 
2937955069SAndrey Smirnov 	return false;
3037955069SAndrey Smirnov }
3137955069SAndrey Smirnov 
si476x_core_element_is_in_range(u16 element,const struct si476x_property_range range[],size_t size)3237955069SAndrey Smirnov static bool si476x_core_element_is_in_range(u16 element,
3337955069SAndrey Smirnov 					    const struct si476x_property_range range[],
3437955069SAndrey Smirnov 					    size_t size)
3537955069SAndrey Smirnov {
3637955069SAndrey Smirnov 	int i;
3737955069SAndrey Smirnov 
3837955069SAndrey Smirnov 	for (i = 0; i < size; i++)
3937955069SAndrey Smirnov 		if (element <= range[i].high && element >= range[i].low)
4037955069SAndrey Smirnov 			return true;
4137955069SAndrey Smirnov 
4237955069SAndrey Smirnov 	return false;
4337955069SAndrey Smirnov }
4437955069SAndrey Smirnov 
si476x_core_is_valid_property_a10(struct si476x_core * core,u16 property)4537955069SAndrey Smirnov static bool si476x_core_is_valid_property_a10(struct si476x_core *core,
4637955069SAndrey Smirnov 					      u16 property)
4737955069SAndrey Smirnov {
4837955069SAndrey Smirnov 	static const u16 valid_properties[] = {
4937955069SAndrey Smirnov 		0x0000,
5037955069SAndrey Smirnov 		0x0500, 0x0501,
5137955069SAndrey Smirnov 		0x0600,
5237955069SAndrey Smirnov 		0x0709, 0x070C, 0x070D, 0x70E, 0x710,
5337955069SAndrey Smirnov 		0x0718,
5437955069SAndrey Smirnov 		0x1207, 0x1208,
5537955069SAndrey Smirnov 		0x2007,
5637955069SAndrey Smirnov 		0x2300,
5737955069SAndrey Smirnov 	};
5837955069SAndrey Smirnov 
5937955069SAndrey Smirnov 	static const struct si476x_property_range valid_ranges[] = {
6037955069SAndrey Smirnov 		{ 0x0200, 0x0203 },
6137955069SAndrey Smirnov 		{ 0x0300, 0x0303 },
6237955069SAndrey Smirnov 		{ 0x0400, 0x0404 },
6337955069SAndrey Smirnov 		{ 0x0700, 0x0707 },
6437955069SAndrey Smirnov 		{ 0x1100, 0x1102 },
6537955069SAndrey Smirnov 		{ 0x1200, 0x1204 },
6637955069SAndrey Smirnov 		{ 0x1300, 0x1306 },
6737955069SAndrey Smirnov 		{ 0x2000, 0x2005 },
6837955069SAndrey Smirnov 		{ 0x2100, 0x2104 },
6937955069SAndrey Smirnov 		{ 0x2106, 0x2106 },
7037955069SAndrey Smirnov 		{ 0x2200, 0x220E },
7137955069SAndrey Smirnov 		{ 0x3100, 0x3104 },
7237955069SAndrey Smirnov 		{ 0x3207, 0x320F },
7337955069SAndrey Smirnov 		{ 0x3300, 0x3304 },
7437955069SAndrey Smirnov 		{ 0x3500, 0x3517 },
7537955069SAndrey Smirnov 		{ 0x3600, 0x3617 },
7637955069SAndrey Smirnov 		{ 0x3700, 0x3717 },
7737955069SAndrey Smirnov 		{ 0x4000, 0x4003 },
7837955069SAndrey Smirnov 	};
7937955069SAndrey Smirnov 
8037955069SAndrey Smirnov 	return	si476x_core_element_is_in_range(property, valid_ranges,
8137955069SAndrey Smirnov 						ARRAY_SIZE(valid_ranges)) ||
8237955069SAndrey Smirnov 		si476x_core_element_is_in_array(property, valid_properties,
8337955069SAndrey Smirnov 						ARRAY_SIZE(valid_properties));
8437955069SAndrey Smirnov }
8537955069SAndrey Smirnov 
si476x_core_is_valid_property_a20(struct si476x_core * core,u16 property)8637955069SAndrey Smirnov static bool si476x_core_is_valid_property_a20(struct si476x_core *core,
8737955069SAndrey Smirnov 					      u16 property)
8837955069SAndrey Smirnov {
8937955069SAndrey Smirnov 	static const u16 valid_properties[] = {
9037955069SAndrey Smirnov 		0x071B,
9137955069SAndrey Smirnov 		0x1006,
9237955069SAndrey Smirnov 		0x2210,
9337955069SAndrey Smirnov 		0x3401,
9437955069SAndrey Smirnov 	};
9537955069SAndrey Smirnov 
9637955069SAndrey Smirnov 	static const struct si476x_property_range valid_ranges[] = {
9737955069SAndrey Smirnov 		{ 0x2215, 0x2219 },
9837955069SAndrey Smirnov 	};
9937955069SAndrey Smirnov 
10037955069SAndrey Smirnov 	return	si476x_core_is_valid_property_a10(core, property) ||
10137955069SAndrey Smirnov 		si476x_core_element_is_in_range(property, valid_ranges,
10237955069SAndrey Smirnov 						ARRAY_SIZE(valid_ranges))  ||
10337955069SAndrey Smirnov 		si476x_core_element_is_in_array(property, valid_properties,
10437955069SAndrey Smirnov 						ARRAY_SIZE(valid_properties));
10537955069SAndrey Smirnov }
10637955069SAndrey Smirnov 
si476x_core_is_valid_property_a30(struct si476x_core * core,u16 property)10737955069SAndrey Smirnov static bool si476x_core_is_valid_property_a30(struct si476x_core *core,
10837955069SAndrey Smirnov 					      u16 property)
10937955069SAndrey Smirnov {
11037955069SAndrey Smirnov 	static const u16 valid_properties[] = {
11137955069SAndrey Smirnov 		0x071C, 0x071D,
11237955069SAndrey Smirnov 		0x1007, 0x1008,
11337955069SAndrey Smirnov 		0x220F, 0x2214,
11437955069SAndrey Smirnov 		0x2301,
11537955069SAndrey Smirnov 		0x3105, 0x3106,
11637955069SAndrey Smirnov 		0x3402,
11737955069SAndrey Smirnov 	};
11837955069SAndrey Smirnov 
11937955069SAndrey Smirnov 	static const struct si476x_property_range valid_ranges[] = {
12037955069SAndrey Smirnov 		{ 0x0405, 0x0411 },
12137955069SAndrey Smirnov 		{ 0x2008, 0x200B },
12237955069SAndrey Smirnov 		{ 0x2220, 0x2223 },
12337955069SAndrey Smirnov 		{ 0x3100, 0x3106 },
12437955069SAndrey Smirnov 	};
12537955069SAndrey Smirnov 
12637955069SAndrey Smirnov 	return	si476x_core_is_valid_property_a20(core, property) ||
12737955069SAndrey Smirnov 		si476x_core_element_is_in_range(property, valid_ranges,
12837955069SAndrey Smirnov 						ARRAY_SIZE(valid_ranges)) ||
12937955069SAndrey Smirnov 		si476x_core_element_is_in_array(property, valid_properties,
13037955069SAndrey Smirnov 						ARRAY_SIZE(valid_properties));
13137955069SAndrey Smirnov }
13237955069SAndrey Smirnov 
13337955069SAndrey Smirnov typedef bool (*valid_property_pred_t) (struct si476x_core *, u16);
13437955069SAndrey Smirnov 
si476x_core_is_valid_property(struct si476x_core * core,u16 property)13537955069SAndrey Smirnov static bool si476x_core_is_valid_property(struct si476x_core *core,
13637955069SAndrey Smirnov 					  u16 property)
13737955069SAndrey Smirnov {
13837955069SAndrey Smirnov 	static const valid_property_pred_t is_valid_property[] = {
13937955069SAndrey Smirnov 		[SI476X_REVISION_A10] = si476x_core_is_valid_property_a10,
14037955069SAndrey Smirnov 		[SI476X_REVISION_A20] = si476x_core_is_valid_property_a20,
14137955069SAndrey Smirnov 		[SI476X_REVISION_A30] = si476x_core_is_valid_property_a30,
14237955069SAndrey Smirnov 	};
14337955069SAndrey Smirnov 
14437955069SAndrey Smirnov 	BUG_ON(core->revision > SI476X_REVISION_A30 ||
14537955069SAndrey Smirnov 	       core->revision == -1);
14637955069SAndrey Smirnov 	return is_valid_property[core->revision](core, property);
14737955069SAndrey Smirnov }
14837955069SAndrey Smirnov 
14937955069SAndrey Smirnov 
si476x_core_is_readonly_property(struct si476x_core * core,u16 property)15037955069SAndrey Smirnov static bool si476x_core_is_readonly_property(struct si476x_core *core,
15137955069SAndrey Smirnov 					     u16 property)
15237955069SAndrey Smirnov {
15337955069SAndrey Smirnov 	BUG_ON(core->revision > SI476X_REVISION_A30 ||
15437955069SAndrey Smirnov 	       core->revision == -1);
15537955069SAndrey Smirnov 
15637955069SAndrey Smirnov 	switch (core->revision) {
15737955069SAndrey Smirnov 	case SI476X_REVISION_A10:
15837955069SAndrey Smirnov 		return (property == 0x3200);
15937955069SAndrey Smirnov 	case SI476X_REVISION_A20:
16037955069SAndrey Smirnov 		return (property == 0x1006 ||
16137955069SAndrey Smirnov 			property == 0x2210 ||
16237955069SAndrey Smirnov 			property == 0x3200);
16337955069SAndrey Smirnov 	case SI476X_REVISION_A30:
16437955069SAndrey Smirnov 		return false;
16537955069SAndrey Smirnov 	}
16637955069SAndrey Smirnov 
16737955069SAndrey Smirnov 	return false;
16837955069SAndrey Smirnov }
16937955069SAndrey Smirnov 
si476x_core_regmap_readable_register(struct device * dev,unsigned int reg)17037955069SAndrey Smirnov static bool si476x_core_regmap_readable_register(struct device *dev,
17137955069SAndrey Smirnov 						 unsigned int reg)
17237955069SAndrey Smirnov {
17337955069SAndrey Smirnov 	struct i2c_client *client = to_i2c_client(dev);
17437955069SAndrey Smirnov 	struct si476x_core *core = i2c_get_clientdata(client);
17537955069SAndrey Smirnov 
17637955069SAndrey Smirnov 	return si476x_core_is_valid_property(core, (u16) reg);
17737955069SAndrey Smirnov 
17837955069SAndrey Smirnov }
17937955069SAndrey Smirnov 
si476x_core_regmap_writable_register(struct device * dev,unsigned int reg)18037955069SAndrey Smirnov static bool si476x_core_regmap_writable_register(struct device *dev,
18137955069SAndrey Smirnov 						 unsigned int reg)
18237955069SAndrey Smirnov {
18337955069SAndrey Smirnov 	struct i2c_client *client = to_i2c_client(dev);
18437955069SAndrey Smirnov 	struct si476x_core *core = i2c_get_clientdata(client);
18537955069SAndrey Smirnov 
18637955069SAndrey Smirnov 	return si476x_core_is_valid_property(core, (u16) reg) &&
18737955069SAndrey Smirnov 		!si476x_core_is_readonly_property(core, (u16) reg);
18837955069SAndrey Smirnov }
18937955069SAndrey Smirnov 
19037955069SAndrey Smirnov 
si476x_core_regmap_write(void * context,unsigned int reg,unsigned int val)19137955069SAndrey Smirnov static int si476x_core_regmap_write(void *context, unsigned int reg,
19237955069SAndrey Smirnov 				    unsigned int val)
19337955069SAndrey Smirnov {
19437955069SAndrey Smirnov 	return si476x_core_cmd_set_property(context, reg, val);
19537955069SAndrey Smirnov }
19637955069SAndrey Smirnov 
si476x_core_regmap_read(void * context,unsigned int reg,unsigned * val)19737955069SAndrey Smirnov static int si476x_core_regmap_read(void *context, unsigned int reg,
19837955069SAndrey Smirnov 				   unsigned *val)
19937955069SAndrey Smirnov {
20037955069SAndrey Smirnov 	struct si476x_core *core = context;
20137955069SAndrey Smirnov 	int err;
20237955069SAndrey Smirnov 
20337955069SAndrey Smirnov 	err = si476x_core_cmd_get_property(core, reg);
20437955069SAndrey Smirnov 	if (err < 0)
20537955069SAndrey Smirnov 		return err;
20637955069SAndrey Smirnov 
20737955069SAndrey Smirnov 	*val = err;
20837955069SAndrey Smirnov 
20937955069SAndrey Smirnov 	return 0;
21037955069SAndrey Smirnov }
21137955069SAndrey Smirnov 
21237955069SAndrey Smirnov 
21337955069SAndrey Smirnov static const struct regmap_config si476x_regmap_config = {
21437955069SAndrey Smirnov 	.reg_bits = 16,
21537955069SAndrey Smirnov 	.val_bits = 16,
21637955069SAndrey Smirnov 
21737955069SAndrey Smirnov 	.max_register = 0x4003,
21837955069SAndrey Smirnov 
21937955069SAndrey Smirnov 	.writeable_reg = si476x_core_regmap_writable_register,
22037955069SAndrey Smirnov 	.readable_reg = si476x_core_regmap_readable_register,
22137955069SAndrey Smirnov 
22237955069SAndrey Smirnov 	.reg_read = si476x_core_regmap_read,
22337955069SAndrey Smirnov 	.reg_write = si476x_core_regmap_write,
22437955069SAndrey Smirnov 
22537955069SAndrey Smirnov 	.cache_type = REGCACHE_RBTREE,
22637955069SAndrey Smirnov };
22737955069SAndrey Smirnov 
devm_regmap_init_si476x(struct si476x_core * core)22837955069SAndrey Smirnov struct regmap *devm_regmap_init_si476x(struct si476x_core *core)
22937955069SAndrey Smirnov {
23037955069SAndrey Smirnov 	return devm_regmap_init(&core->client->dev, NULL,
23137955069SAndrey Smirnov 				core, &si476x_regmap_config);
23237955069SAndrey Smirnov }
23337955069SAndrey Smirnov EXPORT_SYMBOL_GPL(devm_regmap_init_si476x);
234