/* ADC specific channel reference voltage 3.5V */ #define SC27XX_ADC_REFVOL_VDD35 3500000
/* ADC default channel reference voltage is 2.8V */ #define SC27XX_ADC_REFVOL_VDD28 2800000
struct sc27xx_adc_data { struct device *dev; struct regulator *volref; struct regmap *regmap; /* lock to protect against multiple access to the device */ struct mutex lock; /* * One hardware spinlock to synchronize between the multiple * subsystems which will access the unique ADC controller.
*/ struct hwspinlock *hwlock; int channel_scale[SC27XX_ADC_CHANNEL_MAX];
u32 base; int irq; conststruct sc27xx_adc_variant_data *var_data;
};
/* * Since different PMICs of SC27xx series can have different * address and ratio, we should save ratio config and base * in the device data structure.
*/ struct sc27xx_adc_variant_data {
u32 module_en;
u32 clk_en;
u32 scale_shift;
u32 scale_mask; conststruct sc27xx_adc_linear_graph *bscale_cal; conststruct sc27xx_adc_linear_graph *sscale_cal; void (*init_scale)(struct sc27xx_adc_data *data); int (*get_ratio)(int channel, int scale); bool set_volref;
};
struct sc27xx_adc_linear_graph { int volt0; int adc0; int volt1; int adc1;
};
/* * According to the datasheet, we can convert one ADC value to one voltage value * through 2 points in the linear graph. If the voltage is less than 1.2v, we * should use the small-scale graph, and if more than 1.2v, we should use the * big-scale graph.
*/ staticstruct sc27xx_adc_linear_graph big_scale_graph = {
4200, 3310,
3600, 2832,
};
/* Only need to calibrate the adc values in the linear graph. */
graph->adc0 = sc27xx_adc_get_calib_data(calib_data, calib_graph->adc0);
graph->adc1 = sc27xx_adc_get_calib_data(calib_data >> 8,
calib_graph->adc1);
return 0;
}
staticint sc2720_adc_get_ratio(int channel, int scale)
{ switch (channel) { case 14: switch (scale) { case 0: return SC27XX_VOLT_RATIO(68, 900); case 1: return SC27XX_VOLT_RATIO(68, 1760); case 2: return SC27XX_VOLT_RATIO(68, 2327); case 3: return SC27XX_VOLT_RATIO(68, 3654); default: return SC27XX_VOLT_RATIO(1, 1);
} case 16: switch (scale) { case 0: return SC27XX_VOLT_RATIO(48, 100); case 1: return SC27XX_VOLT_RATIO(480, 1955); case 2: return SC27XX_VOLT_RATIO(480, 2586); case 3: return SC27XX_VOLT_RATIO(48, 406); default: return SC27XX_VOLT_RATIO(1, 1);
} case 21: case 22: case 23: switch (scale) { case 0: return SC27XX_VOLT_RATIO(3, 8); case 1: return SC27XX_VOLT_RATIO(375, 1955); case 2: return SC27XX_VOLT_RATIO(375, 2586); case 3: return SC27XX_VOLT_RATIO(300, 3248); default: return SC27XX_VOLT_RATIO(1, 1);
} default: switch (scale) { case 0: return SC27XX_VOLT_RATIO(1, 1); case 1: return SC27XX_VOLT_RATIO(1000, 1955); case 2: return SC27XX_VOLT_RATIO(1000, 2586); case 3: return SC27XX_VOLT_RATIO(100, 406); default: return SC27XX_VOLT_RATIO(1, 1);
}
} return SC27XX_VOLT_RATIO(1, 1);
}
staticint sc2721_adc_get_ratio(int channel, int scale)
{ switch (channel) { case 1: case 2: case 3: case 4: return scale ? SC27XX_VOLT_RATIO(400, 1025) :
SC27XX_VOLT_RATIO(1, 1); case 5: return SC27XX_VOLT_RATIO(7, 29); case 7: case 9: return scale ? SC27XX_VOLT_RATIO(100, 125) :
SC27XX_VOLT_RATIO(1, 1); case 14: return SC27XX_VOLT_RATIO(68, 900); case 16: return SC27XX_VOLT_RATIO(48, 100); case 19: return SC27XX_VOLT_RATIO(1, 3); default: return SC27XX_VOLT_RATIO(1, 1);
} return SC27XX_VOLT_RATIO(1, 1);
}
staticint sc2730_adc_get_ratio(int channel, int scale)
{ switch (channel) { case 14: switch (scale) { case 0: return SC27XX_VOLT_RATIO(68, 900); case 1: return SC27XX_VOLT_RATIO(68, 1760); case 2: return SC27XX_VOLT_RATIO(68, 2327); case 3: return SC27XX_VOLT_RATIO(68, 3654); default: return SC27XX_VOLT_RATIO(1, 1);
} case 15: switch (scale) { case 0: return SC27XX_VOLT_RATIO(1, 3); case 1: return SC27XX_VOLT_RATIO(1000, 5865); case 2: return SC27XX_VOLT_RATIO(500, 3879); case 3: return SC27XX_VOLT_RATIO(500, 6090); default: return SC27XX_VOLT_RATIO(1, 1);
} case 16: switch (scale) { case 0: return SC27XX_VOLT_RATIO(48, 100); case 1: return SC27XX_VOLT_RATIO(480, 1955); case 2: return SC27XX_VOLT_RATIO(480, 2586); case 3: return SC27XX_VOLT_RATIO(48, 406); default: return SC27XX_VOLT_RATIO(1, 1);
} case 21: case 22: case 23: switch (scale) { case 0: return SC27XX_VOLT_RATIO(3, 8); case 1: return SC27XX_VOLT_RATIO(375, 1955); case 2: return SC27XX_VOLT_RATIO(375, 2586); case 3: return SC27XX_VOLT_RATIO(300, 3248); default: return SC27XX_VOLT_RATIO(1, 1);
} default: switch (scale) { case 0: return SC27XX_VOLT_RATIO(1, 1); case 1: return SC27XX_VOLT_RATIO(1000, 1955); case 2: return SC27XX_VOLT_RATIO(1000, 2586); case 3: return SC27XX_VOLT_RATIO(1000, 4060); default: return SC27XX_VOLT_RATIO(1, 1);
}
} return SC27XX_VOLT_RATIO(1, 1);
}
staticint sc2731_adc_get_ratio(int channel, int scale)
{ switch (channel) { case 1: case 2: case 3: case 4: return scale ? SC27XX_VOLT_RATIO(400, 1025) :
SC27XX_VOLT_RATIO(1, 1); case 5: return SC27XX_VOLT_RATIO(7, 29); case 6: return SC27XX_VOLT_RATIO(375, 9000); case 7: case 8: return scale ? SC27XX_VOLT_RATIO(100, 125) :
SC27XX_VOLT_RATIO(1, 1); case 19: return SC27XX_VOLT_RATIO(1, 3); default: return SC27XX_VOLT_RATIO(1, 1);
} return SC27XX_VOLT_RATIO(1, 1);
}
/* * According to the datasheet set specific value on some channel.
*/ staticvoid sc2720_adc_scale_init(struct sc27xx_adc_data *data)
{ int i;
for (i = 0; i < SC27XX_ADC_CHANNEL_MAX; i++) { switch (i) { case 5:
data->channel_scale[i] = 3; break; case 7: case 9:
data->channel_scale[i] = 2; break; case 13:
data->channel_scale[i] = 1; break; case 19: case 30: case 31:
data->channel_scale[i] = 3; break; default:
data->channel_scale[i] = 0; break;
}
}
}
staticvoid sc2730_adc_scale_init(struct sc27xx_adc_data *data)
{ int i;
for (i = 0; i < SC27XX_ADC_CHANNEL_MAX; i++) { switch (i) { case 5: case 10: case 19: case 30: case 31:
data->channel_scale[i] = 3; break; case 7: case 9:
data->channel_scale[i] = 2; break; case 13:
data->channel_scale[i] = 1; break; default:
data->channel_scale[i] = 0; break;
}
}
}
staticvoid sc2731_adc_scale_init(struct sc27xx_adc_data *data)
{ int i; /* * In the current software design, SC2731 support 2 scales, * channels 5 uses big scale, others use smale.
*/ for (i = 0; i < SC27XX_ADC_CHANNEL_MAX; i++) { switch (i) { case 5:
data->channel_scale[i] = 1; break; default:
data->channel_scale[i] = 0; break;
}
}
}
staticint sc27xx_adc_read(struct sc27xx_adc_data *data, int channel, int scale, int *val)
{ int ret, ret_volref;
u32 tmp, value, status;
ret = hwspin_lock_timeout_raw(data->hwlock, SC27XX_ADC_HWLOCK_TIMEOUT); if (ret) {
dev_err(data->dev, "timeout to get the hwspinlock\n"); return ret;
}
/* * According to the sc2721 chip data sheet, the reference voltage of * specific channel 30 and channel 31 in ADC module needs to be set from * the default 2.8v to 3.5v.
*/ if ((data->var_data->set_volref) && (channel == 30 || channel == 31)) {
ret = regulator_set_voltage(data->volref,
SC27XX_ADC_REFVOL_VDD35,
SC27XX_ADC_REFVOL_VDD35); if (ret) {
dev_err(data->dev, "failed to set the volref 3.5v\n"); goto unlock_adc;
}
}
ret = regmap_set_bits(data->regmap, data->base + SC27XX_ADC_CTL,
SC27XX_ADC_EN); if (ret) goto regulator_restore;
ret = regmap_set_bits(data->regmap, data->base + SC27XX_ADC_INT_CLR,
SC27XX_ADC_IRQ_CLR); if (ret) goto disable_adc;
/* Configure the channel id and scale */
tmp = (scale << data->var_data->scale_shift) & data->var_data->scale_mask;
tmp |= channel & SC27XX_ADC_CHN_ID_MASK;
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CH_CFG,
SC27XX_ADC_CHN_ID_MASK |
data->var_data->scale_mask,
tmp); if (ret) goto disable_adc;
/* Select 12bit conversion mode, and only sample 1 time */
tmp = SC27XX_ADC_12BIT_MODE;
tmp |= (0 << SC27XX_ADC_RUN_NUM_SHIFT) & SC27XX_ADC_RUN_NUM_MASK;
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
SC27XX_ADC_RUN_NUM_MASK | SC27XX_ADC_12BIT_MODE,
tmp); if (ret) goto disable_adc;
ret = regmap_set_bits(data->regmap, data->base + SC27XX_ADC_CTL,
SC27XX_ADC_CHN_RUN); if (ret) goto disable_adc;
ret = regmap_read_poll_timeout(data->regmap,
data->base + SC27XX_ADC_INT_RAW,
status, (status & SC27XX_ADC_IRQ_RAW),
SC27XX_ADC_POLL_RAW_STATUS,
SC27XX_ADC_RDY_TIMEOUT); if (ret) {
dev_err(data->dev, "read adc timeout, status = 0x%x\n", status); goto disable_adc;
}
ret = regmap_read(data->regmap, data->base + SC27XX_ADC_DATA, &value); if (ret) goto disable_adc;
value &= SC27XX_ADC_DATA_MASK;
disable_adc:
regmap_clear_bits(data->regmap, data->base + SC27XX_ADC_CTL,
SC27XX_ADC_EN);
regulator_restore: if ((data->var_data->set_volref) && (channel == 30 || channel == 31)) {
ret_volref = regulator_set_voltage(data->volref,
SC27XX_ADC_REFVOL_VDD28,
SC27XX_ADC_REFVOL_VDD28); if (ret_volref) {
dev_err(data->dev, "failed to set the volref 2.8v,ret_volref = 0x%x\n",
ret_volref);
ret = ret || ret_volref;
}
}
unlock_adc:
hwspin_unlock_raw(data->hwlock);
if (!ret)
*val = value;
return ret;
}
staticvoid sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data, int channel, int scale, struct u32_fract *fract)
{
u32 ratio;
ratio = data->var_data->get_ratio(channel, scale);
fract->numerator = ratio >> SC27XX_RATIO_NUMERATOR_OFFSET;
fract->denominator = ratio & SC27XX_RATIO_DENOMINATOR_MASK;
}
staticint adc_to_volt(struct sc27xx_adc_linear_graph *graph, int raw_adc)
{ int tmp;
staticint sc27xx_adc_to_volt(struct sc27xx_adc_linear_graph *graph, int raw_adc)
{ int tmp;
tmp = adc_to_volt(graph, raw_adc);
return tmp < 0 ? 0 : tmp;
}
staticint sc27xx_adc_convert_volt(struct sc27xx_adc_data *data, int channel, int scale, int raw_adc)
{ struct u32_fract fract;
u32 volt;
/* * Convert ADC values to voltage values according to the linear graph, * and channel 5 and channel 1 has been calibrated, so we can just * return the voltage values calculated by the linear graph. But other * channels need be calculated to the real voltage values with the * voltage ratio.
*/ switch (channel) { case 5: return sc27xx_adc_to_volt(&big_scale_graph, raw_adc);
case 1: return sc27xx_adc_to_volt(&small_scale_graph, raw_adc);
default:
volt = sc27xx_adc_to_volt(&small_scale_graph, raw_adc); break;
}
staticint sc27xx_adc_enable(struct sc27xx_adc_data *data)
{ int ret;
ret = regmap_set_bits(data->regmap, data->var_data->module_en,
SC27XX_MODULE_ADC_EN); if (ret) return ret;
/* Enable ADC work clock and controller clock */
ret = regmap_set_bits(data->regmap, data->var_data->clk_en,
SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN); if (ret) goto disable_adc;
/* ADC channel scales' calibration from nvmem device */
ret = sc27xx_adc_scale_calibration(data, true); if (ret) goto disable_clk;
ret = sc27xx_adc_scale_calibration(data, false); if (ret) goto disable_clk;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.