lnbh29.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // SPDX-License-Identifier: GPL-2.0
  2. //
  3. // Driver for LNB supply and control IC STMicroelectronics LNBH29
  4. //
  5. // Copyright (c) 2018 Socionext Inc.
  6. #include <linux/module.h>
  7. #include <linux/init.h>
  8. #include <linux/slab.h>
  9. #include <media/dvb_frontend.h>
  10. #include "lnbh29.h"
  11. /**
  12. * struct lnbh29_priv - LNBH29 driver private data
  13. * @i2c: Pointer to the I2C adapter structure
  14. * @i2c_address: I2C address of LNBH29 chip
  15. * @config: Registers configuration
  16. * offset 0: 1st register address, always 0x01 (DATA)
  17. * offset 1: DATA register value
  18. */
  19. struct lnbh29_priv {
  20. struct i2c_adapter *i2c;
  21. u8 i2c_address;
  22. u8 config[2];
  23. };
  24. #define LNBH29_STATUS_OLF BIT(0)
  25. #define LNBH29_STATUS_OTF BIT(1)
  26. #define LNBH29_STATUS_VMON BIT(2)
  27. #define LNBH29_STATUS_PNG BIT(3)
  28. #define LNBH29_STATUS_PDO BIT(4)
  29. #define LNBH29_VSEL_MASK GENMASK(2, 0)
  30. #define LNBH29_VSEL_0 0x00
  31. /* Min: 13.188V, Typ: 13.667V, Max:14V */
  32. #define LNBH29_VSEL_13 0x03
  33. /* Min: 18.158V, Typ: 18.817V, Max:19.475V */
  34. #define LNBH29_VSEL_18 0x07
  35. static int lnbh29_read_vmon(struct lnbh29_priv *priv)
  36. {
  37. u8 addr = 0x00;
  38. u8 status[2];
  39. int ret;
  40. struct i2c_msg msg[2] = {
  41. {
  42. .addr = priv->i2c_address,
  43. .flags = 0,
  44. .len = 1,
  45. .buf = &addr
  46. }, {
  47. .addr = priv->i2c_address,
  48. .flags = I2C_M_RD,
  49. .len = sizeof(status),
  50. .buf = status
  51. }
  52. };
  53. ret = i2c_transfer(priv->i2c, msg, 2);
  54. if (ret >= 0 && ret != 2)
  55. ret = -EIO;
  56. if (ret < 0) {
  57. dev_dbg(&priv->i2c->dev, "LNBH29 I2C transfer failed (%d)\n",
  58. ret);
  59. return ret;
  60. }
  61. if (status[0] & (LNBH29_STATUS_OLF | LNBH29_STATUS_VMON)) {
  62. dev_err(&priv->i2c->dev,
  63. "LNBH29 voltage in failure state, status reg 0x%x\n",
  64. status[0]);
  65. return -EIO;
  66. }
  67. return 0;
  68. }
  69. static int lnbh29_set_voltage(struct dvb_frontend *fe,
  70. enum fe_sec_voltage voltage)
  71. {
  72. struct lnbh29_priv *priv = fe->sec_priv;
  73. u8 data_reg;
  74. int ret;
  75. struct i2c_msg msg = {
  76. .addr = priv->i2c_address,
  77. .flags = 0,
  78. .len = sizeof(priv->config),
  79. .buf = priv->config
  80. };
  81. switch (voltage) {
  82. case SEC_VOLTAGE_OFF:
  83. data_reg = LNBH29_VSEL_0;
  84. break;
  85. case SEC_VOLTAGE_13:
  86. data_reg = LNBH29_VSEL_13;
  87. break;
  88. case SEC_VOLTAGE_18:
  89. data_reg = LNBH29_VSEL_18;
  90. break;
  91. default:
  92. return -EINVAL;
  93. }
  94. priv->config[1] &= ~LNBH29_VSEL_MASK;
  95. priv->config[1] |= data_reg;
  96. ret = i2c_transfer(priv->i2c, &msg, 1);
  97. if (ret >= 0 && ret != 1)
  98. ret = -EIO;
  99. if (ret < 0) {
  100. dev_err(&priv->i2c->dev, "LNBH29 I2C transfer error (%d)\n",
  101. ret);
  102. return ret;
  103. }
  104. /* Soft-start time (Vout 0V to 18V) is Typ. 6ms. */
  105. usleep_range(6000, 20000);
  106. if (voltage == SEC_VOLTAGE_OFF)
  107. return 0;
  108. return lnbh29_read_vmon(priv);
  109. }
  110. static void lnbh29_release(struct dvb_frontend *fe)
  111. {
  112. lnbh29_set_voltage(fe, SEC_VOLTAGE_OFF);
  113. kfree(fe->sec_priv);
  114. fe->sec_priv = NULL;
  115. }
  116. struct dvb_frontend *lnbh29_attach(struct dvb_frontend *fe,
  117. struct lnbh29_config *cfg,
  118. struct i2c_adapter *i2c)
  119. {
  120. struct lnbh29_priv *priv;
  121. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  122. if (!priv)
  123. return NULL;
  124. priv->i2c_address = (cfg->i2c_address >> 1);
  125. priv->i2c = i2c;
  126. priv->config[0] = 0x01;
  127. priv->config[1] = cfg->data_config;
  128. fe->sec_priv = priv;
  129. if (lnbh29_set_voltage(fe, SEC_VOLTAGE_OFF)) {
  130. dev_err(&i2c->dev, "no LNBH29 found at I2C addr 0x%02x\n",
  131. priv->i2c_address);
  132. kfree(priv);
  133. fe->sec_priv = NULL;
  134. return NULL;
  135. }
  136. fe->ops.release_sec = lnbh29_release;
  137. fe->ops.set_voltage = lnbh29_set_voltage;
  138. dev_info(&i2c->dev, "LNBH29 attached at I2C addr 0x%02x\n",
  139. priv->i2c_address);
  140. return fe;
  141. }
  142. EXPORT_SYMBOL(lnbh29_attach);
  143. MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
  144. MODULE_DESCRIPTION("STMicroelectronics LNBH29 driver");
  145. MODULE_LICENSE("GPL v2");