extcon-sm5502.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. /*
  2. * extcon-sm5502.c - Silicon Mitus SM5502 extcon drvier to support USB switches
  3. *
  4. * Copyright (c) 2014 Samsung Electronics Co., Ltd
  5. * Author: Chanwoo Choi <cw00.choi@samsung.com>
  6. *
  7. * This program is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU General Public License as published by the
  9. * Free Software Foundation; either version 2 of the License, or (at your
  10. * option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. */
  17. #include <linux/err.h>
  18. #include <linux/i2c.h>
  19. #include <linux/input.h>
  20. #include <linux/interrupt.h>
  21. #include <linux/irqdomain.h>
  22. #include <linux/kernel.h>
  23. #include <linux/module.h>
  24. #include <linux/platform_device.h>
  25. #include <linux/regmap.h>
  26. #include <linux/slab.h>
  27. #include <linux/extcon.h>
  28. #include <linux/extcon/sm5502.h>
  29. #define DELAY_MS_DEFAULT 17000 /* unit: millisecond */
  30. struct muic_irq {
  31. unsigned int irq;
  32. const char *name;
  33. unsigned int virq;
  34. };
  35. struct reg_data {
  36. u8 reg;
  37. unsigned int val;
  38. bool invert;
  39. };
  40. struct sm5502_muic_info {
  41. struct device *dev;
  42. struct extcon_dev *edev;
  43. struct i2c_client *i2c;
  44. struct regmap *regmap;
  45. struct regmap_irq_chip_data *irq_data;
  46. struct muic_irq *muic_irqs;
  47. unsigned int num_muic_irqs;
  48. int irq;
  49. bool irq_attach;
  50. bool irq_detach;
  51. struct work_struct irq_work;
  52. struct reg_data *reg_data;
  53. unsigned int num_reg_data;
  54. struct mutex mutex;
  55. /*
  56. * Use delayed workqueue to detect cable state and then
  57. * notify cable state to notifiee/platform through uevent.
  58. * After completing the booting of platform, the extcon provider
  59. * driver should notify cable state to upper layer.
  60. */
  61. struct delayed_work wq_detcable;
  62. };
  63. /* Default value of SM5502 register to bring up MUIC device. */
  64. static struct reg_data sm5502_reg_data[] = {
  65. {
  66. .reg = SM5502_REG_CONTROL,
  67. .val = SM5502_REG_CONTROL_MASK_INT_MASK,
  68. .invert = false,
  69. }, {
  70. .reg = SM5502_REG_INTMASK1,
  71. .val = SM5502_REG_INTM1_KP_MASK
  72. | SM5502_REG_INTM1_LKP_MASK
  73. | SM5502_REG_INTM1_LKR_MASK,
  74. .invert = true,
  75. }, {
  76. .reg = SM5502_REG_INTMASK2,
  77. .val = SM5502_REG_INTM2_VBUS_DET_MASK
  78. | SM5502_REG_INTM2_REV_ACCE_MASK
  79. | SM5502_REG_INTM2_ADC_CHG_MASK
  80. | SM5502_REG_INTM2_STUCK_KEY_MASK
  81. | SM5502_REG_INTM2_STUCK_KEY_RCV_MASK
  82. | SM5502_REG_INTM2_MHL_MASK,
  83. .invert = true,
  84. },
  85. { }
  86. };
  87. /* List of detectable cables */
  88. enum {
  89. EXTCON_CABLE_USB = 0,
  90. EXTCON_CABLE_USB_HOST,
  91. EXTCON_CABLE_TA,
  92. EXTCON_CABLE_END,
  93. };
  94. static const char *sm5502_extcon_cable[] = {
  95. [EXTCON_CABLE_USB] = "USB",
  96. [EXTCON_CABLE_USB_HOST] = "USB-Host",
  97. [EXTCON_CABLE_TA] = "TA",
  98. NULL,
  99. };
  100. /* Define supported accessory type */
  101. enum sm5502_muic_acc_type {
  102. SM5502_MUIC_ADC_GROUND = 0x0,
  103. SM5502_MUIC_ADC_SEND_END_BUTTON,
  104. SM5502_MUIC_ADC_REMOTE_S1_BUTTON,
  105. SM5502_MUIC_ADC_REMOTE_S2_BUTTON,
  106. SM5502_MUIC_ADC_REMOTE_S3_BUTTON,
  107. SM5502_MUIC_ADC_REMOTE_S4_BUTTON,
  108. SM5502_MUIC_ADC_REMOTE_S5_BUTTON,
  109. SM5502_MUIC_ADC_REMOTE_S6_BUTTON,
  110. SM5502_MUIC_ADC_REMOTE_S7_BUTTON,
  111. SM5502_MUIC_ADC_REMOTE_S8_BUTTON,
  112. SM5502_MUIC_ADC_REMOTE_S9_BUTTON,
  113. SM5502_MUIC_ADC_REMOTE_S10_BUTTON,
  114. SM5502_MUIC_ADC_REMOTE_S11_BUTTON,
  115. SM5502_MUIC_ADC_REMOTE_S12_BUTTON,
  116. SM5502_MUIC_ADC_RESERVED_ACC_1,
  117. SM5502_MUIC_ADC_RESERVED_ACC_2,
  118. SM5502_MUIC_ADC_RESERVED_ACC_3,
  119. SM5502_MUIC_ADC_RESERVED_ACC_4,
  120. SM5502_MUIC_ADC_RESERVED_ACC_5,
  121. SM5502_MUIC_ADC_AUDIO_TYPE2,
  122. SM5502_MUIC_ADC_PHONE_POWERED_DEV,
  123. SM5502_MUIC_ADC_TTY_CONVERTER,
  124. SM5502_MUIC_ADC_UART_CABLE,
  125. SM5502_MUIC_ADC_TYPE1_CHARGER,
  126. SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB,
  127. SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB,
  128. SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE,
  129. SM5502_MUIC_ADC_TYPE2_CHARGER,
  130. SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART,
  131. SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART,
  132. SM5502_MUIC_ADC_AUDIO_TYPE1,
  133. SM5502_MUIC_ADC_OPEN = 0x1f,
  134. /* The below accessories have same ADC value (0x1f or 0x1e).
  135. So, Device type1 is used to separate specific accessory. */
  136. /* |---------|--ADC| */
  137. /* | [7:5]|[4:0]| */
  138. SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE = 0x3e, /* | 001|11110| */
  139. SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END = 0x5e, /* | 010|11110| */
  140. /* |Dev Type1|--ADC| */
  141. SM5502_MUIC_ADC_OPEN_USB = 0x5f, /* | 010|11111| */
  142. SM5502_MUIC_ADC_OPEN_TA = 0xdf, /* | 110|11111| */
  143. SM5502_MUIC_ADC_OPEN_USB_OTG = 0xff, /* | 111|11111| */
  144. };
  145. /* List of supported interrupt for SM5502 */
  146. static struct muic_irq sm5502_muic_irqs[] = {
  147. { SM5502_IRQ_INT1_ATTACH, "muic-attach" },
  148. { SM5502_IRQ_INT1_DETACH, "muic-detach" },
  149. { SM5502_IRQ_INT1_KP, "muic-kp" },
  150. { SM5502_IRQ_INT1_LKP, "muic-lkp" },
  151. { SM5502_IRQ_INT1_LKR, "muic-lkr" },
  152. { SM5502_IRQ_INT1_OVP_EVENT, "muic-ovp-event" },
  153. { SM5502_IRQ_INT1_OCP_EVENT, "muic-ocp-event" },
  154. { SM5502_IRQ_INT1_OVP_OCP_DIS, "muic-ovp-ocp-dis" },
  155. { SM5502_IRQ_INT2_VBUS_DET, "muic-vbus-det" },
  156. { SM5502_IRQ_INT2_REV_ACCE, "muic-rev-acce" },
  157. { SM5502_IRQ_INT2_ADC_CHG, "muic-adc-chg" },
  158. { SM5502_IRQ_INT2_STUCK_KEY, "muic-stuck-key" },
  159. { SM5502_IRQ_INT2_STUCK_KEY_RCV, "muic-stuck-key-rcv" },
  160. { SM5502_IRQ_INT2_MHL, "muic-mhl" },
  161. };
  162. /* Define interrupt list of SM5502 to register regmap_irq */
  163. static const struct regmap_irq sm5502_irqs[] = {
  164. /* INT1 interrupts */
  165. { .reg_offset = 0, .mask = SM5502_IRQ_INT1_ATTACH_MASK, },
  166. { .reg_offset = 0, .mask = SM5502_IRQ_INT1_DETACH_MASK, },
  167. { .reg_offset = 0, .mask = SM5502_IRQ_INT1_KP_MASK, },
  168. { .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKP_MASK, },
  169. { .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKR_MASK, },
  170. { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_EVENT_MASK, },
  171. { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OCP_EVENT_MASK, },
  172. { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_OCP_DIS_MASK, },
  173. /* INT2 interrupts */
  174. { .reg_offset = 1, .mask = SM5502_IRQ_INT2_VBUS_DET_MASK,},
  175. { .reg_offset = 1, .mask = SM5502_IRQ_INT2_REV_ACCE_MASK, },
  176. { .reg_offset = 1, .mask = SM5502_IRQ_INT2_ADC_CHG_MASK, },
  177. { .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_MASK, },
  178. { .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_RCV_MASK, },
  179. { .reg_offset = 1, .mask = SM5502_IRQ_INT2_MHL_MASK, },
  180. };
  181. static const struct regmap_irq_chip sm5502_muic_irq_chip = {
  182. .name = "sm5502",
  183. .status_base = SM5502_REG_INT1,
  184. .mask_base = SM5502_REG_INTMASK1,
  185. .mask_invert = false,
  186. .num_regs = 2,
  187. .irqs = sm5502_irqs,
  188. .num_irqs = ARRAY_SIZE(sm5502_irqs),
  189. };
  190. /* Define regmap configuration of SM5502 for I2C communication */
  191. static bool sm5502_muic_volatile_reg(struct device *dev, unsigned int reg)
  192. {
  193. switch (reg) {
  194. case SM5502_REG_INTMASK1:
  195. case SM5502_REG_INTMASK2:
  196. return true;
  197. default:
  198. break;
  199. }
  200. return false;
  201. }
  202. static const struct regmap_config sm5502_muic_regmap_config = {
  203. .reg_bits = 8,
  204. .val_bits = 8,
  205. .volatile_reg = sm5502_muic_volatile_reg,
  206. .max_register = SM5502_REG_END,
  207. };
  208. /* Change DM_CON/DP_CON/VBUSIN switch according to cable type */
  209. static int sm5502_muic_set_path(struct sm5502_muic_info *info,
  210. unsigned int con_sw, unsigned int vbus_sw,
  211. bool attached)
  212. {
  213. int ret;
  214. if (!attached) {
  215. con_sw = DM_DP_SWITCH_OPEN;
  216. vbus_sw = VBUSIN_SWITCH_OPEN;
  217. }
  218. switch (con_sw) {
  219. case DM_DP_SWITCH_OPEN:
  220. case DM_DP_SWITCH_USB:
  221. case DM_DP_SWITCH_AUDIO:
  222. case DM_DP_SWITCH_UART:
  223. ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1,
  224. SM5502_REG_MANUAL_SW1_DP_MASK |
  225. SM5502_REG_MANUAL_SW1_DM_MASK,
  226. con_sw);
  227. if (ret < 0) {
  228. dev_err(info->dev,
  229. "cannot update DM_CON/DP_CON switch\n");
  230. return ret;
  231. }
  232. break;
  233. default:
  234. dev_err(info->dev, "Unknown DM_CON/DP_CON switch type (%d)\n",
  235. con_sw);
  236. return -EINVAL;
  237. };
  238. switch (vbus_sw) {
  239. case VBUSIN_SWITCH_OPEN:
  240. case VBUSIN_SWITCH_VBUSOUT:
  241. case VBUSIN_SWITCH_MIC:
  242. case VBUSIN_SWITCH_VBUSOUT_WITH_USB:
  243. ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1,
  244. SM5502_REG_MANUAL_SW1_VBUSIN_MASK,
  245. vbus_sw);
  246. if (ret < 0) {
  247. dev_err(info->dev,
  248. "cannot update VBUSIN switch\n");
  249. return ret;
  250. }
  251. break;
  252. default:
  253. dev_err(info->dev, "Unknown VBUS switch type (%d)\n", vbus_sw);
  254. return -EINVAL;
  255. };
  256. return 0;
  257. }
  258. /* Return cable type of attached or detached accessories */
  259. static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)
  260. {
  261. unsigned int cable_type = -1, adc, dev_type1;
  262. int ret;
  263. /* Read ADC value according to external cable or button */
  264. ret = regmap_read(info->regmap, SM5502_REG_ADC, &adc);
  265. if (ret) {
  266. dev_err(info->dev, "failed to read ADC register\n");
  267. return ret;
  268. }
  269. /*
  270. * If ADC is SM5502_MUIC_ADC_GROUND(0x0), external cable hasn't
  271. * connected with to MUIC device.
  272. */
  273. cable_type &= SM5502_REG_ADC_MASK;
  274. if (cable_type == SM5502_MUIC_ADC_GROUND)
  275. return SM5502_MUIC_ADC_GROUND;
  276. switch (cable_type) {
  277. case SM5502_MUIC_ADC_GROUND:
  278. case SM5502_MUIC_ADC_SEND_END_BUTTON:
  279. case SM5502_MUIC_ADC_REMOTE_S1_BUTTON:
  280. case SM5502_MUIC_ADC_REMOTE_S2_BUTTON:
  281. case SM5502_MUIC_ADC_REMOTE_S3_BUTTON:
  282. case SM5502_MUIC_ADC_REMOTE_S4_BUTTON:
  283. case SM5502_MUIC_ADC_REMOTE_S5_BUTTON:
  284. case SM5502_MUIC_ADC_REMOTE_S6_BUTTON:
  285. case SM5502_MUIC_ADC_REMOTE_S7_BUTTON:
  286. case SM5502_MUIC_ADC_REMOTE_S8_BUTTON:
  287. case SM5502_MUIC_ADC_REMOTE_S9_BUTTON:
  288. case SM5502_MUIC_ADC_REMOTE_S10_BUTTON:
  289. case SM5502_MUIC_ADC_REMOTE_S11_BUTTON:
  290. case SM5502_MUIC_ADC_REMOTE_S12_BUTTON:
  291. case SM5502_MUIC_ADC_RESERVED_ACC_1:
  292. case SM5502_MUIC_ADC_RESERVED_ACC_2:
  293. case SM5502_MUIC_ADC_RESERVED_ACC_3:
  294. case SM5502_MUIC_ADC_RESERVED_ACC_4:
  295. case SM5502_MUIC_ADC_RESERVED_ACC_5:
  296. case SM5502_MUIC_ADC_AUDIO_TYPE2:
  297. case SM5502_MUIC_ADC_PHONE_POWERED_DEV:
  298. case SM5502_MUIC_ADC_TTY_CONVERTER:
  299. case SM5502_MUIC_ADC_UART_CABLE:
  300. case SM5502_MUIC_ADC_TYPE1_CHARGER:
  301. case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB:
  302. case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB:
  303. case SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE:
  304. case SM5502_MUIC_ADC_TYPE2_CHARGER:
  305. case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART:
  306. case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART:
  307. break;
  308. case SM5502_MUIC_ADC_AUDIO_TYPE1:
  309. /*
  310. * Check whether cable type is
  311. * SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE
  312. * or SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END
  313. * by using Button event.
  314. */
  315. break;
  316. case SM5502_MUIC_ADC_OPEN:
  317. ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1,
  318. &dev_type1);
  319. if (ret) {
  320. dev_err(info->dev, "failed to read DEV_TYPE1 reg\n");
  321. return ret;
  322. }
  323. switch (dev_type1) {
  324. case SM5502_REG_DEV_TYPE1_USB_SDP_MASK:
  325. cable_type = SM5502_MUIC_ADC_OPEN_USB;
  326. break;
  327. case SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK:
  328. cable_type = SM5502_MUIC_ADC_OPEN_TA;
  329. break;
  330. case SM5502_REG_DEV_TYPE1_USB_OTG_MASK:
  331. cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG;
  332. break;
  333. default:
  334. dev_dbg(info->dev,
  335. "cannot identify the cable type: adc(0x%x) "
  336. "dev_type1(0x%x)\n", adc, dev_type1);
  337. return -EINVAL;
  338. };
  339. break;
  340. default:
  341. dev_err(info->dev,
  342. "failed to identify the cable type: adc(0x%x)\n", adc);
  343. return -EINVAL;
  344. };
  345. return cable_type;
  346. }
  347. static int sm5502_muic_cable_handler(struct sm5502_muic_info *info,
  348. bool attached)
  349. {
  350. static unsigned int prev_cable_type = SM5502_MUIC_ADC_GROUND;
  351. const char **cable_names = info->edev->supported_cable;
  352. unsigned int cable_type = SM5502_MUIC_ADC_GROUND;
  353. unsigned int con_sw = DM_DP_SWITCH_OPEN;
  354. unsigned int vbus_sw = VBUSIN_SWITCH_OPEN;
  355. unsigned int idx = 0;
  356. int ret;
  357. if (!cable_names)
  358. return 0;
  359. /* Get the type of attached or detached cable */
  360. if (attached)
  361. cable_type = sm5502_muic_get_cable_type(info);
  362. else if (!attached)
  363. cable_type = prev_cable_type;
  364. prev_cable_type = cable_type;
  365. switch (cable_type) {
  366. case SM5502_MUIC_ADC_OPEN_USB:
  367. idx = EXTCON_CABLE_USB;
  368. con_sw = DM_DP_SWITCH_USB;
  369. vbus_sw = VBUSIN_SWITCH_VBUSOUT_WITH_USB;
  370. break;
  371. case SM5502_MUIC_ADC_OPEN_TA:
  372. idx = EXTCON_CABLE_TA;
  373. con_sw = DM_DP_SWITCH_OPEN;
  374. vbus_sw = VBUSIN_SWITCH_VBUSOUT;
  375. break;
  376. case SM5502_MUIC_ADC_OPEN_USB_OTG:
  377. idx = EXTCON_CABLE_USB_HOST;
  378. con_sw = DM_DP_SWITCH_USB;
  379. vbus_sw = VBUSIN_SWITCH_OPEN;
  380. break;
  381. default:
  382. dev_dbg(info->dev,
  383. "cannot handle this cable_type (0x%x)\n", cable_type);
  384. return 0;
  385. };
  386. /* Change internal hardware path(DM_CON/DP_CON, VBUSIN) */
  387. ret = sm5502_muic_set_path(info, con_sw, vbus_sw, attached);
  388. if (ret < 0)
  389. return ret;
  390. /* Change the state of external accessory */
  391. extcon_set_cable_state(info->edev, cable_names[idx], attached);
  392. return 0;
  393. }
  394. static void sm5502_muic_irq_work(struct work_struct *work)
  395. {
  396. struct sm5502_muic_info *info = container_of(work,
  397. struct sm5502_muic_info, irq_work);
  398. int ret = 0;
  399. if (!info->edev)
  400. return;
  401. mutex_lock(&info->mutex);
  402. /* Detect attached or detached cables */
  403. if (info->irq_attach) {
  404. ret = sm5502_muic_cable_handler(info, true);
  405. info->irq_attach = false;
  406. }
  407. if (info->irq_detach) {
  408. ret = sm5502_muic_cable_handler(info, false);
  409. info->irq_detach = false;
  410. }
  411. if (ret < 0)
  412. dev_err(info->dev, "failed to handle MUIC interrupt\n");
  413. mutex_unlock(&info->mutex);
  414. return;
  415. }
  416. /*
  417. * Sets irq_attach or irq_detach in sm5502_muic_info and returns 0.
  418. * Returns -ESRCH if irq_type does not match registered IRQ for this dev type.
  419. */
  420. static int sm5502_parse_irq(struct sm5502_muic_info *info, int irq_type)
  421. {
  422. switch (irq_type) {
  423. case SM5502_IRQ_INT1_ATTACH:
  424. info->irq_attach = true;
  425. break;
  426. case SM5502_IRQ_INT1_DETACH:
  427. info->irq_detach = true;
  428. break;
  429. case SM5502_IRQ_INT1_KP:
  430. case SM5502_IRQ_INT1_LKP:
  431. case SM5502_IRQ_INT1_LKR:
  432. case SM5502_IRQ_INT1_OVP_EVENT:
  433. case SM5502_IRQ_INT1_OCP_EVENT:
  434. case SM5502_IRQ_INT1_OVP_OCP_DIS:
  435. case SM5502_IRQ_INT2_VBUS_DET:
  436. case SM5502_IRQ_INT2_REV_ACCE:
  437. case SM5502_IRQ_INT2_ADC_CHG:
  438. case SM5502_IRQ_INT2_STUCK_KEY:
  439. case SM5502_IRQ_INT2_STUCK_KEY_RCV:
  440. case SM5502_IRQ_INT2_MHL:
  441. default:
  442. break;
  443. }
  444. return 0;
  445. }
  446. static irqreturn_t sm5502_muic_irq_handler(int irq, void *data)
  447. {
  448. struct sm5502_muic_info *info = data;
  449. int i, irq_type = -1, ret;
  450. for (i = 0; i < info->num_muic_irqs; i++)
  451. if (irq == info->muic_irqs[i].virq)
  452. irq_type = info->muic_irqs[i].irq;
  453. ret = sm5502_parse_irq(info, irq_type);
  454. if (ret < 0) {
  455. dev_warn(info->dev, "cannot handle is interrupt:%d\n",
  456. irq_type);
  457. return IRQ_HANDLED;
  458. }
  459. schedule_work(&info->irq_work);
  460. return IRQ_HANDLED;
  461. }
  462. static void sm5502_muic_detect_cable_wq(struct work_struct *work)
  463. {
  464. struct sm5502_muic_info *info = container_of(to_delayed_work(work),
  465. struct sm5502_muic_info, wq_detcable);
  466. int ret;
  467. /* Notify the state of connector cable or not */
  468. ret = sm5502_muic_cable_handler(info, true);
  469. if (ret < 0)
  470. dev_warn(info->dev, "failed to detect cable state\n");
  471. }
  472. static void sm5502_init_dev_type(struct sm5502_muic_info *info)
  473. {
  474. unsigned int reg_data, vendor_id, version_id;
  475. int i, ret;
  476. /* To test I2C, Print version_id and vendor_id of SM5502 */
  477. ret = regmap_read(info->regmap, SM5502_REG_DEVICE_ID, &reg_data);
  478. if (ret) {
  479. dev_err(info->dev,
  480. "failed to read DEVICE_ID register: %d\n", ret);
  481. return;
  482. }
  483. vendor_id = ((reg_data & SM5502_REG_DEVICE_ID_VENDOR_MASK) >>
  484. SM5502_REG_DEVICE_ID_VENDOR_SHIFT);
  485. version_id = ((reg_data & SM5502_REG_DEVICE_ID_VERSION_MASK) >>
  486. SM5502_REG_DEVICE_ID_VERSION_SHIFT);
  487. dev_info(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n",
  488. version_id, vendor_id);
  489. /* Initiazle the register of SM5502 device to bring-up */
  490. for (i = 0; i < info->num_reg_data; i++) {
  491. unsigned int val = 0;
  492. if (!info->reg_data[i].invert)
  493. val |= ~info->reg_data[i].val;
  494. else
  495. val = info->reg_data[i].val;
  496. regmap_write(info->regmap, info->reg_data[i].reg, val);
  497. }
  498. }
  499. static int sm5022_muic_i2c_probe(struct i2c_client *i2c,
  500. const struct i2c_device_id *id)
  501. {
  502. struct device_node *np = i2c->dev.of_node;
  503. struct sm5502_muic_info *info;
  504. int i, ret, irq_flags;
  505. if (!np)
  506. return -EINVAL;
  507. info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL);
  508. if (!info)
  509. return -ENOMEM;
  510. i2c_set_clientdata(i2c, info);
  511. info->dev = &i2c->dev;
  512. info->i2c = i2c;
  513. info->irq = i2c->irq;
  514. info->muic_irqs = sm5502_muic_irqs;
  515. info->num_muic_irqs = ARRAY_SIZE(sm5502_muic_irqs);
  516. info->reg_data = sm5502_reg_data;
  517. info->num_reg_data = ARRAY_SIZE(sm5502_reg_data);
  518. mutex_init(&info->mutex);
  519. INIT_WORK(&info->irq_work, sm5502_muic_irq_work);
  520. info->regmap = devm_regmap_init_i2c(i2c, &sm5502_muic_regmap_config);
  521. if (IS_ERR(info->regmap)) {
  522. ret = PTR_ERR(info->regmap);
  523. dev_err(info->dev, "failed to allocate register map: %d\n",
  524. ret);
  525. return ret;
  526. }
  527. /* Support irq domain for SM5502 MUIC device */
  528. irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED;
  529. ret = regmap_add_irq_chip(info->regmap, info->irq, irq_flags, 0,
  530. &sm5502_muic_irq_chip, &info->irq_data);
  531. if (ret != 0) {
  532. dev_err(info->dev, "failed to request IRQ %d: %d\n",
  533. info->irq, ret);
  534. return ret;
  535. }
  536. for (i = 0; i < info->num_muic_irqs; i++) {
  537. struct muic_irq *muic_irq = &info->muic_irqs[i];
  538. unsigned int virq = 0;
  539. virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq);
  540. if (virq <= 0)
  541. return -EINVAL;
  542. muic_irq->virq = virq;
  543. ret = devm_request_threaded_irq(info->dev, virq, NULL,
  544. sm5502_muic_irq_handler,
  545. IRQF_NO_SUSPEND,
  546. muic_irq->name, info);
  547. if (ret) {
  548. dev_err(info->dev, "failed: irq request (IRQ: %d,"
  549. " error :%d)\n", muic_irq->irq, ret);
  550. return ret;
  551. }
  552. }
  553. /* Allocate extcon device */
  554. info->edev = devm_extcon_dev_allocate(info->dev, sm5502_extcon_cable);
  555. if (IS_ERR(info->edev)) {
  556. dev_err(info->dev, "failed to allocate memory for extcon\n");
  557. return -ENOMEM;
  558. }
  559. info->edev->name = np->name;
  560. /* Register extcon device */
  561. ret = devm_extcon_dev_register(info->dev, info->edev);
  562. if (ret) {
  563. dev_err(info->dev, "failed to register extcon device\n");
  564. return ret;
  565. }
  566. /*
  567. * Detect accessory after completing the initialization of platform
  568. *
  569. * - Use delayed workqueue to detect cable state and then
  570. * notify cable state to notifiee/platform through uevent.
  571. * After completing the booting of platform, the extcon provider
  572. * driver should notify cable state to upper layer.
  573. */
  574. INIT_DELAYED_WORK(&info->wq_detcable, sm5502_muic_detect_cable_wq);
  575. queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
  576. msecs_to_jiffies(DELAY_MS_DEFAULT));
  577. /* Initialize SM5502 device and print vendor id and version id */
  578. sm5502_init_dev_type(info);
  579. return 0;
  580. }
  581. static int sm5502_muic_i2c_remove(struct i2c_client *i2c)
  582. {
  583. struct sm5502_muic_info *info = i2c_get_clientdata(i2c);
  584. regmap_del_irq_chip(info->irq, info->irq_data);
  585. return 0;
  586. }
  587. static struct of_device_id sm5502_dt_match[] = {
  588. { .compatible = "siliconmitus,sm5502-muic" },
  589. { },
  590. };
  591. #ifdef CONFIG_PM_SLEEP
  592. static int sm5502_muic_suspend(struct device *dev)
  593. {
  594. struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
  595. struct sm5502_muic_info *info = i2c_get_clientdata(i2c);
  596. enable_irq_wake(info->irq);
  597. return 0;
  598. }
  599. static int sm5502_muic_resume(struct device *dev)
  600. {
  601. struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
  602. struct sm5502_muic_info *info = i2c_get_clientdata(i2c);
  603. disable_irq_wake(info->irq);
  604. return 0;
  605. }
  606. #endif
  607. static SIMPLE_DEV_PM_OPS(sm5502_muic_pm_ops,
  608. sm5502_muic_suspend, sm5502_muic_resume);
  609. static const struct i2c_device_id sm5502_i2c_id[] = {
  610. { "sm5502", TYPE_SM5502 },
  611. { }
  612. };
  613. MODULE_DEVICE_TABLE(i2c, sm5502_i2c_id);
  614. static struct i2c_driver sm5502_muic_i2c_driver = {
  615. .driver = {
  616. .name = "sm5502",
  617. .owner = THIS_MODULE,
  618. .pm = &sm5502_muic_pm_ops,
  619. .of_match_table = sm5502_dt_match,
  620. },
  621. .probe = sm5022_muic_i2c_probe,
  622. .remove = sm5502_muic_i2c_remove,
  623. .id_table = sm5502_i2c_id,
  624. };
  625. static int __init sm5502_muic_i2c_init(void)
  626. {
  627. return i2c_add_driver(&sm5502_muic_i2c_driver);
  628. }
  629. subsys_initcall(sm5502_muic_i2c_init);
  630. MODULE_DESCRIPTION("Silicon Mitus SM5502 Extcon driver");
  631. MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
  632. MODULE_LICENSE("GPL");