brintos

brintos / linux-shallow public Read only

0
0
Text · 14.7 KiB · 828d3fe Raw
564 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Freescale i.MX7D ADC driver4 *5 * Copyright (C) 2015 Freescale Semiconductor, Inc.6 */7 8#include <linux/clk.h>9#include <linux/completion.h>10#include <linux/err.h>11#include <linux/interrupt.h>12#include <linux/io.h>13#include <linux/kernel.h>14#include <linux/mod_devicetable.h>15#include <linux/module.h>16#include <linux/mutex.h>17#include <linux/platform_device.h>18#include <linux/regulator/consumer.h>19 20#include <linux/iio/iio.h>21#include <linux/iio/driver.h>22#include <linux/iio/sysfs.h>23 24/* ADC register */25#define IMX7D_REG_ADC_CH_A_CFG1			0x0026#define IMX7D_REG_ADC_CH_A_CFG2			0x1027#define IMX7D_REG_ADC_CH_B_CFG1			0x2028#define IMX7D_REG_ADC_CH_B_CFG2			0x3029#define IMX7D_REG_ADC_CH_C_CFG1			0x4030#define IMX7D_REG_ADC_CH_C_CFG2			0x5031#define IMX7D_REG_ADC_CH_D_CFG1			0x6032#define IMX7D_REG_ADC_CH_D_CFG2			0x7033#define IMX7D_REG_ADC_CH_SW_CFG			0x8034#define IMX7D_REG_ADC_TIMER_UNIT		0x9035#define IMX7D_REG_ADC_DMA_FIFO			0xa036#define IMX7D_REG_ADC_FIFO_STATUS		0xb037#define IMX7D_REG_ADC_INT_SIG_EN		0xc038#define IMX7D_REG_ADC_INT_EN			0xd039#define IMX7D_REG_ADC_INT_STATUS		0xe040#define IMX7D_REG_ADC_CHA_B_CNV_RSLT		0xf041#define IMX7D_REG_ADC_CHC_D_CNV_RSLT		0x10042#define IMX7D_REG_ADC_CH_SW_CNV_RSLT		0x11043#define IMX7D_REG_ADC_DMA_FIFO_DAT		0x12044#define IMX7D_REG_ADC_ADC_CFG			0x13045 46#define IMX7D_REG_ADC_CHANNEL_CFG2_BASE		0x1047#define IMX7D_EACH_CHANNEL_REG_OFFSET		0x2048 49#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN			(0x1 << 31)50#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE			BIT(30)51#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN			BIT(29)52#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(x)			((x) << 24)53 54#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4				(0x0 << 12)55#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8				(0x1 << 12)56#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16			(0x2 << 12)57#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32			(0x3 << 12)58 59#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4			(0x0 << 29)60#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8			(0x1 << 29)61#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16			(0x2 << 29)62#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32			(0x3 << 29)63#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64			(0x4 << 29)64#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128			(0x5 << 29)65 66#define IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN			BIT(31)67#define IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN			BIT(1)68#define IMX7D_REG_ADC_ADC_CFG_ADC_EN				BIT(0)69 70#define IMX7D_REG_ADC_INT_CHA_COV_INT_EN			BIT(8)71#define IMX7D_REG_ADC_INT_CHB_COV_INT_EN			BIT(9)72#define IMX7D_REG_ADC_INT_CHC_COV_INT_EN			BIT(10)73#define IMX7D_REG_ADC_INT_CHD_COV_INT_EN			BIT(11)74#define IMX7D_REG_ADC_INT_CHANNEL_INT_EN \75	(IMX7D_REG_ADC_INT_CHA_COV_INT_EN | \76	 IMX7D_REG_ADC_INT_CHB_COV_INT_EN | \77	 IMX7D_REG_ADC_INT_CHC_COV_INT_EN | \78	 IMX7D_REG_ADC_INT_CHD_COV_INT_EN)79#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS		0xf0080#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT		0xf000081 82#define IMX7D_ADC_TIMEOUT		msecs_to_jiffies(100)83#define IMX7D_ADC_INPUT_CLK		2400000084 85enum imx7d_adc_clk_pre_div {86	IMX7D_ADC_ANALOG_CLK_PRE_DIV_4,87	IMX7D_ADC_ANALOG_CLK_PRE_DIV_8,88	IMX7D_ADC_ANALOG_CLK_PRE_DIV_16,89	IMX7D_ADC_ANALOG_CLK_PRE_DIV_32,90	IMX7D_ADC_ANALOG_CLK_PRE_DIV_64,91	IMX7D_ADC_ANALOG_CLK_PRE_DIV_128,92};93 94enum imx7d_adc_average_num {95	IMX7D_ADC_AVERAGE_NUM_4,96	IMX7D_ADC_AVERAGE_NUM_8,97	IMX7D_ADC_AVERAGE_NUM_16,98	IMX7D_ADC_AVERAGE_NUM_32,99};100 101struct imx7d_adc_feature {102	enum imx7d_adc_clk_pre_div clk_pre_div;103	enum imx7d_adc_average_num avg_num;104 105	u32 core_time_unit;	/* impact the sample rate */106};107 108struct imx7d_adc {109	struct device *dev;110	void __iomem *regs;111	struct clk *clk;112	/* lock to protect against multiple access to the device */113	struct mutex lock;114	u32 vref_uv;115	u32 value;116	u32 channel;117	u32 pre_div_num;118 119	struct regulator *vref;120	struct imx7d_adc_feature adc_feature;121 122	struct completion completion;123};124 125struct imx7d_adc_analogue_core_clk {126	u32 pre_div;127	u32 reg_config;128};129 130#define IMX7D_ADC_ANALOGUE_CLK_CONFIG(_pre_div, _reg_conf) {	\131	.pre_div = (_pre_div),					\132	.reg_config = (_reg_conf),				\133}134 135static const struct imx7d_adc_analogue_core_clk imx7d_adc_analogue_clk[] = {136	IMX7D_ADC_ANALOGUE_CLK_CONFIG(4, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4),137	IMX7D_ADC_ANALOGUE_CLK_CONFIG(8, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8),138	IMX7D_ADC_ANALOGUE_CLK_CONFIG(16, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16),139	IMX7D_ADC_ANALOGUE_CLK_CONFIG(32, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32),140	IMX7D_ADC_ANALOGUE_CLK_CONFIG(64, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64),141	IMX7D_ADC_ANALOGUE_CLK_CONFIG(128, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128),142};143 144#define IMX7D_ADC_CHAN(_idx) {					\145	.type = IIO_VOLTAGE,					\146	.indexed = 1,						\147	.channel = (_idx),					\148	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\149	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |	\150				BIT(IIO_CHAN_INFO_SAMP_FREQ),	\151}152 153static const struct iio_chan_spec imx7d_adc_iio_channels[] = {154	IMX7D_ADC_CHAN(0),155	IMX7D_ADC_CHAN(1),156	IMX7D_ADC_CHAN(2),157	IMX7D_ADC_CHAN(3),158	IMX7D_ADC_CHAN(4),159	IMX7D_ADC_CHAN(5),160	IMX7D_ADC_CHAN(6),161	IMX7D_ADC_CHAN(7),162	IMX7D_ADC_CHAN(8),163	IMX7D_ADC_CHAN(9),164	IMX7D_ADC_CHAN(10),165	IMX7D_ADC_CHAN(11),166	IMX7D_ADC_CHAN(12),167	IMX7D_ADC_CHAN(13),168	IMX7D_ADC_CHAN(14),169	IMX7D_ADC_CHAN(15),170};171 172static const u32 imx7d_adc_average_num[] = {173	IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4,174	IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8,175	IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16,176	IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32,177};178 179static void imx7d_adc_feature_config(struct imx7d_adc *info)180{181	info->adc_feature.clk_pre_div = IMX7D_ADC_ANALOG_CLK_PRE_DIV_4;182	info->adc_feature.avg_num = IMX7D_ADC_AVERAGE_NUM_32;183	info->adc_feature.core_time_unit = 1;184}185 186static void imx7d_adc_sample_rate_set(struct imx7d_adc *info)187{188	struct imx7d_adc_feature *adc_feature = &info->adc_feature;189	struct imx7d_adc_analogue_core_clk adc_analogure_clk;190	u32 i;191	u32 tmp_cfg1;192	u32 sample_rate = 0;193 194	/*195	 * Before sample set, disable channel A,B,C,D. Here we196	 * clear the bit 31 of register REG_ADC_CH_A\B\C\D_CFG1.197	 */198	for (i = 0; i < 4; i++) {199		tmp_cfg1 =200			readl(info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);201		tmp_cfg1 &= ~IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN;202		writel(tmp_cfg1,203		       info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);204	}205 206	adc_analogure_clk = imx7d_adc_analogue_clk[adc_feature->clk_pre_div];207	sample_rate |= adc_analogure_clk.reg_config;208	info->pre_div_num = adc_analogure_clk.pre_div;209 210	sample_rate |= adc_feature->core_time_unit;211	writel(sample_rate, info->regs + IMX7D_REG_ADC_TIMER_UNIT);212}213 214static void imx7d_adc_hw_init(struct imx7d_adc *info)215{216	u32 cfg;217 218	/* power up and enable adc analogue core */219	cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);220	cfg &= ~(IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |221		 IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN);222	cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_EN;223	writel(cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);224 225	/* enable channel A,B,C,D interrupt */226	writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,227	       info->regs + IMX7D_REG_ADC_INT_SIG_EN);228	writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,229	       info->regs + IMX7D_REG_ADC_INT_EN);230 231	imx7d_adc_sample_rate_set(info);232}233 234static void imx7d_adc_channel_set(struct imx7d_adc *info)235{236	u32 cfg1 = 0;237	u32 cfg2;238	u32 channel;239 240	channel = info->channel;241 242	/* the channel choose single conversion, and enable average mode */243	cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN |244		 IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE |245		 IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN);246 247	/*248	 * physical channel 0 chose logical channel A249	 * physical channel 1 chose logical channel B250	 * physical channel 2 chose logical channel C251	 * physical channel 3 chose logical channel D252	 */253	cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(channel);254 255	/*256	 * read register REG_ADC_CH_A\B\C\D_CFG2, according to the257	 * channel chosen258	 */259	cfg2 = readl(info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +260		     IMX7D_REG_ADC_CHANNEL_CFG2_BASE);261 262	cfg2 |= imx7d_adc_average_num[info->adc_feature.avg_num];263 264	/*265	 * write the register REG_ADC_CH_A\B\C\D_CFG2, according to266	 * the channel chosen267	 */268	writel(cfg2, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +269	       IMX7D_REG_ADC_CHANNEL_CFG2_BASE);270	writel(cfg1, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel);271}272 273static u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info)274{275	u32 analogue_core_clk;276	u32 core_time_unit = info->adc_feature.core_time_unit;277	u32 tmp;278 279	analogue_core_clk = IMX7D_ADC_INPUT_CLK / info->pre_div_num;280	tmp = (core_time_unit + 1) * 6;281 282	return analogue_core_clk / tmp;283}284 285static int imx7d_adc_read_raw(struct iio_dev *indio_dev,286			struct iio_chan_spec const *chan,287			int *val,288			int *val2,289			long mask)290{291	struct imx7d_adc *info = iio_priv(indio_dev);292 293	u32 channel;294	long ret;295 296	switch (mask) {297	case IIO_CHAN_INFO_RAW:298		mutex_lock(&info->lock);299		reinit_completion(&info->completion);300 301		channel = chan->channel & 0x03;302		info->channel = channel;303		imx7d_adc_channel_set(info);304 305		ret = wait_for_completion_interruptible_timeout306				(&info->completion, IMX7D_ADC_TIMEOUT);307		if (ret == 0) {308			mutex_unlock(&info->lock);309			return -ETIMEDOUT;310		}311		if (ret < 0) {312			mutex_unlock(&info->lock);313			return ret;314		}315 316		*val = info->value;317		mutex_unlock(&info->lock);318		return IIO_VAL_INT;319 320	case IIO_CHAN_INFO_SCALE:321		info->vref_uv = regulator_get_voltage(info->vref);322		*val = info->vref_uv / 1000;323		*val2 = 12;324		return IIO_VAL_FRACTIONAL_LOG2;325 326	case IIO_CHAN_INFO_SAMP_FREQ:327		*val = imx7d_adc_get_sample_rate(info);328		return IIO_VAL_INT;329 330	default:331		return -EINVAL;332	}333}334 335static int imx7d_adc_read_data(struct imx7d_adc *info)336{337	u32 channel;338	u32 value;339 340	channel = info->channel & 0x03;341 342	/*343	 * channel A and B conversion result share one register,344	 * bit[27~16] is the channel B conversion result,345	 * bit[11~0] is the channel A conversion result.346	 * channel C and D is the same.347	 */348	if (channel < 2)349		value = readl(info->regs + IMX7D_REG_ADC_CHA_B_CNV_RSLT);350	else351		value = readl(info->regs + IMX7D_REG_ADC_CHC_D_CNV_RSLT);352	if (channel & 0x1)	/* channel B or D */353		value = (value >> 16) & 0xFFF;354	else			/* channel A or C */355		value &= 0xFFF;356 357	return value;358}359 360static irqreturn_t imx7d_adc_isr(int irq, void *dev_id)361{362	struct imx7d_adc *info = dev_id;363	int status;364 365	status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS);366	if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS) {367		info->value = imx7d_adc_read_data(info);368		complete(&info->completion);369 370		/*371		 * The register IMX7D_REG_ADC_INT_STATUS can't clear372		 * itself after read operation, need software to write373		 * 0 to the related bit. Here we clear the channel A/B/C/D374		 * conversion finished flag.375		 */376		status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS;377		writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);378	}379 380	/*381	 * If the channel A/B/C/D conversion timeout, report it and clear these382	 * timeout flags.383	 */384	if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT) {385		dev_err(info->dev,386			"ADC got conversion time out interrupt: 0x%08x\n",387			status);388		status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT;389		writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);390	}391 392	return IRQ_HANDLED;393}394 395static int imx7d_adc_reg_access(struct iio_dev *indio_dev,396			unsigned reg, unsigned writeval,397			unsigned *readval)398{399	struct imx7d_adc *info = iio_priv(indio_dev);400 401	if (!readval || reg % 4 || reg > IMX7D_REG_ADC_ADC_CFG)402		return -EINVAL;403 404	*readval = readl(info->regs + reg);405 406	return 0;407}408 409static const struct iio_info imx7d_adc_iio_info = {410	.read_raw = &imx7d_adc_read_raw,411	.debugfs_reg_access = &imx7d_adc_reg_access,412};413 414static const struct of_device_id imx7d_adc_match[] = {415	{ .compatible = "fsl,imx7d-adc", },416	{ /* sentinel */ }417};418MODULE_DEVICE_TABLE(of, imx7d_adc_match);419 420static void imx7d_adc_power_down(struct imx7d_adc *info)421{422	u32 adc_cfg;423 424	adc_cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);425	adc_cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |426		   IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN;427	adc_cfg &= ~IMX7D_REG_ADC_ADC_CFG_ADC_EN;428	writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);429}430 431static int imx7d_adc_enable(struct device *dev)432{433	struct iio_dev *indio_dev = dev_get_drvdata(dev);434	struct imx7d_adc *info = iio_priv(indio_dev);435	int ret;436 437	ret = regulator_enable(info->vref);438	if (ret) {439		dev_err(info->dev,440			"Can't enable adc reference top voltage, err = %d\n",441			ret);442		return ret;443	}444 445	ret = clk_prepare_enable(info->clk);446	if (ret) {447		dev_err(info->dev,448			"Could not prepare or enable clock.\n");449		regulator_disable(info->vref);450		return ret;451	}452 453	imx7d_adc_hw_init(info);454 455	return 0;456}457 458static int imx7d_adc_disable(struct device *dev)459{460	struct iio_dev *indio_dev = dev_get_drvdata(dev);461	struct imx7d_adc *info = iio_priv(indio_dev);462 463	imx7d_adc_power_down(info);464 465	clk_disable_unprepare(info->clk);466	regulator_disable(info->vref);467 468	return 0;469}470 471static void __imx7d_adc_disable(void *data)472{473	imx7d_adc_disable(data);474}475 476static int imx7d_adc_probe(struct platform_device *pdev)477{478	struct imx7d_adc *info;479	struct iio_dev *indio_dev;480	struct device *dev = &pdev->dev;481	int irq;482	int ret;483 484	indio_dev = devm_iio_device_alloc(dev, sizeof(*info));485	if (!indio_dev) {486		dev_err(&pdev->dev, "Failed allocating iio device\n");487		return -ENOMEM;488	}489 490	info = iio_priv(indio_dev);491	info->dev = dev;492 493	info->regs = devm_platform_ioremap_resource(pdev, 0);494	if (IS_ERR(info->regs))495		return PTR_ERR(info->regs);496 497	irq = platform_get_irq(pdev, 0);498	if (irq < 0)499		return irq;500 501	info->clk = devm_clk_get(dev, "adc");502	if (IS_ERR(info->clk))503		return dev_err_probe(dev, PTR_ERR(info->clk), "Failed getting clock\n");504 505	info->vref = devm_regulator_get(dev, "vref");506	if (IS_ERR(info->vref))507		return dev_err_probe(dev, PTR_ERR(info->vref),508				     "Failed getting reference voltage\n");509 510	platform_set_drvdata(pdev, indio_dev);511 512	init_completion(&info->completion);513 514	indio_dev->name = dev_name(dev);515	indio_dev->info = &imx7d_adc_iio_info;516	indio_dev->modes = INDIO_DIRECT_MODE;517	indio_dev->channels = imx7d_adc_iio_channels;518	indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels);519 520	ret = devm_request_irq(dev, irq, imx7d_adc_isr, 0, dev_name(dev), info);521	if (ret < 0) {522		dev_err(dev, "Failed requesting irq, irq = %d\n", irq);523		return ret;524	}525 526	imx7d_adc_feature_config(info);527 528	ret = imx7d_adc_enable(dev);529	if (ret)530		return ret;531 532	ret = devm_add_action_or_reset(dev, __imx7d_adc_disable, dev);533	if (ret)534		return ret;535 536	mutex_init(&info->lock);537 538	ret = devm_iio_device_register(dev, indio_dev);539	if (ret) {540		dev_err(&pdev->dev, "Couldn't register the device.\n");541		return ret;542	}543 544	return 0;545}546 547static DEFINE_SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_disable,548				imx7d_adc_enable);549 550static struct platform_driver imx7d_adc_driver = {551	.probe		= imx7d_adc_probe,552	.driver		= {553		.name	= "imx7d_adc",554		.of_match_table = imx7d_adc_match,555		.pm	= pm_sleep_ptr(&imx7d_adc_pm_ops),556	},557};558 559module_platform_driver(imx7d_adc_driver);560 561MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>");562MODULE_DESCRIPTION("Freescale IMX7D ADC driver");563MODULE_LICENSE("GPL v2");564