mirror of
https://github.com/torvalds/linux.git
synced 2024-12-05 02:23:16 +00:00
6bce28cb49
Counter subsystem changes now sent separately. This has been a busy cycle, so lots here and a few more stragglers to come next week. Big new feature in this cycle is probably output buffer support. This has been in the works for a very long time so it's great to see Mihail pick up the challenge and build upon his predecessors work to finally bring this feature to mainline. New device support ------------------ * adi,adxl313 - New driver and dt bindings for this low power accelerometer. * adi,adxl355 - New driver and dt bindings for this accelerometer. - Later series adds buffer support. * asahi-kasei,ak8975 - Minor additions to driver to support ak09916 * aspeed,aspeed-adc - Substantial rework plus feature additions to add support for the ast2600 including a new dt bindings doc. * atmel,at91_sama5d2 - Rework and support introduced for the sama7g5 parts. * maxim,max31865 - New driver and bindings for this RTD temperature sensor chip. * nxp,imx8qxp - New driver and bindings for the ADC found on the i.MX 8QuadXPlus Soc. * senseair,sunrise - New driver and bindings for this family of carbon dioxide gas sensors. * sensiron,scd4x - New driver and bindings for this carbon dioxide gas sensor. New features ------------ * Output buffer support. Works in a similar fashion to input buffers, but in this case userspace pushes data into the kfifo which is then drained to the device when a trigger occurs. Support added to the ad5766 DAC driver. * Core, devm_iio_map_array_register() to avoid need for devm_add_action_or_reset() based cleanup in fully managed allocation drivers. * Core iio_push_to_buffers_with_ts_unaligned() function to safely handle a few drivers where it really hard to ensure the correct data alignment in an iio_push_to_buffers_with_timestamp() call. Note this uses a bounce buffer so should be avoided whenever possible. Used in the ti,adc108s102, invense,mpu3050 and adi,adis16400. This closes the last known set of drivers with alignment issues at this interface. * maxim,max1027 - Substantial rework to this driver main target of which was supporting use of other triggers than it's own EOC interrupt. - Transfer optimization. * nxp,fxls8962af - Threshold even support including using it as a wakeup source. Cleanups, minor fixes etc ------------------------- Chances of a common type to multiple drivers: * devm_ conversion and drop of .remove() callbacks in: - adi,ad5064 - adi,ad7291 - adi,ad7303 - adi,ad7746 - adi,ad9832 - adi,adis16080 - dialog,da9150-gpadc - intel,mrfld_adc - marvell,berlin2 - maxim,max1363 - maxim,max44000 - nuvoton,nau7802 - st_sensors (includes a lot of rework!) - ti,ads8344 - ti,lp8788 * devm_platform_ioremap_resource() used to reduce boilerplate - cirrus,ep93xx - rockchip,saradc - stm,stm32-dac * Use dev_err_probe() in more places to both not print on deferred probe and ensure a reason for the deferral is available for debug purposes. - adi,ad8801 - capella,cm36651 - linear,ltc1660 - maxim,ds4424 - maxim,max5821 - microchip,mcp4922 - nxp,lpc18xx - onnn,noa1305 - st,lsm9ds0 - st,st_sensors - st,stm32-dac - ti,afe4403 - ti,afe4404 - ti,dac7311 * Drop error returns in SPI and I2C remove() functions as they are ignored and long term plan is to change these all over to returning void. In some cases these patches just make it 'obvious' they always return 0 where it was the case before but not easy to tell. - adi,ad5380 - adi,ad5446 - adi,ad5686 - adi,ad5592r - bosch,bma400 - bosch,bmc150 - fsl,mma7455 - honeywell,hmc5843 - kionix,kxsd9 - maxim,max5487 - meas,ms5611 - ti,afe4403 Driver specific changes * adi,ad5770r - Bring driver inline with documented bindings. * adi,ad7746 - Trivial style fix * adi,ad7949 - Express some magic values as the underlying parts via new #defines. - Make it work with SPI controllers that don't support 14 or 16 bit messages - Support selection of voltage reference from dt including expanding the dt-bindings to cover this new functionality. * adi,ad799x - Implement selection of external reference voltage on AD7991, AD7995 and AD7999. - Add missing dt-bindings doc for devices supported by this driver. * adi,adislib - Move interrupt startup to better location in startup flow. - Handle devices that cannot mask/unmask the drdy pin and must instead mask at the interrupt controller. Applies to the adis16460 and adis16475 from which we then drop equivalent code. * adi,ltc2983 - Add support for optional reset pin. - Fail to probe if no channels specified in dt binding. * asahi-kasei,ak8975 - dt-binding additions of missing vid-supply regulator. * aspeed,aspeed-adc - Typo fix. * fsl,mma7660 - Mark acpi_device_id table __maybe_unused to avoid build warning. * fsl,imx25-gcq - Avoid initializing regulators that aren't used. * invensense,mpu3050 - Drop a dead protection against a clash with the old input driver. * invensense,mpu6050 - Rework code to not use strcpy() and hence avoid possibility of wrong sized buffers. Note this wasn't a bug, but the new code is a lot more readable. - Mark acpi_device_id table __maybe_unused to avoid build warning. * kionix,kxcjk1013 - dt-binding addition to note it supports interrupts. * marvell,berlin2-adc - Enable COMPILE_TEST building. * maxim,max1027 - Avoid returning success in an error path. * nxp,imx8qxp - Fix warning when runtime pm not enabled via __maybe_unused. * ricoh,rn5t618 - Use the new devm_iio_map_array_register() instead of open coding the same. * samsung,exynos_adc - Improve kconfig help text. * st,lsm6dsx - Move max_fifo_size into the fifo_ops structure where the other configuration parameters are found. * st,st_sensors: - Reorder to ensure we turn the power off after removing userspace interfaces. * senseair,sunrise - Add missing I2C dependency. * ti,twl6030 - Small code tidy up. -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAmFudVERHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0Foiepg//R5DRm1gMFGP/dl2P8u6KD2oXNmbqc3J0 +EPRTkyTyawQzc21wdxUh3oObgxmDS6P7Cr17z9viGse2PsUjza4Sptic3Zqi3Kv FzeXZaP0GY9nC+hAyz+dSkMKNw19mkCYYEIWhDwYFpXMiOV8QlT+6pFgOiffofeR GYMUoV2XeuxR6OH0zMOuYKTzjOCYC+VJTP4BfeUetpRZQyllgFOA+5KX4ZWTiJMV eGCaq3Hhn6IQa7ATgYUBYJyMkAXQDrKDaMxiaCIuh/k3bgjAkfhkV48mu2cHTsX9 v0q21JkgmBenwNP/OQ6mxmWR9SFY4eB22Ptkd1foTPjAsqoST7qmqEBhhLrRb/fB HkL0I7M4tWDqSTJKgdn+UHYrKFW9oBHIt5SK1tCS+zTYIn1wEmkY/w/9MuZllBFO VKu5q2ypf/yGgCqPIZilK69mrZd7/72rE6tr3oefUF2iUVLdh9fI1TjGGyoT6GZ5 ZlEIfGHRpcRGHFiA4e+6tTx9VLr8m1B5YVvBWWPlruYe8ep7MmD3KjUcwINiRyNs 5rdBl2RI8VAUBbQ+60VWn03e/0kuRESpFC3Gf/ng+6hRSUbRFe4lZw60GbyFDXwV m1odTlkXA7l6oRG9f5COhE/oNO7dsfMO74W6+yzhWICnxjgJFosaaZTLw8F9QJdS m3z9tSU84js= =htmG -----END PGP SIGNATURE----- Merge tag 'iio-for-5.16a-split-take4' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into char-misc-next Jonathan writes: First set of IIO new device and feature support for the 5.16 cycle Counter subsystem changes now sent separately. This has been a busy cycle, so lots here and a few more stragglers to come next week. Big new feature in this cycle is probably output buffer support. This has been in the works for a very long time so it's great to see Mihail pick up the challenge and build upon his predecessors work to finally bring this feature to mainline. New device support ------------------ * adi,adxl313 - New driver and dt bindings for this low power accelerometer. * adi,adxl355 - New driver and dt bindings for this accelerometer. - Later series adds buffer support. * asahi-kasei,ak8975 - Minor additions to driver to support ak09916 * aspeed,aspeed-adc - Substantial rework plus feature additions to add support for the ast2600 including a new dt bindings doc. * atmel,at91_sama5d2 - Rework and support introduced for the sama7g5 parts. * maxim,max31865 - New driver and bindings for this RTD temperature sensor chip. * nxp,imx8qxp - New driver and bindings for the ADC found on the i.MX 8QuadXPlus Soc. * senseair,sunrise - New driver and bindings for this family of carbon dioxide gas sensors. * sensiron,scd4x - New driver and bindings for this carbon dioxide gas sensor. New features ------------ * Output buffer support. Works in a similar fashion to input buffers, but in this case userspace pushes data into the kfifo which is then drained to the device when a trigger occurs. Support added to the ad5766 DAC driver. * Core, devm_iio_map_array_register() to avoid need for devm_add_action_or_reset() based cleanup in fully managed allocation drivers. * Core iio_push_to_buffers_with_ts_unaligned() function to safely handle a few drivers where it really hard to ensure the correct data alignment in an iio_push_to_buffers_with_timestamp() call. Note this uses a bounce buffer so should be avoided whenever possible. Used in the ti,adc108s102, invense,mpu3050 and adi,adis16400. This closes the last known set of drivers with alignment issues at this interface. * maxim,max1027 - Substantial rework to this driver main target of which was supporting use of other triggers than it's own EOC interrupt. - Transfer optimization. * nxp,fxls8962af - Threshold even support including using it as a wakeup source. Cleanups, minor fixes etc ------------------------- Chances of a common type to multiple drivers: * devm_ conversion and drop of .remove() callbacks in: - adi,ad5064 - adi,ad7291 - adi,ad7303 - adi,ad7746 - adi,ad9832 - adi,adis16080 - dialog,da9150-gpadc - intel,mrfld_adc - marvell,berlin2 - maxim,max1363 - maxim,max44000 - nuvoton,nau7802 - st_sensors (includes a lot of rework!) - ti,ads8344 - ti,lp8788 * devm_platform_ioremap_resource() used to reduce boilerplate - cirrus,ep93xx - rockchip,saradc - stm,stm32-dac * Use dev_err_probe() in more places to both not print on deferred probe and ensure a reason for the deferral is available for debug purposes. - adi,ad8801 - capella,cm36651 - linear,ltc1660 - maxim,ds4424 - maxim,max5821 - microchip,mcp4922 - nxp,lpc18xx - onnn,noa1305 - st,lsm9ds0 - st,st_sensors - st,stm32-dac - ti,afe4403 - ti,afe4404 - ti,dac7311 * Drop error returns in SPI and I2C remove() functions as they are ignored and long term plan is to change these all over to returning void. In some cases these patches just make it 'obvious' they always return 0 where it was the case before but not easy to tell. - adi,ad5380 - adi,ad5446 - adi,ad5686 - adi,ad5592r - bosch,bma400 - bosch,bmc150 - fsl,mma7455 - honeywell,hmc5843 - kionix,kxsd9 - maxim,max5487 - meas,ms5611 - ti,afe4403 Driver specific changes * adi,ad5770r - Bring driver inline with documented bindings. * adi,ad7746 - Trivial style fix * adi,ad7949 - Express some magic values as the underlying parts via new #defines. - Make it work with SPI controllers that don't support 14 or 16 bit messages - Support selection of voltage reference from dt including expanding the dt-bindings to cover this new functionality. * adi,ad799x - Implement selection of external reference voltage on AD7991, AD7995 and AD7999. - Add missing dt-bindings doc for devices supported by this driver. * adi,adislib - Move interrupt startup to better location in startup flow. - Handle devices that cannot mask/unmask the drdy pin and must instead mask at the interrupt controller. Applies to the adis16460 and adis16475 from which we then drop equivalent code. * adi,ltc2983 - Add support for optional reset pin. - Fail to probe if no channels specified in dt binding. * asahi-kasei,ak8975 - dt-binding additions of missing vid-supply regulator. * aspeed,aspeed-adc - Typo fix. * fsl,mma7660 - Mark acpi_device_id table __maybe_unused to avoid build warning. * fsl,imx25-gcq - Avoid initializing regulators that aren't used. * invensense,mpu3050 - Drop a dead protection against a clash with the old input driver. * invensense,mpu6050 - Rework code to not use strcpy() and hence avoid possibility of wrong sized buffers. Note this wasn't a bug, but the new code is a lot more readable. - Mark acpi_device_id table __maybe_unused to avoid build warning. * kionix,kxcjk1013 - dt-binding addition to note it supports interrupts. * marvell,berlin2-adc - Enable COMPILE_TEST building. * maxim,max1027 - Avoid returning success in an error path. * nxp,imx8qxp - Fix warning when runtime pm not enabled via __maybe_unused. * ricoh,rn5t618 - Use the new devm_iio_map_array_register() instead of open coding the same. * samsung,exynos_adc - Improve kconfig help text. * st,lsm6dsx - Move max_fifo_size into the fifo_ops structure where the other configuration parameters are found. * st,st_sensors: - Reorder to ensure we turn the power off after removing userspace interfaces. * senseair,sunrise - Add missing I2C dependency. * ti,twl6030 - Small code tidy up. * tag 'iio-for-5.16a-split-take4' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (148 commits) iio: imx8qxp-adc: mark PM functions as __maybe_unused iio: pressure: ms5611: Make ms5611_remove() return void iio: potentiometer: max5487: Don't return an error in .remove() iio: magn: hmc5843: Make hmc5843_common_remove() return void iio: health: afe4403: Don't return an error in .remove() iio: dac: ad5686: Make ad5686_remove() return void iio: dac: ad5592r: Make ad5592r_remove() return void iio: dac: ad5446: Make ad5446_remove() return void iio: dac: ad5380: Make ad5380_remove() return void iio: accel: mma7455: Make mma7455_core_remove() return void iio: accel: kxsd9: Make kxsd9_common_remove() return void iio: accel: bmi088: Make bmi088_accel_core_remove() return void iio: accel: bmc150: Make bmc150_accel_core_remove() return void iio: accel: bma400: Make bma400_remove() return void drivers:iio:dac:ad5766.c: Add trigger buffer iio: triggered-buffer: extend support to configure output buffers iio: kfifo-buffer: Add output buffer support iio: Add output buffer support iio: documentation: Document scd4x calibration use drivers: iio: chemical: Add support for Sensirion SCD4x CO2 sensor ...
727 lines
21 KiB
C
727 lines
21 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Aspeed AST2400/2500/2600 ADC
|
|
*
|
|
* Copyright (C) 2017 Google, Inc.
|
|
* Copyright (C) 2021 Aspeed Technology Inc.
|
|
*
|
|
* ADC clock formula:
|
|
* Ast2400/Ast2500:
|
|
* clock period = period of PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1)
|
|
* Ast2600:
|
|
* clock period = period of PCLK * 2 * (ADC0C[15:0] + 1)
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/err.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/io.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/reset.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/types.h>
|
|
#include <linux/bitfield.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/mfd/syscon.h>
|
|
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/iio/driver.h>
|
|
#include <linux/iopoll.h>
|
|
|
|
#define ASPEED_RESOLUTION_BITS 10
|
|
#define ASPEED_CLOCKS_PER_SAMPLE 12
|
|
|
|
#define ASPEED_REG_ENGINE_CONTROL 0x00
|
|
#define ASPEED_REG_INTERRUPT_CONTROL 0x04
|
|
#define ASPEED_REG_VGA_DETECT_CONTROL 0x08
|
|
#define ASPEED_REG_CLOCK_CONTROL 0x0C
|
|
#define ASPEED_REG_COMPENSATION_TRIM 0xC4
|
|
/*
|
|
* The register offset between 0xC8~0xCC can be read and won't affect the
|
|
* hardware logic in each version of ADC.
|
|
*/
|
|
#define ASPEED_REG_MAX 0xD0
|
|
|
|
#define ASPEED_ADC_ENGINE_ENABLE BIT(0)
|
|
#define ASPEED_ADC_OP_MODE GENMASK(3, 1)
|
|
#define ASPEED_ADC_OP_MODE_PWR_DOWN 0
|
|
#define ASPEED_ADC_OP_MODE_STANDBY 1
|
|
#define ASPEED_ADC_OP_MODE_NORMAL 7
|
|
#define ASPEED_ADC_CTRL_COMPENSATION BIT(4)
|
|
#define ASPEED_ADC_AUTO_COMPENSATION BIT(5)
|
|
/*
|
|
* Bit 6 determines not only the reference voltage range but also the dividing
|
|
* circuit for battery sensing.
|
|
*/
|
|
#define ASPEED_ADC_REF_VOLTAGE GENMASK(7, 6)
|
|
#define ASPEED_ADC_REF_VOLTAGE_2500mV 0
|
|
#define ASPEED_ADC_REF_VOLTAGE_1200mV 1
|
|
#define ASPEED_ADC_REF_VOLTAGE_EXT_HIGH 2
|
|
#define ASPEED_ADC_REF_VOLTAGE_EXT_LOW 3
|
|
#define ASPEED_ADC_BAT_SENSING_DIV BIT(6)
|
|
#define ASPEED_ADC_BAT_SENSING_DIV_2_3 0
|
|
#define ASPEED_ADC_BAT_SENSING_DIV_1_3 1
|
|
#define ASPEED_ADC_CTRL_INIT_RDY BIT(8)
|
|
#define ASPEED_ADC_CH7_MODE BIT(12)
|
|
#define ASPEED_ADC_CH7_NORMAL 0
|
|
#define ASPEED_ADC_CH7_BAT 1
|
|
#define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13)
|
|
#define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16)
|
|
#define ASPEED_ADC_CTRL_CHANNEL_ENABLE(ch) FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, BIT(ch))
|
|
|
|
#define ASPEED_ADC_INIT_POLLING_TIME 500
|
|
#define ASPEED_ADC_INIT_TIMEOUT 500000
|
|
/*
|
|
* When the sampling rate is too high, the ADC may not have enough charging
|
|
* time, resulting in a low voltage value. Thus, the default uses a slow
|
|
* sampling rate for most use cases.
|
|
*/
|
|
#define ASPEED_ADC_DEF_SAMPLING_RATE 65000
|
|
|
|
struct aspeed_adc_trim_locate {
|
|
const unsigned int offset;
|
|
const unsigned int field;
|
|
};
|
|
|
|
struct aspeed_adc_model_data {
|
|
const char *model_name;
|
|
unsigned int min_sampling_rate; // Hz
|
|
unsigned int max_sampling_rate; // Hz
|
|
unsigned int vref_fixed_mv;
|
|
bool wait_init_sequence;
|
|
bool need_prescaler;
|
|
bool bat_sense_sup;
|
|
u8 scaler_bit_width;
|
|
unsigned int num_channels;
|
|
const struct aspeed_adc_trim_locate *trim_locate;
|
|
};
|
|
|
|
struct adc_gain {
|
|
u8 mult;
|
|
u8 div;
|
|
};
|
|
|
|
struct aspeed_adc_data {
|
|
struct device *dev;
|
|
const struct aspeed_adc_model_data *model_data;
|
|
struct regulator *regulator;
|
|
void __iomem *base;
|
|
spinlock_t clk_lock;
|
|
struct clk_hw *fixed_div_clk;
|
|
struct clk_hw *clk_prescaler;
|
|
struct clk_hw *clk_scaler;
|
|
struct reset_control *rst;
|
|
int vref_mv;
|
|
u32 sample_period_ns;
|
|
int cv;
|
|
bool battery_sensing;
|
|
struct adc_gain battery_mode_gain;
|
|
};
|
|
|
|
#define ASPEED_CHAN(_idx, _data_reg_addr) { \
|
|
.type = IIO_VOLTAGE, \
|
|
.indexed = 1, \
|
|
.channel = (_idx), \
|
|
.address = (_data_reg_addr), \
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
|
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
|
BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
|
|
BIT(IIO_CHAN_INFO_OFFSET), \
|
|
}
|
|
|
|
static const struct iio_chan_spec aspeed_adc_iio_channels[] = {
|
|
ASPEED_CHAN(0, 0x10),
|
|
ASPEED_CHAN(1, 0x12),
|
|
ASPEED_CHAN(2, 0x14),
|
|
ASPEED_CHAN(3, 0x16),
|
|
ASPEED_CHAN(4, 0x18),
|
|
ASPEED_CHAN(5, 0x1A),
|
|
ASPEED_CHAN(6, 0x1C),
|
|
ASPEED_CHAN(7, 0x1E),
|
|
ASPEED_CHAN(8, 0x20),
|
|
ASPEED_CHAN(9, 0x22),
|
|
ASPEED_CHAN(10, 0x24),
|
|
ASPEED_CHAN(11, 0x26),
|
|
ASPEED_CHAN(12, 0x28),
|
|
ASPEED_CHAN(13, 0x2A),
|
|
ASPEED_CHAN(14, 0x2C),
|
|
ASPEED_CHAN(15, 0x2E),
|
|
};
|
|
|
|
#define ASPEED_BAT_CHAN(_idx, _data_reg_addr) { \
|
|
.type = IIO_VOLTAGE, \
|
|
.indexed = 1, \
|
|
.channel = (_idx), \
|
|
.address = (_data_reg_addr), \
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
|
BIT(IIO_CHAN_INFO_OFFSET), \
|
|
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
|
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
|
}
|
|
static const struct iio_chan_spec aspeed_adc_iio_bat_channels[] = {
|
|
ASPEED_CHAN(0, 0x10),
|
|
ASPEED_CHAN(1, 0x12),
|
|
ASPEED_CHAN(2, 0x14),
|
|
ASPEED_CHAN(3, 0x16),
|
|
ASPEED_CHAN(4, 0x18),
|
|
ASPEED_CHAN(5, 0x1A),
|
|
ASPEED_CHAN(6, 0x1C),
|
|
ASPEED_BAT_CHAN(7, 0x1E),
|
|
};
|
|
|
|
static int aspeed_adc_set_trim_data(struct iio_dev *indio_dev)
|
|
{
|
|
struct device_node *syscon;
|
|
struct regmap *scu;
|
|
u32 scu_otp, trimming_val;
|
|
struct aspeed_adc_data *data = iio_priv(indio_dev);
|
|
|
|
syscon = of_find_node_by_name(NULL, "syscon");
|
|
if (syscon == NULL) {
|
|
dev_warn(data->dev, "Couldn't find syscon node\n");
|
|
return -EOPNOTSUPP;
|
|
}
|
|
scu = syscon_node_to_regmap(syscon);
|
|
if (IS_ERR(scu)) {
|
|
dev_warn(data->dev, "Failed to get syscon regmap\n");
|
|
return -EOPNOTSUPP;
|
|
}
|
|
if (data->model_data->trim_locate) {
|
|
if (regmap_read(scu, data->model_data->trim_locate->offset,
|
|
&scu_otp)) {
|
|
dev_warn(data->dev,
|
|
"Failed to get adc trimming data\n");
|
|
trimming_val = 0x8;
|
|
} else {
|
|
trimming_val =
|
|
((scu_otp) &
|
|
(data->model_data->trim_locate->field)) >>
|
|
__ffs(data->model_data->trim_locate->field);
|
|
}
|
|
dev_dbg(data->dev,
|
|
"trimming val = %d, offset = %08x, fields = %08x\n",
|
|
trimming_val, data->model_data->trim_locate->offset,
|
|
data->model_data->trim_locate->field);
|
|
writel(trimming_val, data->base + ASPEED_REG_COMPENSATION_TRIM);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int aspeed_adc_compensation(struct iio_dev *indio_dev)
|
|
{
|
|
struct aspeed_adc_data *data = iio_priv(indio_dev);
|
|
u32 index, adc_raw = 0;
|
|
u32 adc_engine_control_reg_val;
|
|
|
|
adc_engine_control_reg_val =
|
|
readl(data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
adc_engine_control_reg_val &= ~ASPEED_ADC_OP_MODE;
|
|
adc_engine_control_reg_val |=
|
|
(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) |
|
|
ASPEED_ADC_ENGINE_ENABLE);
|
|
/*
|
|
* Enable compensating sensing:
|
|
* After that, the input voltage of ADC will force to half of the reference
|
|
* voltage. So the expected reading raw data will become half of the max
|
|
* value. We can get compensating value = 0x200 - ADC read raw value.
|
|
* It is recommended to average at least 10 samples to get a final CV.
|
|
*/
|
|
writel(adc_engine_control_reg_val | ASPEED_ADC_CTRL_COMPENSATION |
|
|
ASPEED_ADC_CTRL_CHANNEL_ENABLE(0),
|
|
data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
/*
|
|
* After enable compensating sensing mode need to wait some time for ADC stable
|
|
* Experiment result is 1ms.
|
|
*/
|
|
mdelay(1);
|
|
|
|
for (index = 0; index < 16; index++) {
|
|
/*
|
|
* Waiting for the sampling period ensures that the value acquired
|
|
* is fresh each time.
|
|
*/
|
|
ndelay(data->sample_period_ns);
|
|
adc_raw += readw(data->base + aspeed_adc_iio_channels[0].address);
|
|
}
|
|
adc_raw >>= 4;
|
|
data->cv = BIT(ASPEED_RESOLUTION_BITS - 1) - adc_raw;
|
|
writel(adc_engine_control_reg_val,
|
|
data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
dev_dbg(data->dev, "Compensating value = %d\n", data->cv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aspeed_adc_set_sampling_rate(struct iio_dev *indio_dev, u32 rate)
|
|
{
|
|
struct aspeed_adc_data *data = iio_priv(indio_dev);
|
|
|
|
if (rate < data->model_data->min_sampling_rate ||
|
|
rate > data->model_data->max_sampling_rate)
|
|
return -EINVAL;
|
|
/* Each sampling needs 12 clocks to convert.*/
|
|
clk_set_rate(data->clk_scaler->clk, rate * ASPEED_CLOCKS_PER_SAMPLE);
|
|
rate = clk_get_rate(data->clk_scaler->clk);
|
|
data->sample_period_ns = DIV_ROUND_UP_ULL(
|
|
(u64)NSEC_PER_SEC * ASPEED_CLOCKS_PER_SAMPLE, rate);
|
|
dev_dbg(data->dev, "Adc clock = %d sample period = %d ns", rate,
|
|
data->sample_period_ns);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aspeed_adc_read_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
int *val, int *val2, long mask)
|
|
{
|
|
struct aspeed_adc_data *data = iio_priv(indio_dev);
|
|
u32 adc_engine_control_reg_val;
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_RAW:
|
|
if (data->battery_sensing && chan->channel == 7) {
|
|
adc_engine_control_reg_val =
|
|
readl(data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
writel(adc_engine_control_reg_val |
|
|
FIELD_PREP(ASPEED_ADC_CH7_MODE,
|
|
ASPEED_ADC_CH7_BAT) |
|
|
ASPEED_ADC_BAT_SENSING_ENABLE,
|
|
data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
/*
|
|
* After enable battery sensing mode need to wait some time for adc stable
|
|
* Experiment result is 1ms.
|
|
*/
|
|
mdelay(1);
|
|
*val = readw(data->base + chan->address);
|
|
*val = (*val * data->battery_mode_gain.mult) /
|
|
data->battery_mode_gain.div;
|
|
/* Restore control register value */
|
|
writel(adc_engine_control_reg_val,
|
|
data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
} else
|
|
*val = readw(data->base + chan->address);
|
|
return IIO_VAL_INT;
|
|
|
|
case IIO_CHAN_INFO_OFFSET:
|
|
if (data->battery_sensing && chan->channel == 7)
|
|
*val = (data->cv * data->battery_mode_gain.mult) /
|
|
data->battery_mode_gain.div;
|
|
else
|
|
*val = data->cv;
|
|
return IIO_VAL_INT;
|
|
|
|
case IIO_CHAN_INFO_SCALE:
|
|
*val = data->vref_mv;
|
|
*val2 = ASPEED_RESOLUTION_BITS;
|
|
return IIO_VAL_FRACTIONAL_LOG2;
|
|
|
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
|
*val = clk_get_rate(data->clk_scaler->clk) /
|
|
ASPEED_CLOCKS_PER_SAMPLE;
|
|
return IIO_VAL_INT;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int aspeed_adc_write_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
int val, int val2, long mask)
|
|
{
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
|
return aspeed_adc_set_sampling_rate(indio_dev, val);
|
|
|
|
case IIO_CHAN_INFO_SCALE:
|
|
case IIO_CHAN_INFO_RAW:
|
|
/*
|
|
* Technically, these could be written but the only reasons
|
|
* for doing so seem better handled in userspace. EPERM is
|
|
* returned to signal this is a policy choice rather than a
|
|
* hardware limitation.
|
|
*/
|
|
return -EPERM;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int aspeed_adc_reg_access(struct iio_dev *indio_dev,
|
|
unsigned int reg, unsigned int writeval,
|
|
unsigned int *readval)
|
|
{
|
|
struct aspeed_adc_data *data = iio_priv(indio_dev);
|
|
|
|
if (!readval || reg % 4 || reg > ASPEED_REG_MAX)
|
|
return -EINVAL;
|
|
|
|
*readval = readl(data->base + reg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct iio_info aspeed_adc_iio_info = {
|
|
.read_raw = aspeed_adc_read_raw,
|
|
.write_raw = aspeed_adc_write_raw,
|
|
.debugfs_reg_access = aspeed_adc_reg_access,
|
|
};
|
|
|
|
static void aspeed_adc_unregister_fixed_divider(void *data)
|
|
{
|
|
struct clk_hw *clk = data;
|
|
|
|
clk_hw_unregister_fixed_factor(clk);
|
|
}
|
|
|
|
static void aspeed_adc_reset_assert(void *data)
|
|
{
|
|
struct reset_control *rst = data;
|
|
|
|
reset_control_assert(rst);
|
|
}
|
|
|
|
static void aspeed_adc_clk_disable_unprepare(void *data)
|
|
{
|
|
struct clk *clk = data;
|
|
|
|
clk_disable_unprepare(clk);
|
|
}
|
|
|
|
static void aspeed_adc_power_down(void *data)
|
|
{
|
|
struct aspeed_adc_data *priv_data = data;
|
|
|
|
writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN),
|
|
priv_data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
}
|
|
|
|
static void aspeed_adc_reg_disable(void *data)
|
|
{
|
|
struct regulator *reg = data;
|
|
|
|
regulator_disable(reg);
|
|
}
|
|
|
|
static int aspeed_adc_vref_config(struct iio_dev *indio_dev)
|
|
{
|
|
struct aspeed_adc_data *data = iio_priv(indio_dev);
|
|
int ret;
|
|
u32 adc_engine_control_reg_val;
|
|
|
|
if (data->model_data->vref_fixed_mv) {
|
|
data->vref_mv = data->model_data->vref_fixed_mv;
|
|
return 0;
|
|
}
|
|
adc_engine_control_reg_val =
|
|
readl(data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
data->regulator = devm_regulator_get_optional(data->dev, "vref");
|
|
if (!IS_ERR(data->regulator)) {
|
|
ret = regulator_enable(data->regulator);
|
|
if (ret)
|
|
return ret;
|
|
ret = devm_add_action_or_reset(
|
|
data->dev, aspeed_adc_reg_disable, data->regulator);
|
|
if (ret)
|
|
return ret;
|
|
data->vref_mv = regulator_get_voltage(data->regulator);
|
|
/* Conversion from uV to mV */
|
|
data->vref_mv /= 1000;
|
|
if ((data->vref_mv >= 1550) && (data->vref_mv <= 2700))
|
|
writel(adc_engine_control_reg_val |
|
|
FIELD_PREP(
|
|
ASPEED_ADC_REF_VOLTAGE,
|
|
ASPEED_ADC_REF_VOLTAGE_EXT_HIGH),
|
|
data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
else if ((data->vref_mv >= 900) && (data->vref_mv <= 1650))
|
|
writel(adc_engine_control_reg_val |
|
|
FIELD_PREP(
|
|
ASPEED_ADC_REF_VOLTAGE,
|
|
ASPEED_ADC_REF_VOLTAGE_EXT_LOW),
|
|
data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
else {
|
|
dev_err(data->dev, "Regulator voltage %d not support",
|
|
data->vref_mv);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
} else {
|
|
if (PTR_ERR(data->regulator) != -ENODEV)
|
|
return PTR_ERR(data->regulator);
|
|
data->vref_mv = 2500000;
|
|
of_property_read_u32(data->dev->of_node,
|
|
"aspeed,int-vref-microvolt",
|
|
&data->vref_mv);
|
|
/* Conversion from uV to mV */
|
|
data->vref_mv /= 1000;
|
|
if (data->vref_mv == 2500)
|
|
writel(adc_engine_control_reg_val |
|
|
FIELD_PREP(ASPEED_ADC_REF_VOLTAGE,
|
|
ASPEED_ADC_REF_VOLTAGE_2500mV),
|
|
data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
else if (data->vref_mv == 1200)
|
|
writel(adc_engine_control_reg_val |
|
|
FIELD_PREP(ASPEED_ADC_REF_VOLTAGE,
|
|
ASPEED_ADC_REF_VOLTAGE_1200mV),
|
|
data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
else {
|
|
dev_err(data->dev, "Voltage %d not support", data->vref_mv);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aspeed_adc_probe(struct platform_device *pdev)
|
|
{
|
|
struct iio_dev *indio_dev;
|
|
struct aspeed_adc_data *data;
|
|
int ret;
|
|
u32 adc_engine_control_reg_val;
|
|
unsigned long scaler_flags = 0;
|
|
char clk_name[32], clk_parent_name[32];
|
|
|
|
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
|
|
if (!indio_dev)
|
|
return -ENOMEM;
|
|
|
|
data = iio_priv(indio_dev);
|
|
data->dev = &pdev->dev;
|
|
data->model_data = of_device_get_match_data(&pdev->dev);
|
|
platform_set_drvdata(pdev, indio_dev);
|
|
|
|
data->base = devm_platform_ioremap_resource(pdev, 0);
|
|
if (IS_ERR(data->base))
|
|
return PTR_ERR(data->base);
|
|
|
|
/* Register ADC clock prescaler with source specified by device tree. */
|
|
spin_lock_init(&data->clk_lock);
|
|
snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), "%s",
|
|
of_clk_get_parent_name(pdev->dev.of_node, 0));
|
|
snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-fixed-div",
|
|
data->model_data->model_name);
|
|
data->fixed_div_clk = clk_hw_register_fixed_factor(
|
|
&pdev->dev, clk_name, clk_parent_name, 0, 1, 2);
|
|
if (IS_ERR(data->fixed_div_clk))
|
|
return PTR_ERR(data->fixed_div_clk);
|
|
|
|
ret = devm_add_action_or_reset(data->dev,
|
|
aspeed_adc_unregister_fixed_divider,
|
|
data->fixed_div_clk);
|
|
if (ret)
|
|
return ret;
|
|
snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), clk_name);
|
|
|
|
if (data->model_data->need_prescaler) {
|
|
snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler",
|
|
data->model_data->model_name);
|
|
data->clk_prescaler = devm_clk_hw_register_divider(
|
|
&pdev->dev, clk_name, clk_parent_name, 0,
|
|
data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0,
|
|
&data->clk_lock);
|
|
if (IS_ERR(data->clk_prescaler))
|
|
return PTR_ERR(data->clk_prescaler);
|
|
snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name),
|
|
clk_name);
|
|
scaler_flags = CLK_SET_RATE_PARENT;
|
|
}
|
|
/*
|
|
* Register ADC clock scaler downstream from the prescaler. Allow rate
|
|
* setting to adjust the prescaler as well.
|
|
*/
|
|
snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-scaler",
|
|
data->model_data->model_name);
|
|
data->clk_scaler = devm_clk_hw_register_divider(
|
|
&pdev->dev, clk_name, clk_parent_name, scaler_flags,
|
|
data->base + ASPEED_REG_CLOCK_CONTROL, 0,
|
|
data->model_data->scaler_bit_width, 0, &data->clk_lock);
|
|
if (IS_ERR(data->clk_scaler))
|
|
return PTR_ERR(data->clk_scaler);
|
|
|
|
data->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
|
|
if (IS_ERR(data->rst)) {
|
|
dev_err(&pdev->dev,
|
|
"invalid or missing reset controller device tree entry");
|
|
return PTR_ERR(data->rst);
|
|
}
|
|
reset_control_deassert(data->rst);
|
|
|
|
ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert,
|
|
data->rst);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = aspeed_adc_vref_config(indio_dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (of_find_property(data->dev->of_node, "aspeed,trim-data-valid",
|
|
NULL)) {
|
|
ret = aspeed_adc_set_trim_data(indio_dev);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (of_find_property(data->dev->of_node, "aspeed,battery-sensing",
|
|
NULL)) {
|
|
if (data->model_data->bat_sense_sup) {
|
|
data->battery_sensing = 1;
|
|
if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) &
|
|
ASPEED_ADC_BAT_SENSING_DIV) {
|
|
data->battery_mode_gain.mult = 3;
|
|
data->battery_mode_gain.div = 1;
|
|
} else {
|
|
data->battery_mode_gain.mult = 3;
|
|
data->battery_mode_gain.div = 2;
|
|
}
|
|
} else
|
|
dev_warn(&pdev->dev,
|
|
"Failed to enable battery-sensing mode\n");
|
|
}
|
|
|
|
ret = clk_prepare_enable(data->clk_scaler->clk);
|
|
if (ret)
|
|
return ret;
|
|
ret = devm_add_action_or_reset(data->dev,
|
|
aspeed_adc_clk_disable_unprepare,
|
|
data->clk_scaler->clk);
|
|
if (ret)
|
|
return ret;
|
|
ret = aspeed_adc_set_sampling_rate(indio_dev,
|
|
ASPEED_ADC_DEF_SAMPLING_RATE);
|
|
if (ret)
|
|
return ret;
|
|
|
|
adc_engine_control_reg_val =
|
|
readl(data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
adc_engine_control_reg_val |=
|
|
FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) |
|
|
ASPEED_ADC_ENGINE_ENABLE;
|
|
/* Enable engine in normal mode. */
|
|
writel(adc_engine_control_reg_val,
|
|
data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
|
|
ret = devm_add_action_or_reset(data->dev, aspeed_adc_power_down,
|
|
data);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (data->model_data->wait_init_sequence) {
|
|
/* Wait for initial sequence complete. */
|
|
ret = readl_poll_timeout(data->base + ASPEED_REG_ENGINE_CONTROL,
|
|
adc_engine_control_reg_val,
|
|
adc_engine_control_reg_val &
|
|
ASPEED_ADC_CTRL_INIT_RDY,
|
|
ASPEED_ADC_INIT_POLLING_TIME,
|
|
ASPEED_ADC_INIT_TIMEOUT);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
aspeed_adc_compensation(indio_dev);
|
|
/* Start all channels in normal mode. */
|
|
adc_engine_control_reg_val =
|
|
readl(data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
adc_engine_control_reg_val |= ASPEED_ADC_CTRL_CHANNEL;
|
|
writel(adc_engine_control_reg_val,
|
|
data->base + ASPEED_REG_ENGINE_CONTROL);
|
|
|
|
indio_dev->name = data->model_data->model_name;
|
|
indio_dev->info = &aspeed_adc_iio_info;
|
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
|
indio_dev->channels = data->battery_sensing ?
|
|
aspeed_adc_iio_bat_channels :
|
|
aspeed_adc_iio_channels;
|
|
indio_dev->num_channels = data->model_data->num_channels;
|
|
|
|
ret = devm_iio_device_register(data->dev, indio_dev);
|
|
return ret;
|
|
}
|
|
|
|
static const struct aspeed_adc_trim_locate ast2500_adc_trim = {
|
|
.offset = 0x154,
|
|
.field = GENMASK(31, 28),
|
|
};
|
|
|
|
static const struct aspeed_adc_trim_locate ast2600_adc0_trim = {
|
|
.offset = 0x5d0,
|
|
.field = GENMASK(3, 0),
|
|
};
|
|
|
|
static const struct aspeed_adc_trim_locate ast2600_adc1_trim = {
|
|
.offset = 0x5d0,
|
|
.field = GENMASK(7, 4),
|
|
};
|
|
|
|
static const struct aspeed_adc_model_data ast2400_model_data = {
|
|
.model_name = "ast2400-adc",
|
|
.vref_fixed_mv = 2500,
|
|
.min_sampling_rate = 10000,
|
|
.max_sampling_rate = 500000,
|
|
.need_prescaler = true,
|
|
.scaler_bit_width = 10,
|
|
.num_channels = 16,
|
|
};
|
|
|
|
static const struct aspeed_adc_model_data ast2500_model_data = {
|
|
.model_name = "ast2500-adc",
|
|
.vref_fixed_mv = 1800,
|
|
.min_sampling_rate = 1,
|
|
.max_sampling_rate = 1000000,
|
|
.wait_init_sequence = true,
|
|
.need_prescaler = true,
|
|
.scaler_bit_width = 10,
|
|
.num_channels = 16,
|
|
.trim_locate = &ast2500_adc_trim,
|
|
};
|
|
|
|
static const struct aspeed_adc_model_data ast2600_adc0_model_data = {
|
|
.model_name = "ast2600-adc0",
|
|
.min_sampling_rate = 10000,
|
|
.max_sampling_rate = 500000,
|
|
.wait_init_sequence = true,
|
|
.bat_sense_sup = true,
|
|
.scaler_bit_width = 16,
|
|
.num_channels = 8,
|
|
.trim_locate = &ast2600_adc0_trim,
|
|
};
|
|
|
|
static const struct aspeed_adc_model_data ast2600_adc1_model_data = {
|
|
.model_name = "ast2600-adc1",
|
|
.min_sampling_rate = 10000,
|
|
.max_sampling_rate = 500000,
|
|
.wait_init_sequence = true,
|
|
.bat_sense_sup = true,
|
|
.scaler_bit_width = 16,
|
|
.num_channels = 8,
|
|
.trim_locate = &ast2600_adc1_trim,
|
|
};
|
|
|
|
static const struct of_device_id aspeed_adc_matches[] = {
|
|
{ .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data },
|
|
{ .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data },
|
|
{ .compatible = "aspeed,ast2600-adc0", .data = &ast2600_adc0_model_data },
|
|
{ .compatible = "aspeed,ast2600-adc1", .data = &ast2600_adc1_model_data },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, aspeed_adc_matches);
|
|
|
|
static struct platform_driver aspeed_adc_driver = {
|
|
.probe = aspeed_adc_probe,
|
|
.driver = {
|
|
.name = KBUILD_MODNAME,
|
|
.of_match_table = aspeed_adc_matches,
|
|
}
|
|
};
|
|
|
|
module_platform_driver(aspeed_adc_driver);
|
|
|
|
MODULE_AUTHOR("Rick Altherr <raltherr@google.com>");
|
|
MODULE_DESCRIPTION("Aspeed AST2400/2500/2600 ADC Driver");
|
|
MODULE_LICENSE("GPL");
|