279 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Allegro A8293 SEC driver4 *5 * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>6 */7 8#include "a8293.h"9 10#define A8293_FLAG_ODT 0x1011 12struct a8293_dev {13 struct i2c_client *client;14 u8 reg[2];15 int volt_slew_nanos_per_mv;16};17 18/*19 * When increasing voltage, do so in minimal steps over time, minimizing20 * risk of vIN undervoltage.21 */22 23static int a8293_set_voltage_slew(struct a8293_dev *dev,24 struct i2c_client *client,25 enum fe_sec_voltage fe_sec_voltage,26 int min_nanos_per_mv)27{28 int ret;29 u8 reg0, reg1;30 int new_volt_idx;31 const int idx_to_mv[] = {32 0, 12709, 13042, 13375, 14042, 15042, 18042, 18709, 1904233 };34 const u8 idx_to_reg[] = {35 0x00, 0x20, 0x21, 0x22, 0x24, 0x27, 0x28, 0x2A, 0x2B36 };37 int this_volt_idx;38 u8 status;39 int prev_volt_idx;40 41 dev_dbg(&client->dev, "set_voltage_slew fe_sec_voltage=%d\n",42 fe_sec_voltage);43 44 /* Read status register to clear any stale faults. */45 ret = i2c_master_recv(client, &status, 1);46 if (ret < 0)47 goto err;48 49 /* Determine previous voltage */50 switch (dev->reg[0] & 0x2F) {51 case 0x00:52 prev_volt_idx = 0;53 break;54 case 0x20:55 prev_volt_idx = 1;56 break;57 case 0x21:58 prev_volt_idx = 2;59 break;60 case 0x22:61 prev_volt_idx = 3;62 break;63 case 0x24:64 prev_volt_idx = 4;65 break;66 case 0x27:67 prev_volt_idx = 5;68 break;69 case 0x28:70 prev_volt_idx = 6;71 break;72 case 0x2A:73 prev_volt_idx = 7;74 break;75 case 0x2B:76 prev_volt_idx = 8;77 break;78 default:79 prev_volt_idx = 0;80 }81 82 /* Determine new voltage */83 switch (fe_sec_voltage) {84 case SEC_VOLTAGE_OFF:85 new_volt_idx = 0;86 break;87 case SEC_VOLTAGE_13:88 new_volt_idx = 2;89 break;90 case SEC_VOLTAGE_18:91 new_volt_idx = 6;92 break;93 default:94 ret = -EINVAL;95 goto err;96 }97 98 /* Slew to new voltage if new voltage is greater than current voltage */99 this_volt_idx = prev_volt_idx;100 if (this_volt_idx < new_volt_idx) {101 while (this_volt_idx < new_volt_idx) {102 int delta_mv = idx_to_mv[this_volt_idx+1] - idx_to_mv[this_volt_idx];103 int min_wait_time = delta_mv * min_nanos_per_mv;104 105 reg0 = idx_to_reg[this_volt_idx+1];106 reg0 |= A8293_FLAG_ODT;107 108 ret = i2c_master_send(client, ®0, 1);109 if (ret < 0)110 goto err;111 dev->reg[0] = reg0;112 this_volt_idx++;113 usleep_range(min_wait_time, min_wait_time * 2);114 }115 } else { /* Else just set the voltage */116 reg0 = idx_to_reg[new_volt_idx];117 reg0 |= A8293_FLAG_ODT;118 ret = i2c_master_send(client, ®0, 1);119 if (ret < 0)120 goto err;121 dev->reg[0] = reg0;122 }123 124 /* TMODE=0, TGATE=1 */125 reg1 = 0x82;126 if (reg1 != dev->reg[1]) {127 ret = i2c_master_send(client, ®1, 1);128 if (ret < 0)129 goto err;130 dev->reg[1] = reg1;131 }132 133 usleep_range(1500, 5000);134 135 return 0;136err:137 dev_dbg(&client->dev, "failed=%d\n", ret);138 return ret;139}140 141 142static int a8293_set_voltage_noslew(struct dvb_frontend *fe,143 enum fe_sec_voltage fe_sec_voltage)144{145 struct a8293_dev *dev = fe->sec_priv;146 struct i2c_client *client = dev->client;147 int ret;148 u8 reg0, reg1;149 150 dev_dbg(&client->dev, "set_voltage_noslew fe_sec_voltage=%d\n",151 fe_sec_voltage);152 153 switch (fe_sec_voltage) {154 case SEC_VOLTAGE_OFF:155 /* ENB=0 */156 reg0 = 0x10;157 break;158 case SEC_VOLTAGE_13:159 /* VSEL0=1, VSEL1=0, VSEL2=0, VSEL3=0, ENB=1*/160 reg0 = 0x31;161 break;162 case SEC_VOLTAGE_18:163 /* VSEL0=0, VSEL1=0, VSEL2=0, VSEL3=1, ENB=1*/164 reg0 = 0x38;165 break;166 default:167 ret = -EINVAL;168 goto err;169 }170 if (reg0 != dev->reg[0]) {171 ret = i2c_master_send(client, ®0, 1);172 if (ret < 0)173 goto err;174 dev->reg[0] = reg0;175 }176 177 /* TMODE=0, TGATE=1 */178 reg1 = 0x82;179 if (reg1 != dev->reg[1]) {180 ret = i2c_master_send(client, ®1, 1);181 if (ret < 0)182 goto err;183 dev->reg[1] = reg1;184 }185 186 usleep_range(1500, 50000);187 return 0;188err:189 dev_dbg(&client->dev, "failed=%d\n", ret);190 return ret;191}192 193static int a8293_set_voltage(struct dvb_frontend *fe,194 enum fe_sec_voltage fe_sec_voltage)195{196 struct a8293_dev *dev = fe->sec_priv;197 struct i2c_client *client = dev->client;198 int volt_slew_nanos_per_mv = dev->volt_slew_nanos_per_mv;199 200 dev_dbg(&client->dev, "set_voltage volt_slew_nanos_per_mv=%d\n",201 volt_slew_nanos_per_mv);202 203 /* Use slew version if slew rate is set to a sane value */204 if (volt_slew_nanos_per_mv > 0 && volt_slew_nanos_per_mv < 1600)205 a8293_set_voltage_slew(dev, client, fe_sec_voltage,206 volt_slew_nanos_per_mv);207 else208 a8293_set_voltage_noslew(fe, fe_sec_voltage);209 210 return 0;211}212 213static int a8293_probe(struct i2c_client *client)214{215 struct a8293_dev *dev;216 struct a8293_platform_data *pdata = client->dev.platform_data;217 struct dvb_frontend *fe = pdata->dvb_frontend;218 int ret;219 u8 buf[2];220 221 dev = kzalloc(sizeof(*dev), GFP_KERNEL);222 if (!dev) {223 ret = -ENOMEM;224 goto err;225 }226 227 dev->client = client;228 dev->volt_slew_nanos_per_mv = pdata->volt_slew_nanos_per_mv;229 230 /* check if the SEC is there */231 ret = i2c_master_recv(client, buf, 2);232 if (ret < 0)233 goto err_kfree;234 235 /* override frontend ops */236 fe->ops.set_voltage = a8293_set_voltage;237 fe->sec_priv = dev;238 i2c_set_clientdata(client, dev);239 240 dev_info(&client->dev, "Allegro A8293 SEC successfully attached\n");241 return 0;242err_kfree:243 kfree(dev);244err:245 dev_dbg(&client->dev, "failed=%d\n", ret);246 return ret;247}248 249static void a8293_remove(struct i2c_client *client)250{251 struct a8293_dev *dev = i2c_get_clientdata(client);252 253 dev_dbg(&client->dev, "\n");254 255 kfree(dev);256}257 258static const struct i2c_device_id a8293_id_table[] = {259 { "a8293" },260 {}261};262MODULE_DEVICE_TABLE(i2c, a8293_id_table);263 264static struct i2c_driver a8293_driver = {265 .driver = {266 .name = "a8293",267 .suppress_bind_attrs = true,268 },269 .probe = a8293_probe,270 .remove = a8293_remove,271 .id_table = a8293_id_table,272};273 274module_i2c_driver(a8293_driver);275 276MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");277MODULE_DESCRIPTION("Allegro A8293 SEC driver");278MODULE_LICENSE("GPL");279