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