cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mt6577_auxadc.c (9583B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (c) 2016 MediaTek Inc.
      4 * Author: Zhiyong Tao <zhiyong.tao@mediatek.com>
      5 */
      6
      7#include <linux/clk.h>
      8#include <linux/delay.h>
      9#include <linux/err.h>
     10#include <linux/kernel.h>
     11#include <linux/module.h>
     12#include <linux/mod_devicetable.h>
     13#include <linux/platform_device.h>
     14#include <linux/property.h>
     15#include <linux/iopoll.h>
     16#include <linux/io.h>
     17#include <linux/iio/iio.h>
     18
     19/* Register definitions */
     20#define MT6577_AUXADC_CON0                    0x00
     21#define MT6577_AUXADC_CON1                    0x04
     22#define MT6577_AUXADC_CON2                    0x10
     23#define MT6577_AUXADC_STA                     BIT(0)
     24
     25#define MT6577_AUXADC_DAT0                    0x14
     26#define MT6577_AUXADC_RDY0                    BIT(12)
     27
     28#define MT6577_AUXADC_MISC                    0x94
     29#define MT6577_AUXADC_PDN_EN                  BIT(14)
     30
     31#define MT6577_AUXADC_DAT_MASK                0xfff
     32#define MT6577_AUXADC_SLEEP_US                1000
     33#define MT6577_AUXADC_TIMEOUT_US              10000
     34#define MT6577_AUXADC_POWER_READY_MS          1
     35#define MT6577_AUXADC_SAMPLE_READY_US         25
     36
     37struct mtk_auxadc_compatible {
     38	bool sample_data_cali;
     39	bool check_global_idle;
     40};
     41
     42struct mt6577_auxadc_device {
     43	void __iomem *reg_base;
     44	struct clk *adc_clk;
     45	struct mutex lock;
     46	const struct mtk_auxadc_compatible *dev_comp;
     47};
     48
     49static const struct mtk_auxadc_compatible mt8186_compat = {
     50	.sample_data_cali = false,
     51	.check_global_idle = false,
     52};
     53
     54static const struct mtk_auxadc_compatible mt8173_compat = {
     55	.sample_data_cali = false,
     56	.check_global_idle = true,
     57};
     58
     59static const struct mtk_auxadc_compatible mt6765_compat = {
     60	.sample_data_cali = true,
     61	.check_global_idle = false,
     62};
     63
     64#define MT6577_AUXADC_CHANNEL(idx) {				    \
     65		.type = IIO_VOLTAGE,				    \
     66		.indexed = 1,					    \
     67		.channel = (idx),				    \
     68		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
     69}
     70
     71static const struct iio_chan_spec mt6577_auxadc_iio_channels[] = {
     72	MT6577_AUXADC_CHANNEL(0),
     73	MT6577_AUXADC_CHANNEL(1),
     74	MT6577_AUXADC_CHANNEL(2),
     75	MT6577_AUXADC_CHANNEL(3),
     76	MT6577_AUXADC_CHANNEL(4),
     77	MT6577_AUXADC_CHANNEL(5),
     78	MT6577_AUXADC_CHANNEL(6),
     79	MT6577_AUXADC_CHANNEL(7),
     80	MT6577_AUXADC_CHANNEL(8),
     81	MT6577_AUXADC_CHANNEL(9),
     82	MT6577_AUXADC_CHANNEL(10),
     83	MT6577_AUXADC_CHANNEL(11),
     84	MT6577_AUXADC_CHANNEL(12),
     85	MT6577_AUXADC_CHANNEL(13),
     86	MT6577_AUXADC_CHANNEL(14),
     87	MT6577_AUXADC_CHANNEL(15),
     88};
     89
     90/* For Voltage calculation */
     91#define VOLTAGE_FULL_RANGE  1500	/* VA voltage */
     92#define AUXADC_PRECISE      4096	/* 12 bits */
     93
     94static int mt_auxadc_get_cali_data(int rawdata, bool enable_cali)
     95{
     96	return rawdata;
     97}
     98
     99static inline void mt6577_auxadc_mod_reg(void __iomem *reg,
    100					 u32 or_mask, u32 and_mask)
    101{
    102	u32 val;
    103
    104	val = readl(reg);
    105	val |= or_mask;
    106	val &= ~and_mask;
    107	writel(val, reg);
    108}
    109
    110static int mt6577_auxadc_read(struct iio_dev *indio_dev,
    111			      struct iio_chan_spec const *chan)
    112{
    113	u32 val;
    114	void __iomem *reg_channel;
    115	int ret;
    116	struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
    117
    118	reg_channel = adc_dev->reg_base + MT6577_AUXADC_DAT0 +
    119		      chan->channel * 0x04;
    120
    121	mutex_lock(&adc_dev->lock);
    122
    123	mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1,
    124			      0, 1 << chan->channel);
    125
    126	/* read channel and make sure old ready bit == 0 */
    127	ret = readl_poll_timeout(reg_channel, val,
    128				 ((val & MT6577_AUXADC_RDY0) == 0),
    129				 MT6577_AUXADC_SLEEP_US,
    130				 MT6577_AUXADC_TIMEOUT_US);
    131	if (ret < 0) {
    132		dev_err(indio_dev->dev.parent,
    133			"wait for channel[%d] ready bit clear time out\n",
    134			chan->channel);
    135		goto err_timeout;
    136	}
    137
    138	/* set bit to trigger sample */
    139	mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1,
    140			      1 << chan->channel, 0);
    141
    142	/* we must delay here for hardware sample channel data */
    143	udelay(MT6577_AUXADC_SAMPLE_READY_US);
    144
    145	if (adc_dev->dev_comp->check_global_idle) {
    146		/* check MTK_AUXADC_CON2 if auxadc is idle */
    147		ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2,
    148					 val, ((val & MT6577_AUXADC_STA) == 0),
    149					 MT6577_AUXADC_SLEEP_US,
    150					 MT6577_AUXADC_TIMEOUT_US);
    151		if (ret < 0) {
    152			dev_err(indio_dev->dev.parent,
    153				"wait for auxadc idle time out\n");
    154			goto err_timeout;
    155		}
    156	}
    157
    158	/* read channel and make sure ready bit == 1 */
    159	ret = readl_poll_timeout(reg_channel, val,
    160				 ((val & MT6577_AUXADC_RDY0) != 0),
    161				 MT6577_AUXADC_SLEEP_US,
    162				 MT6577_AUXADC_TIMEOUT_US);
    163	if (ret < 0) {
    164		dev_err(indio_dev->dev.parent,
    165			"wait for channel[%d] data ready time out\n",
    166			chan->channel);
    167		goto err_timeout;
    168	}
    169
    170	/* read data */
    171	val = readl(reg_channel) & MT6577_AUXADC_DAT_MASK;
    172
    173	mutex_unlock(&adc_dev->lock);
    174
    175	return val;
    176
    177err_timeout:
    178
    179	mutex_unlock(&adc_dev->lock);
    180
    181	return -ETIMEDOUT;
    182}
    183
    184static int mt6577_auxadc_read_raw(struct iio_dev *indio_dev,
    185				  struct iio_chan_spec const *chan,
    186				  int *val,
    187				  int *val2,
    188				  long info)
    189{
    190	struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
    191
    192	switch (info) {
    193	case IIO_CHAN_INFO_PROCESSED:
    194		*val = mt6577_auxadc_read(indio_dev, chan);
    195		if (*val < 0) {
    196			dev_err(indio_dev->dev.parent,
    197				"failed to sample data on channel[%d]\n",
    198				chan->channel);
    199			return *val;
    200		}
    201		if (adc_dev->dev_comp->sample_data_cali)
    202			*val = mt_auxadc_get_cali_data(*val, true);
    203
    204		/* Convert adc raw data to voltage: 0 - 1500 mV */
    205		*val = *val * VOLTAGE_FULL_RANGE / AUXADC_PRECISE;
    206
    207		return IIO_VAL_INT;
    208
    209	default:
    210		return -EINVAL;
    211	}
    212}
    213
    214static const struct iio_info mt6577_auxadc_info = {
    215	.read_raw = &mt6577_auxadc_read_raw,
    216};
    217
    218static int __maybe_unused mt6577_auxadc_resume(struct device *dev)
    219{
    220	struct iio_dev *indio_dev = dev_get_drvdata(dev);
    221	struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
    222	int ret;
    223
    224	ret = clk_prepare_enable(adc_dev->adc_clk);
    225	if (ret) {
    226		pr_err("failed to enable auxadc clock\n");
    227		return ret;
    228	}
    229
    230	mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
    231			      MT6577_AUXADC_PDN_EN, 0);
    232	mdelay(MT6577_AUXADC_POWER_READY_MS);
    233
    234	return 0;
    235}
    236
    237static int __maybe_unused mt6577_auxadc_suspend(struct device *dev)
    238{
    239	struct iio_dev *indio_dev = dev_get_drvdata(dev);
    240	struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
    241
    242	mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
    243			      0, MT6577_AUXADC_PDN_EN);
    244	clk_disable_unprepare(adc_dev->adc_clk);
    245
    246	return 0;
    247}
    248
    249static int mt6577_auxadc_probe(struct platform_device *pdev)
    250{
    251	struct mt6577_auxadc_device *adc_dev;
    252	unsigned long adc_clk_rate;
    253	struct iio_dev *indio_dev;
    254	int ret;
    255
    256	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
    257	if (!indio_dev)
    258		return -ENOMEM;
    259
    260	adc_dev = iio_priv(indio_dev);
    261	indio_dev->name = dev_name(&pdev->dev);
    262	indio_dev->info = &mt6577_auxadc_info;
    263	indio_dev->modes = INDIO_DIRECT_MODE;
    264	indio_dev->channels = mt6577_auxadc_iio_channels;
    265	indio_dev->num_channels = ARRAY_SIZE(mt6577_auxadc_iio_channels);
    266
    267	adc_dev->reg_base = devm_platform_ioremap_resource(pdev, 0);
    268	if (IS_ERR(adc_dev->reg_base)) {
    269		dev_err(&pdev->dev, "failed to get auxadc base address\n");
    270		return PTR_ERR(adc_dev->reg_base);
    271	}
    272
    273	adc_dev->adc_clk = devm_clk_get(&pdev->dev, "main");
    274	if (IS_ERR(adc_dev->adc_clk)) {
    275		dev_err(&pdev->dev, "failed to get auxadc clock\n");
    276		return PTR_ERR(adc_dev->adc_clk);
    277	}
    278
    279	ret = clk_prepare_enable(adc_dev->adc_clk);
    280	if (ret) {
    281		dev_err(&pdev->dev, "failed to enable auxadc clock\n");
    282		return ret;
    283	}
    284
    285	adc_clk_rate = clk_get_rate(adc_dev->adc_clk);
    286	if (!adc_clk_rate) {
    287		ret = -EINVAL;
    288		dev_err(&pdev->dev, "null clock rate\n");
    289		goto err_disable_clk;
    290	}
    291
    292	adc_dev->dev_comp = device_get_match_data(&pdev->dev);
    293
    294	mutex_init(&adc_dev->lock);
    295
    296	mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
    297			      MT6577_AUXADC_PDN_EN, 0);
    298	mdelay(MT6577_AUXADC_POWER_READY_MS);
    299
    300	platform_set_drvdata(pdev, indio_dev);
    301
    302	ret = iio_device_register(indio_dev);
    303	if (ret < 0) {
    304		dev_err(&pdev->dev, "failed to register iio device\n");
    305		goto err_power_off;
    306	}
    307
    308	return 0;
    309
    310err_power_off:
    311	mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
    312			      0, MT6577_AUXADC_PDN_EN);
    313err_disable_clk:
    314	clk_disable_unprepare(adc_dev->adc_clk);
    315	return ret;
    316}
    317
    318static int mt6577_auxadc_remove(struct platform_device *pdev)
    319{
    320	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
    321	struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
    322
    323	iio_device_unregister(indio_dev);
    324
    325	mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
    326			      0, MT6577_AUXADC_PDN_EN);
    327
    328	clk_disable_unprepare(adc_dev->adc_clk);
    329
    330	return 0;
    331}
    332
    333static SIMPLE_DEV_PM_OPS(mt6577_auxadc_pm_ops,
    334			 mt6577_auxadc_suspend,
    335			 mt6577_auxadc_resume);
    336
    337static const struct of_device_id mt6577_auxadc_of_match[] = {
    338	{ .compatible = "mediatek,mt2701-auxadc", .data = &mt8173_compat },
    339	{ .compatible = "mediatek,mt2712-auxadc", .data = &mt8173_compat },
    340	{ .compatible = "mediatek,mt7622-auxadc", .data = &mt8173_compat },
    341	{ .compatible = "mediatek,mt8173-auxadc", .data = &mt8173_compat },
    342	{ .compatible = "mediatek,mt8186-auxadc", .data = &mt8186_compat },
    343	{ .compatible = "mediatek,mt6765-auxadc", .data = &mt6765_compat },
    344	{ }
    345};
    346MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match);
    347
    348static struct platform_driver mt6577_auxadc_driver = {
    349	.driver = {
    350		.name   = "mt6577-auxadc",
    351		.of_match_table = mt6577_auxadc_of_match,
    352		.pm = &mt6577_auxadc_pm_ops,
    353	},
    354	.probe	= mt6577_auxadc_probe,
    355	.remove	= mt6577_auxadc_remove,
    356};
    357module_platform_driver(mt6577_auxadc_driver);
    358
    359MODULE_AUTHOR("Zhiyong Tao <zhiyong.tao@mediatek.com>");
    360MODULE_DESCRIPTION("MTK AUXADC Device Driver");
    361MODULE_LICENSE("GPL v2");