si476x-prop.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. * drivers/mfd/si476x-prop.c -- Subroutines to access
  3. * properties of si476x chips
  4. *
  5. * Copyright (C) 2012 Innovative Converged Devices(ICD)
  6. * Copyright (C) 2013 Andrey Smirnov
  7. *
  8. * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; version 2 of the License.
  13. *
  14. * This program is distributed in the hope that it will be useful, but
  15. * WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * General Public License for more details.
  18. */
  19. #include <linux/module.h>
  20. #include <linux/mfd/si476x-core.h>
  21. struct si476x_property_range {
  22. u16 low, high;
  23. };
  24. static bool si476x_core_element_is_in_array(u16 element,
  25. const u16 array[],
  26. size_t size)
  27. {
  28. int i;
  29. for (i = 0; i < size; i++)
  30. if (element == array[i])
  31. return true;
  32. return false;
  33. }
  34. static bool si476x_core_element_is_in_range(u16 element,
  35. const struct si476x_property_range range[],
  36. size_t size)
  37. {
  38. int i;
  39. for (i = 0; i < size; i++)
  40. if (element <= range[i].high && element >= range[i].low)
  41. return true;
  42. return false;
  43. }
  44. static bool si476x_core_is_valid_property_a10(struct si476x_core *core,
  45. u16 property)
  46. {
  47. static const u16 valid_properties[] = {
  48. 0x0000,
  49. 0x0500, 0x0501,
  50. 0x0600,
  51. 0x0709, 0x070C, 0x070D, 0x70E, 0x710,
  52. 0x0718,
  53. 0x1207, 0x1208,
  54. 0x2007,
  55. 0x2300,
  56. };
  57. static const struct si476x_property_range valid_ranges[] = {
  58. { 0x0200, 0x0203 },
  59. { 0x0300, 0x0303 },
  60. { 0x0400, 0x0404 },
  61. { 0x0700, 0x0707 },
  62. { 0x1100, 0x1102 },
  63. { 0x1200, 0x1204 },
  64. { 0x1300, 0x1306 },
  65. { 0x2000, 0x2005 },
  66. { 0x2100, 0x2104 },
  67. { 0x2106, 0x2106 },
  68. { 0x2200, 0x220E },
  69. { 0x3100, 0x3104 },
  70. { 0x3207, 0x320F },
  71. { 0x3300, 0x3304 },
  72. { 0x3500, 0x3517 },
  73. { 0x3600, 0x3617 },
  74. { 0x3700, 0x3717 },
  75. { 0x4000, 0x4003 },
  76. };
  77. return si476x_core_element_is_in_range(property, valid_ranges,
  78. ARRAY_SIZE(valid_ranges)) ||
  79. si476x_core_element_is_in_array(property, valid_properties,
  80. ARRAY_SIZE(valid_properties));
  81. }
  82. static bool si476x_core_is_valid_property_a20(struct si476x_core *core,
  83. u16 property)
  84. {
  85. static const u16 valid_properties[] = {
  86. 0x071B,
  87. 0x1006,
  88. 0x2210,
  89. 0x3401,
  90. };
  91. static const struct si476x_property_range valid_ranges[] = {
  92. { 0x2215, 0x2219 },
  93. };
  94. return si476x_core_is_valid_property_a10(core, property) ||
  95. si476x_core_element_is_in_range(property, valid_ranges,
  96. ARRAY_SIZE(valid_ranges)) ||
  97. si476x_core_element_is_in_array(property, valid_properties,
  98. ARRAY_SIZE(valid_properties));
  99. }
  100. static bool si476x_core_is_valid_property_a30(struct si476x_core *core,
  101. u16 property)
  102. {
  103. static const u16 valid_properties[] = {
  104. 0x071C, 0x071D,
  105. 0x1007, 0x1008,
  106. 0x220F, 0x2214,
  107. 0x2301,
  108. 0x3105, 0x3106,
  109. 0x3402,
  110. };
  111. static const struct si476x_property_range valid_ranges[] = {
  112. { 0x0405, 0x0411 },
  113. { 0x2008, 0x200B },
  114. { 0x2220, 0x2223 },
  115. { 0x3100, 0x3106 },
  116. };
  117. return si476x_core_is_valid_property_a20(core, property) ||
  118. si476x_core_element_is_in_range(property, valid_ranges,
  119. ARRAY_SIZE(valid_ranges)) ||
  120. si476x_core_element_is_in_array(property, valid_properties,
  121. ARRAY_SIZE(valid_properties));
  122. }
  123. typedef bool (*valid_property_pred_t) (struct si476x_core *, u16);
  124. static bool si476x_core_is_valid_property(struct si476x_core *core,
  125. u16 property)
  126. {
  127. static const valid_property_pred_t is_valid_property[] = {
  128. [SI476X_REVISION_A10] = si476x_core_is_valid_property_a10,
  129. [SI476X_REVISION_A20] = si476x_core_is_valid_property_a20,
  130. [SI476X_REVISION_A30] = si476x_core_is_valid_property_a30,
  131. };
  132. BUG_ON(core->revision > SI476X_REVISION_A30 ||
  133. core->revision == -1);
  134. return is_valid_property[core->revision](core, property);
  135. }
  136. static bool si476x_core_is_readonly_property(struct si476x_core *core,
  137. u16 property)
  138. {
  139. BUG_ON(core->revision > SI476X_REVISION_A30 ||
  140. core->revision == -1);
  141. switch (core->revision) {
  142. case SI476X_REVISION_A10:
  143. return (property == 0x3200);
  144. case SI476X_REVISION_A20:
  145. return (property == 0x1006 ||
  146. property == 0x2210 ||
  147. property == 0x3200);
  148. case SI476X_REVISION_A30:
  149. return false;
  150. }
  151. return false;
  152. }
  153. static bool si476x_core_regmap_readable_register(struct device *dev,
  154. unsigned int reg)
  155. {
  156. struct i2c_client *client = to_i2c_client(dev);
  157. struct si476x_core *core = i2c_get_clientdata(client);
  158. return si476x_core_is_valid_property(core, (u16) reg);
  159. }
  160. static bool si476x_core_regmap_writable_register(struct device *dev,
  161. unsigned int reg)
  162. {
  163. struct i2c_client *client = to_i2c_client(dev);
  164. struct si476x_core *core = i2c_get_clientdata(client);
  165. return si476x_core_is_valid_property(core, (u16) reg) &&
  166. !si476x_core_is_readonly_property(core, (u16) reg);
  167. }
  168. static int si476x_core_regmap_write(void *context, unsigned int reg,
  169. unsigned int val)
  170. {
  171. return si476x_core_cmd_set_property(context, reg, val);
  172. }
  173. static int si476x_core_regmap_read(void *context, unsigned int reg,
  174. unsigned *val)
  175. {
  176. struct si476x_core *core = context;
  177. int err;
  178. err = si476x_core_cmd_get_property(core, reg);
  179. if (err < 0)
  180. return err;
  181. *val = err;
  182. return 0;
  183. }
  184. static const struct regmap_config si476x_regmap_config = {
  185. .reg_bits = 16,
  186. .val_bits = 16,
  187. .max_register = 0x4003,
  188. .writeable_reg = si476x_core_regmap_writable_register,
  189. .readable_reg = si476x_core_regmap_readable_register,
  190. .reg_read = si476x_core_regmap_read,
  191. .reg_write = si476x_core_regmap_write,
  192. .cache_type = REGCACHE_RBTREE,
  193. };
  194. struct regmap *devm_regmap_init_si476x(struct si476x_core *core)
  195. {
  196. return devm_regmap_init(&core->client->dev, NULL,
  197. core, &si476x_regmap_config);
  198. }
  199. EXPORT_SYMBOL_GPL(devm_regmap_init_si476x);