234 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * drivers/mfd/si476x-prop.c -- Subroutines to access4 * properties of si476x chips5 *6 * Copyright (C) 2012 Innovative Converged Devices(ICD)7 * Copyright (C) 2013 Andrey Smirnov8 *9 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>10 */11#include <linux/module.h>12 13#include <linux/mfd/si476x-core.h>14 15struct si476x_property_range {16 u16 low, high;17};18 19static bool si476x_core_element_is_in_array(u16 element,20 const u16 array[],21 size_t size)22{23 int i;24 25 for (i = 0; i < size; i++)26 if (element == array[i])27 return true;28 29 return false;30}31 32static bool si476x_core_element_is_in_range(u16 element,33 const struct si476x_property_range range[],34 size_t size)35{36 int i;37 38 for (i = 0; i < size; i++)39 if (element <= range[i].high && element >= range[i].low)40 return true;41 42 return false;43}44 45static bool si476x_core_is_valid_property_a10(struct si476x_core *core,46 u16 property)47{48 static const u16 valid_properties[] = {49 0x0000,50 0x0500, 0x0501,51 0x0600,52 0x0709, 0x070C, 0x070D, 0x70E, 0x710,53 0x0718,54 0x1207, 0x1208,55 0x2007,56 0x2300,57 };58 59 static const struct si476x_property_range valid_ranges[] = {60 { 0x0200, 0x0203 },61 { 0x0300, 0x0303 },62 { 0x0400, 0x0404 },63 { 0x0700, 0x0707 },64 { 0x1100, 0x1102 },65 { 0x1200, 0x1204 },66 { 0x1300, 0x1306 },67 { 0x2000, 0x2005 },68 { 0x2100, 0x2104 },69 { 0x2106, 0x2106 },70 { 0x2200, 0x220E },71 { 0x3100, 0x3104 },72 { 0x3207, 0x320F },73 { 0x3300, 0x3304 },74 { 0x3500, 0x3517 },75 { 0x3600, 0x3617 },76 { 0x3700, 0x3717 },77 { 0x4000, 0x4003 },78 };79 80 return si476x_core_element_is_in_range(property, valid_ranges,81 ARRAY_SIZE(valid_ranges)) ||82 si476x_core_element_is_in_array(property, valid_properties,83 ARRAY_SIZE(valid_properties));84}85 86static bool si476x_core_is_valid_property_a20(struct si476x_core *core,87 u16 property)88{89 static const u16 valid_properties[] = {90 0x071B,91 0x1006,92 0x2210,93 0x3401,94 };95 96 static const struct si476x_property_range valid_ranges[] = {97 { 0x2215, 0x2219 },98 };99 100 return si476x_core_is_valid_property_a10(core, property) ||101 si476x_core_element_is_in_range(property, valid_ranges,102 ARRAY_SIZE(valid_ranges)) ||103 si476x_core_element_is_in_array(property, valid_properties,104 ARRAY_SIZE(valid_properties));105}106 107static bool si476x_core_is_valid_property_a30(struct si476x_core *core,108 u16 property)109{110 static const u16 valid_properties[] = {111 0x071C, 0x071D,112 0x1007, 0x1008,113 0x220F, 0x2214,114 0x2301,115 0x3105, 0x3106,116 0x3402,117 };118 119 static const struct si476x_property_range valid_ranges[] = {120 { 0x0405, 0x0411 },121 { 0x2008, 0x200B },122 { 0x2220, 0x2223 },123 { 0x3100, 0x3106 },124 };125 126 return si476x_core_is_valid_property_a20(core, property) ||127 si476x_core_element_is_in_range(property, valid_ranges,128 ARRAY_SIZE(valid_ranges)) ||129 si476x_core_element_is_in_array(property, valid_properties,130 ARRAY_SIZE(valid_properties));131}132 133typedef bool (*valid_property_pred_t) (struct si476x_core *, u16);134 135static bool si476x_core_is_valid_property(struct si476x_core *core,136 u16 property)137{138 static const valid_property_pred_t is_valid_property[] = {139 [SI476X_REVISION_A10] = si476x_core_is_valid_property_a10,140 [SI476X_REVISION_A20] = si476x_core_is_valid_property_a20,141 [SI476X_REVISION_A30] = si476x_core_is_valid_property_a30,142 };143 144 BUG_ON(core->revision > SI476X_REVISION_A30 ||145 core->revision == -1);146 return is_valid_property[core->revision](core, property);147}148 149 150static bool si476x_core_is_readonly_property(struct si476x_core *core,151 u16 property)152{153 BUG_ON(core->revision > SI476X_REVISION_A30 ||154 core->revision == -1);155 156 switch (core->revision) {157 case SI476X_REVISION_A10:158 return (property == 0x3200);159 case SI476X_REVISION_A20:160 return (property == 0x1006 ||161 property == 0x2210 ||162 property == 0x3200);163 case SI476X_REVISION_A30:164 return false;165 }166 167 return false;168}169 170static bool si476x_core_regmap_readable_register(struct device *dev,171 unsigned int reg)172{173 struct i2c_client *client = to_i2c_client(dev);174 struct si476x_core *core = i2c_get_clientdata(client);175 176 return si476x_core_is_valid_property(core, (u16) reg);177 178}179 180static bool si476x_core_regmap_writable_register(struct device *dev,181 unsigned int reg)182{183 struct i2c_client *client = to_i2c_client(dev);184 struct si476x_core *core = i2c_get_clientdata(client);185 186 return si476x_core_is_valid_property(core, (u16) reg) &&187 !si476x_core_is_readonly_property(core, (u16) reg);188}189 190 191static int si476x_core_regmap_write(void *context, unsigned int reg,192 unsigned int val)193{194 return si476x_core_cmd_set_property(context, reg, val);195}196 197static int si476x_core_regmap_read(void *context, unsigned int reg,198 unsigned *val)199{200 struct si476x_core *core = context;201 int err;202 203 err = si476x_core_cmd_get_property(core, reg);204 if (err < 0)205 return err;206 207 *val = err;208 209 return 0;210}211 212 213static const struct regmap_config si476x_regmap_config = {214 .reg_bits = 16,215 .val_bits = 16,216 217 .max_register = 0x4003,218 219 .writeable_reg = si476x_core_regmap_writable_register,220 .readable_reg = si476x_core_regmap_readable_register,221 222 .reg_read = si476x_core_regmap_read,223 .reg_write = si476x_core_regmap_write,224 225 .cache_type = REGCACHE_MAPLE,226};227 228struct regmap *devm_regmap_init_si476x(struct si476x_core *core)229{230 return devm_regmap_init(&core->client->dev, NULL,231 core, &si476x_regmap_config);232}233EXPORT_SYMBOL_GPL(devm_regmap_init_si476x);234