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

ir-hix5hd2.c (9883B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (c) 2014 Linaro Ltd.
      4 * Copyright (c) 2014 HiSilicon Limited.
      5 */
      6
      7#include <linux/clk.h>
      8#include <linux/delay.h>
      9#include <linux/interrupt.h>
     10#include <linux/mfd/syscon.h>
     11#include <linux/module.h>
     12#include <linux/of_device.h>
     13#include <linux/regmap.h>
     14#include <media/rc-core.h>
     15
     16#define IR_ENABLE		0x00
     17#define IR_CONFIG		0x04
     18#define CNT_LEADS		0x08
     19#define CNT_LEADE		0x0c
     20#define CNT_SLEADE		0x10
     21#define CNT0_B			0x14
     22#define CNT1_B			0x18
     23#define IR_BUSY			0x1c
     24#define IR_DATAH		0x20
     25#define IR_DATAL		0x24
     26#define IR_INTM			0x28
     27#define IR_INTS			0x2c
     28#define IR_INTC			0x30
     29#define IR_START		0x34
     30
     31/* interrupt mask */
     32#define INTMS_SYMBRCV		(BIT(24) | BIT(8))
     33#define INTMS_TIMEOUT		(BIT(25) | BIT(9))
     34#define INTMS_OVERFLOW		(BIT(26) | BIT(10))
     35#define INT_CLR_OVERFLOW	BIT(18)
     36#define INT_CLR_TIMEOUT		BIT(17)
     37#define INT_CLR_RCV		BIT(16)
     38#define INT_CLR_RCVTIMEOUT	(BIT(16) | BIT(17))
     39
     40#define IR_CLK_ENABLE		BIT(4)
     41#define IR_CLK_RESET		BIT(5)
     42
     43/* IR_ENABLE register bits */
     44#define IR_ENABLE_EN		BIT(0)
     45#define IR_ENABLE_EN_EXTRA	BIT(8)
     46
     47#define IR_CFG_WIDTH_MASK	0xffff
     48#define IR_CFG_WIDTH_SHIFT	16
     49#define IR_CFG_FORMAT_MASK	0x3
     50#define IR_CFG_FORMAT_SHIFT	14
     51#define IR_CFG_INT_LEVEL_MASK	0x3f
     52#define IR_CFG_INT_LEVEL_SHIFT	8
     53/* only support raw mode */
     54#define IR_CFG_MODE_RAW		BIT(7)
     55#define IR_CFG_FREQ_MASK	0x7f
     56#define IR_CFG_FREQ_SHIFT	0
     57#define IR_CFG_INT_THRESHOLD	1
     58/* symbol start from low to high, symbol stream end at high*/
     59#define IR_CFG_SYMBOL_FMT	0
     60#define IR_CFG_SYMBOL_MAXWIDTH	0x3e80
     61
     62#define IR_HIX5HD2_NAME		"hix5hd2-ir"
     63
     64/* Need to set extra bit for enabling IR */
     65#define HIX5HD2_FLAG_EXTRA_ENABLE	BIT(0)
     66
     67struct hix5hd2_soc_data {
     68	u32 clk_reg;
     69	u32 flags;
     70};
     71
     72static const struct hix5hd2_soc_data hix5hd2_data = {
     73	.clk_reg = 0x48,
     74};
     75
     76static const struct hix5hd2_soc_data hi3796cv300_data = {
     77	.clk_reg = 0x60,
     78	.flags = HIX5HD2_FLAG_EXTRA_ENABLE,
     79};
     80
     81struct hix5hd2_ir_priv {
     82	int			irq;
     83	void __iomem		*base;
     84	struct device		*dev;
     85	struct rc_dev		*rdev;
     86	struct regmap		*regmap;
     87	struct clk		*clock;
     88	unsigned long		rate;
     89	const struct hix5hd2_soc_data *socdata;
     90};
     91
     92static int hix5hd2_ir_clk_enable(struct hix5hd2_ir_priv *dev, bool on)
     93{
     94	u32 clk_reg = dev->socdata->clk_reg;
     95	u32 val;
     96	int ret = 0;
     97
     98	if (dev->regmap) {
     99		regmap_read(dev->regmap, clk_reg, &val);
    100		if (on) {
    101			val &= ~IR_CLK_RESET;
    102			val |= IR_CLK_ENABLE;
    103		} else {
    104			val &= ~IR_CLK_ENABLE;
    105			val |= IR_CLK_RESET;
    106		}
    107		regmap_write(dev->regmap, clk_reg, val);
    108	} else {
    109		if (on)
    110			ret = clk_prepare_enable(dev->clock);
    111		else
    112			clk_disable_unprepare(dev->clock);
    113	}
    114	return ret;
    115}
    116
    117static inline void hix5hd2_ir_enable(struct hix5hd2_ir_priv *priv)
    118{
    119	u32 val = IR_ENABLE_EN;
    120
    121	if (priv->socdata->flags & HIX5HD2_FLAG_EXTRA_ENABLE)
    122		val |= IR_ENABLE_EN_EXTRA;
    123
    124	writel_relaxed(val, priv->base + IR_ENABLE);
    125}
    126
    127static int hix5hd2_ir_config(struct hix5hd2_ir_priv *priv)
    128{
    129	int timeout = 10000;
    130	u32 val, rate;
    131
    132	hix5hd2_ir_enable(priv);
    133
    134	while (readl_relaxed(priv->base + IR_BUSY)) {
    135		if (timeout--) {
    136			udelay(1);
    137		} else {
    138			dev_err(priv->dev, "IR_BUSY timeout\n");
    139			return -ETIMEDOUT;
    140		}
    141	}
    142
    143	/* Now only support raw mode, with symbol start from low to high */
    144	rate = DIV_ROUND_CLOSEST(priv->rate, 1000000);
    145	val = IR_CFG_SYMBOL_MAXWIDTH & IR_CFG_WIDTH_MASK << IR_CFG_WIDTH_SHIFT;
    146	val |= IR_CFG_SYMBOL_FMT & IR_CFG_FORMAT_MASK << IR_CFG_FORMAT_SHIFT;
    147	val |= (IR_CFG_INT_THRESHOLD - 1) & IR_CFG_INT_LEVEL_MASK
    148	       << IR_CFG_INT_LEVEL_SHIFT;
    149	val |= IR_CFG_MODE_RAW;
    150	val |= (rate - 1) & IR_CFG_FREQ_MASK << IR_CFG_FREQ_SHIFT;
    151	writel_relaxed(val, priv->base + IR_CONFIG);
    152
    153	writel_relaxed(0x00, priv->base + IR_INTM);
    154	/* write arbitrary value to start  */
    155	writel_relaxed(0x01, priv->base + IR_START);
    156	return 0;
    157}
    158
    159static int hix5hd2_ir_open(struct rc_dev *rdev)
    160{
    161	struct hix5hd2_ir_priv *priv = rdev->priv;
    162	int ret;
    163
    164	ret = hix5hd2_ir_clk_enable(priv, true);
    165	if (ret)
    166		return ret;
    167
    168	ret = hix5hd2_ir_config(priv);
    169	if (ret) {
    170		hix5hd2_ir_clk_enable(priv, false);
    171		return ret;
    172	}
    173	return 0;
    174}
    175
    176static void hix5hd2_ir_close(struct rc_dev *rdev)
    177{
    178	struct hix5hd2_ir_priv *priv = rdev->priv;
    179
    180	hix5hd2_ir_clk_enable(priv, false);
    181}
    182
    183static irqreturn_t hix5hd2_ir_rx_interrupt(int irq, void *data)
    184{
    185	u32 symb_num, symb_val, symb_time;
    186	u32 data_l, data_h;
    187	u32 irq_sr, i;
    188	struct hix5hd2_ir_priv *priv = data;
    189
    190	irq_sr = readl_relaxed(priv->base + IR_INTS);
    191	if (irq_sr & INTMS_OVERFLOW) {
    192		/*
    193		 * we must read IR_DATAL first, then we can clean up
    194		 * IR_INTS availably since logic would not clear
    195		 * fifo when overflow, drv do the job
    196		 */
    197		ir_raw_event_overflow(priv->rdev);
    198		symb_num = readl_relaxed(priv->base + IR_DATAH);
    199		for (i = 0; i < symb_num; i++)
    200			readl_relaxed(priv->base + IR_DATAL);
    201
    202		writel_relaxed(INT_CLR_OVERFLOW, priv->base + IR_INTC);
    203		dev_info(priv->dev, "overflow, level=%d\n",
    204			 IR_CFG_INT_THRESHOLD);
    205	}
    206
    207	if ((irq_sr & INTMS_SYMBRCV) || (irq_sr & INTMS_TIMEOUT)) {
    208		struct ir_raw_event ev = {};
    209
    210		symb_num = readl_relaxed(priv->base + IR_DATAH);
    211		for (i = 0; i < symb_num; i++) {
    212			symb_val = readl_relaxed(priv->base + IR_DATAL);
    213			data_l = ((symb_val & 0xffff) * 10);
    214			data_h =  ((symb_val >> 16) & 0xffff) * 10;
    215			symb_time = (data_l + data_h) / 10;
    216
    217			ev.duration = data_l;
    218			ev.pulse = true;
    219			ir_raw_event_store(priv->rdev, &ev);
    220
    221			if (symb_time < IR_CFG_SYMBOL_MAXWIDTH) {
    222				ev.duration = data_h;
    223				ev.pulse = false;
    224				ir_raw_event_store(priv->rdev, &ev);
    225			} else {
    226				ir_raw_event_set_idle(priv->rdev, true);
    227			}
    228		}
    229
    230		if (irq_sr & INTMS_SYMBRCV)
    231			writel_relaxed(INT_CLR_RCV, priv->base + IR_INTC);
    232		if (irq_sr & INTMS_TIMEOUT)
    233			writel_relaxed(INT_CLR_TIMEOUT, priv->base + IR_INTC);
    234	}
    235
    236	/* Empty software fifo */
    237	ir_raw_event_handle(priv->rdev);
    238	return IRQ_HANDLED;
    239}
    240
    241static const struct of_device_id hix5hd2_ir_table[] = {
    242	{ .compatible = "hisilicon,hix5hd2-ir", &hix5hd2_data, },
    243	{ .compatible = "hisilicon,hi3796cv300-ir", &hi3796cv300_data, },
    244	{},
    245};
    246MODULE_DEVICE_TABLE(of, hix5hd2_ir_table);
    247
    248static int hix5hd2_ir_probe(struct platform_device *pdev)
    249{
    250	struct rc_dev *rdev;
    251	struct device *dev = &pdev->dev;
    252	struct hix5hd2_ir_priv *priv;
    253	struct device_node *node = pdev->dev.of_node;
    254	const struct of_device_id *of_id;
    255	const char *map_name;
    256	int ret;
    257
    258	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    259	if (!priv)
    260		return -ENOMEM;
    261
    262	of_id = of_match_device(hix5hd2_ir_table, dev);
    263	if (!of_id) {
    264		dev_err(dev, "Unable to initialize IR data\n");
    265		return -ENODEV;
    266	}
    267	priv->socdata = of_id->data;
    268
    269	priv->regmap = syscon_regmap_lookup_by_phandle(node,
    270						       "hisilicon,power-syscon");
    271	if (IS_ERR(priv->regmap)) {
    272		dev_info(dev, "no power-reg\n");
    273		priv->regmap = NULL;
    274	}
    275
    276	priv->base = devm_platform_ioremap_resource(pdev, 0);
    277	if (IS_ERR(priv->base))
    278		return PTR_ERR(priv->base);
    279
    280	priv->irq = platform_get_irq(pdev, 0);
    281	if (priv->irq < 0)
    282		return priv->irq;
    283
    284	rdev = rc_allocate_device(RC_DRIVER_IR_RAW);
    285	if (!rdev)
    286		return -ENOMEM;
    287
    288	priv->clock = devm_clk_get(dev, NULL);
    289	if (IS_ERR(priv->clock)) {
    290		dev_err(dev, "clock not found\n");
    291		ret = PTR_ERR(priv->clock);
    292		goto err;
    293	}
    294	ret = clk_prepare_enable(priv->clock);
    295	if (ret)
    296		goto err;
    297	priv->rate = clk_get_rate(priv->clock);
    298
    299	rdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
    300	rdev->priv = priv;
    301	rdev->open = hix5hd2_ir_open;
    302	rdev->close = hix5hd2_ir_close;
    303	rdev->driver_name = IR_HIX5HD2_NAME;
    304	map_name = of_get_property(node, "linux,rc-map-name", NULL);
    305	rdev->map_name = map_name ?: RC_MAP_EMPTY;
    306	rdev->device_name = IR_HIX5HD2_NAME;
    307	rdev->input_phys = IR_HIX5HD2_NAME "/input0";
    308	rdev->input_id.bustype = BUS_HOST;
    309	rdev->input_id.vendor = 0x0001;
    310	rdev->input_id.product = 0x0001;
    311	rdev->input_id.version = 0x0100;
    312	rdev->rx_resolution = 10;
    313	rdev->timeout = IR_CFG_SYMBOL_MAXWIDTH * 10;
    314
    315	ret = rc_register_device(rdev);
    316	if (ret < 0)
    317		goto clkerr;
    318
    319	if (devm_request_irq(dev, priv->irq, hix5hd2_ir_rx_interrupt,
    320			     0, pdev->name, priv) < 0) {
    321		dev_err(dev, "IRQ %d register failed\n", priv->irq);
    322		ret = -EINVAL;
    323		goto regerr;
    324	}
    325
    326	priv->rdev = rdev;
    327	priv->dev = dev;
    328	platform_set_drvdata(pdev, priv);
    329
    330	return ret;
    331
    332regerr:
    333	rc_unregister_device(rdev);
    334	rdev = NULL;
    335clkerr:
    336	clk_disable_unprepare(priv->clock);
    337err:
    338	rc_free_device(rdev);
    339	dev_err(dev, "Unable to register device (%d)\n", ret);
    340	return ret;
    341}
    342
    343static int hix5hd2_ir_remove(struct platform_device *pdev)
    344{
    345	struct hix5hd2_ir_priv *priv = platform_get_drvdata(pdev);
    346
    347	clk_disable_unprepare(priv->clock);
    348	rc_unregister_device(priv->rdev);
    349	return 0;
    350}
    351
    352#ifdef CONFIG_PM_SLEEP
    353static int hix5hd2_ir_suspend(struct device *dev)
    354{
    355	struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
    356
    357	clk_disable_unprepare(priv->clock);
    358	hix5hd2_ir_clk_enable(priv, false);
    359
    360	return 0;
    361}
    362
    363static int hix5hd2_ir_resume(struct device *dev)
    364{
    365	struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
    366	int ret;
    367
    368	ret = hix5hd2_ir_clk_enable(priv, true);
    369	if (ret)
    370		return ret;
    371
    372	ret = clk_prepare_enable(priv->clock);
    373	if (ret) {
    374		hix5hd2_ir_clk_enable(priv, false);
    375		return ret;
    376	}
    377
    378	hix5hd2_ir_enable(priv);
    379
    380	writel_relaxed(0x00, priv->base + IR_INTM);
    381	writel_relaxed(0xff, priv->base + IR_INTC);
    382	writel_relaxed(0x01, priv->base + IR_START);
    383
    384	return 0;
    385}
    386#endif
    387
    388static SIMPLE_DEV_PM_OPS(hix5hd2_ir_pm_ops, hix5hd2_ir_suspend,
    389			 hix5hd2_ir_resume);
    390
    391static struct platform_driver hix5hd2_ir_driver = {
    392	.driver = {
    393		.name = IR_HIX5HD2_NAME,
    394		.of_match_table = hix5hd2_ir_table,
    395		.pm     = &hix5hd2_ir_pm_ops,
    396	},
    397	.probe = hix5hd2_ir_probe,
    398	.remove = hix5hd2_ir_remove,
    399};
    400
    401module_platform_driver(hix5hd2_ir_driver);
    402
    403MODULE_DESCRIPTION("IR controller driver for hix5hd2 platforms");
    404MODULE_AUTHOR("Guoxiong Yan <yanguoxiong@huawei.com>");
    405MODULE_LICENSE("GPL v2");
    406MODULE_ALIAS("platform:hix5hd2-ir");