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

meson-ir-tx.c (9525B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * meson-ir-tx.c - Amlogic Meson IR TX driver
      4 *
      5 * Copyright (c) 2021, SberDevices. All Rights Reserved.
      6 *
      7 * Author: Viktor Prutyanov <viktor.prutyanov@phystech.edu>
      8 */
      9
     10#include <linux/device.h>
     11#include <linux/module.h>
     12#include <linux/sched.h>
     13#include <linux/platform_device.h>
     14#include <linux/of.h>
     15#include <linux/interrupt.h>
     16#include <linux/spinlock.h>
     17#include <linux/of_irq.h>
     18#include <linux/clk.h>
     19#include <linux/slab.h>
     20#include <media/rc-core.h>
     21
     22#define DEVICE_NAME	"Meson IR TX"
     23#define DRIVER_NAME	"meson-ir-tx"
     24
     25#define MIRTX_DEFAULT_CARRIER		38000
     26#define MIRTX_DEFAULT_DUTY_CYCLE	50
     27#define MIRTX_FIFO_THD			32
     28
     29#define IRB_MOD_1US_CLK_RATE	1000000
     30
     31#define IRB_FIFO_LEN	128
     32
     33#define IRB_ADDR0	0x0
     34#define IRB_ADDR1	0x4
     35#define IRB_ADDR2	0x8
     36#define IRB_ADDR3	0xc
     37
     38#define IRB_MAX_DELAY	(1 << 10)
     39#define IRB_DELAY_MASK	(IRB_MAX_DELAY - 1)
     40
     41/* IRCTRL_IR_BLASTER_ADDR0 */
     42#define IRB_MOD_CLK(x)		((x) << 12)
     43#define IRB_MOD_SYS_CLK		0
     44#define IRB_MOD_XTAL3_CLK	1
     45#define IRB_MOD_1US_CLK		2
     46#define IRB_MOD_10US_CLK	3
     47#define IRB_INIT_HIGH		BIT(2)
     48#define IRB_ENABLE		BIT(0)
     49
     50/* IRCTRL_IR_BLASTER_ADDR2 */
     51#define IRB_MOD_COUNT(lo, hi)	((((lo) - 1) << 16) | ((hi) - 1))
     52
     53/* IRCTRL_IR_BLASTER_ADDR2 */
     54#define IRB_WRITE_FIFO	BIT(16)
     55#define IRB_MOD_ENABLE	BIT(12)
     56#define IRB_TB_1US	(0x0 << 10)
     57#define IRB_TB_10US	(0x1 << 10)
     58#define IRB_TB_100US	(0x2 << 10)
     59#define IRB_TB_MOD_CLK	(0x3 << 10)
     60
     61/* IRCTRL_IR_BLASTER_ADDR3 */
     62#define IRB_FIFO_THD_PENDING	BIT(16)
     63#define IRB_FIFO_IRQ_ENABLE	BIT(8)
     64
     65struct meson_irtx {
     66	struct device *dev;
     67	void __iomem *reg_base;
     68	u32 *buf;
     69	unsigned int buf_len;
     70	unsigned int buf_head;
     71	unsigned int carrier;
     72	unsigned int duty_cycle;
     73	/* Locks buf */
     74	spinlock_t lock;
     75	struct completion completion;
     76	unsigned long clk_rate;
     77};
     78
     79static void meson_irtx_set_mod(struct meson_irtx *ir)
     80{
     81	unsigned int cnt = DIV_ROUND_CLOSEST(ir->clk_rate, ir->carrier);
     82	unsigned int pulse_cnt = DIV_ROUND_CLOSEST(cnt * ir->duty_cycle, 100);
     83	unsigned int space_cnt = cnt - pulse_cnt;
     84
     85	dev_dbg(ir->dev, "F_mod = %uHz, T_mod = %luns, duty_cycle = %u%%\n",
     86		ir->carrier, NSEC_PER_SEC / ir->clk_rate * cnt,
     87		100 * pulse_cnt / cnt);
     88
     89	writel(IRB_MOD_COUNT(pulse_cnt, space_cnt),
     90	       ir->reg_base + IRB_ADDR1);
     91}
     92
     93static void meson_irtx_setup(struct meson_irtx *ir, unsigned int clk_nr)
     94{
     95	/*
     96	 * Disable the TX, set modulator clock tick and set initialize
     97	 * output to be high. Set up carrier frequency and duty cycle. Then
     98	 * unset initialize output. Enable FIFO interrupt, set FIFO interrupt
     99	 * threshold. Finally, enable the transmitter back.
    100	 */
    101	writel(~IRB_ENABLE & (IRB_MOD_CLK(clk_nr) | IRB_INIT_HIGH),
    102	       ir->reg_base + IRB_ADDR0);
    103	meson_irtx_set_mod(ir);
    104	writel(readl(ir->reg_base + IRB_ADDR0) & ~IRB_INIT_HIGH,
    105	       ir->reg_base + IRB_ADDR0);
    106	writel(IRB_FIFO_IRQ_ENABLE | MIRTX_FIFO_THD,
    107	       ir->reg_base + IRB_ADDR3);
    108	writel(readl(ir->reg_base + IRB_ADDR0) | IRB_ENABLE,
    109	       ir->reg_base + IRB_ADDR0);
    110}
    111
    112static u32 meson_irtx_prepare_pulse(struct meson_irtx *ir, unsigned int time)
    113{
    114	unsigned int delay;
    115	unsigned int tb = IRB_TB_MOD_CLK;
    116	unsigned int tb_us = DIV_ROUND_CLOSEST(USEC_PER_SEC, ir->carrier);
    117
    118	delay = (DIV_ROUND_CLOSEST(time, tb_us) - 1) & IRB_DELAY_MASK;
    119
    120	return ((IRB_WRITE_FIFO | IRB_MOD_ENABLE) | tb | delay);
    121}
    122
    123static u32 meson_irtx_prepare_space(struct meson_irtx *ir, unsigned int time)
    124{
    125	unsigned int delay;
    126	unsigned int tb = IRB_TB_100US;
    127	unsigned int tb_us = 100;
    128
    129	if (time <= IRB_MAX_DELAY) {
    130		tb = IRB_TB_1US;
    131		tb_us = 1;
    132	} else if (time <= 10 * IRB_MAX_DELAY) {
    133		tb = IRB_TB_10US;
    134		tb_us = 10;
    135	} else if (time <= 100 * IRB_MAX_DELAY) {
    136		tb = IRB_TB_100US;
    137		tb_us = 100;
    138	}
    139
    140	delay = (DIV_ROUND_CLOSEST(time, tb_us) - 1) & IRB_DELAY_MASK;
    141
    142	return ((IRB_WRITE_FIFO & ~IRB_MOD_ENABLE) | tb | delay);
    143}
    144
    145static void meson_irtx_send_buffer(struct meson_irtx *ir)
    146{
    147	unsigned int nr = 0;
    148	unsigned int max_fifo_level = IRB_FIFO_LEN - MIRTX_FIFO_THD;
    149
    150	while (ir->buf_head < ir->buf_len && nr < max_fifo_level) {
    151		writel(ir->buf[ir->buf_head], ir->reg_base + IRB_ADDR2);
    152
    153		ir->buf_head++;
    154		nr++;
    155	}
    156}
    157
    158static bool meson_irtx_check_buf(struct meson_irtx *ir,
    159				 unsigned int *buf, unsigned int len)
    160{
    161	unsigned int i;
    162
    163	for (i = 0; i < len; i++) {
    164		unsigned int max_tb_us;
    165		/*
    166		 * Max space timebase is 100 us.
    167		 * Pulse timebase equals to carrier period.
    168		 */
    169		if (i % 2 == 0)
    170			max_tb_us = USEC_PER_SEC / ir->carrier;
    171		else
    172			max_tb_us = 100;
    173
    174		if (buf[i] >= max_tb_us * IRB_MAX_DELAY)
    175			return false;
    176	}
    177
    178	return true;
    179}
    180
    181static void meson_irtx_fill_buf(struct meson_irtx *ir, u32 *dst_buf,
    182				unsigned int *src_buf, unsigned int len)
    183{
    184	unsigned int i;
    185
    186	for (i = 0; i < len; i++) {
    187		if (i % 2 == 0)
    188			dst_buf[i] = meson_irtx_prepare_pulse(ir, src_buf[i]);
    189		else
    190			dst_buf[i] = meson_irtx_prepare_space(ir, src_buf[i]);
    191	}
    192}
    193
    194static irqreturn_t meson_irtx_irqhandler(int irq, void *data)
    195{
    196	unsigned long flags;
    197	struct meson_irtx *ir = data;
    198
    199	writel(readl(ir->reg_base + IRB_ADDR3) & ~IRB_FIFO_THD_PENDING,
    200	       ir->reg_base + IRB_ADDR3);
    201
    202	if (completion_done(&ir->completion))
    203		return IRQ_HANDLED;
    204
    205	spin_lock_irqsave(&ir->lock, flags);
    206	if (ir->buf_head < ir->buf_len)
    207		meson_irtx_send_buffer(ir);
    208	else
    209		complete(&ir->completion);
    210	spin_unlock_irqrestore(&ir->lock, flags);
    211
    212	return IRQ_HANDLED;
    213}
    214
    215static int meson_irtx_set_carrier(struct rc_dev *rc, u32 carrier)
    216{
    217	struct meson_irtx *ir = rc->priv;
    218
    219	if (carrier == 0)
    220		return -EINVAL;
    221
    222	ir->carrier = carrier;
    223	meson_irtx_set_mod(ir);
    224
    225	return 0;
    226}
    227
    228static int meson_irtx_set_duty_cycle(struct rc_dev *rc, u32 duty_cycle)
    229{
    230	struct meson_irtx *ir = rc->priv;
    231
    232	ir->duty_cycle = duty_cycle;
    233	meson_irtx_set_mod(ir);
    234
    235	return 0;
    236}
    237
    238static void meson_irtx_update_buf(struct meson_irtx *ir, u32 *buf,
    239				  unsigned int len, unsigned int head)
    240{
    241	ir->buf = buf;
    242	ir->buf_len = len;
    243	ir->buf_head = head;
    244}
    245
    246static int meson_irtx_transmit(struct rc_dev *rc, unsigned int *buf,
    247			       unsigned int len)
    248{
    249	unsigned long flags;
    250	struct meson_irtx *ir = rc->priv;
    251	u32 *tx_buf;
    252	int ret = len;
    253
    254	if (!meson_irtx_check_buf(ir, buf, len))
    255		return -EINVAL;
    256
    257	tx_buf = kmalloc_array(len, sizeof(u32), GFP_KERNEL);
    258	if (!tx_buf)
    259		return -ENOMEM;
    260
    261	meson_irtx_fill_buf(ir, tx_buf, buf, len);
    262	dev_dbg(ir->dev, "TX buffer filled, length = %u\n", len);
    263
    264	spin_lock_irqsave(&ir->lock, flags);
    265	meson_irtx_update_buf(ir, tx_buf, len, 0);
    266	reinit_completion(&ir->completion);
    267	meson_irtx_send_buffer(ir);
    268	spin_unlock_irqrestore(&ir->lock, flags);
    269
    270	if (!wait_for_completion_timeout(&ir->completion,
    271					 usecs_to_jiffies(IR_MAX_DURATION)))
    272		ret = -ETIMEDOUT;
    273
    274	spin_lock_irqsave(&ir->lock, flags);
    275	kfree(ir->buf);
    276	meson_irtx_update_buf(ir, NULL, 0, 0);
    277	spin_unlock_irqrestore(&ir->lock, flags);
    278
    279	return ret;
    280}
    281
    282static int meson_irtx_mod_clock_probe(struct meson_irtx *ir,
    283				      unsigned int *clk_nr)
    284{
    285	struct device_node *np = ir->dev->of_node;
    286	struct clk *clock;
    287
    288	if (!np)
    289		return -ENODEV;
    290
    291	clock = devm_clk_get(ir->dev, "xtal");
    292	if (IS_ERR(clock) || clk_prepare_enable(clock))
    293		return -ENODEV;
    294
    295	*clk_nr = IRB_MOD_XTAL3_CLK;
    296	ir->clk_rate = clk_get_rate(clock) / 3;
    297
    298	if (ir->clk_rate < IRB_MOD_1US_CLK_RATE) {
    299		*clk_nr = IRB_MOD_1US_CLK;
    300		ir->clk_rate = IRB_MOD_1US_CLK_RATE;
    301	}
    302
    303	dev_info(ir->dev, "F_clk = %luHz\n", ir->clk_rate);
    304
    305	return 0;
    306}
    307
    308static int __init meson_irtx_probe(struct platform_device *pdev)
    309{
    310	struct device *dev = &pdev->dev;
    311	struct meson_irtx *ir;
    312	struct rc_dev *rc;
    313	int irq;
    314	unsigned int clk_nr;
    315	int ret;
    316
    317	ir = devm_kzalloc(dev, sizeof(*ir), GFP_KERNEL);
    318	if (!ir)
    319		return -ENOMEM;
    320
    321	ir->reg_base = devm_platform_ioremap_resource(pdev, 0);
    322	if (IS_ERR(ir->reg_base))
    323		return PTR_ERR(ir->reg_base);
    324
    325	irq = platform_get_irq(pdev, 0);
    326	if (irq < 0)
    327		return -ENODEV;
    328
    329	ir->dev = dev;
    330	ir->carrier = MIRTX_DEFAULT_CARRIER;
    331	ir->duty_cycle = MIRTX_DEFAULT_DUTY_CYCLE;
    332	init_completion(&ir->completion);
    333	spin_lock_init(&ir->lock);
    334
    335	ret = meson_irtx_mod_clock_probe(ir, &clk_nr);
    336	if (ret) {
    337		dev_err(dev, "modulator clock setup failed\n");
    338		return ret;
    339	}
    340	meson_irtx_setup(ir, clk_nr);
    341
    342	ret = devm_request_irq(dev, irq,
    343			       meson_irtx_irqhandler,
    344			       IRQF_TRIGGER_RISING,
    345			       DRIVER_NAME, ir);
    346	if (ret) {
    347		dev_err(dev, "irq request failed\n");
    348		return ret;
    349	}
    350
    351	rc = rc_allocate_device(RC_DRIVER_IR_RAW_TX);
    352	if (!rc)
    353		return -ENOMEM;
    354
    355	rc->driver_name = DRIVER_NAME;
    356	rc->device_name = DEVICE_NAME;
    357	rc->priv = ir;
    358
    359	rc->tx_ir = meson_irtx_transmit;
    360	rc->s_tx_carrier = meson_irtx_set_carrier;
    361	rc->s_tx_duty_cycle = meson_irtx_set_duty_cycle;
    362
    363	ret = rc_register_device(rc);
    364	if (ret < 0) {
    365		dev_err(dev, "rc_dev registration failed\n");
    366		rc_free_device(rc);
    367		return ret;
    368	}
    369
    370	platform_set_drvdata(pdev, rc);
    371
    372	return 0;
    373}
    374
    375static int meson_irtx_remove(struct platform_device *pdev)
    376{
    377	struct rc_dev *rc = platform_get_drvdata(pdev);
    378
    379	rc_unregister_device(rc);
    380
    381	return 0;
    382}
    383
    384static const struct of_device_id meson_irtx_dt_match[] = {
    385	{
    386		.compatible = "amlogic,meson-g12a-ir-tx",
    387	},
    388	{},
    389};
    390MODULE_DEVICE_TABLE(of, meson_irtx_dt_match);
    391
    392static struct platform_driver meson_irtx_pd = {
    393	.remove = meson_irtx_remove,
    394	.driver = {
    395		.name = DRIVER_NAME,
    396		.of_match_table = meson_irtx_dt_match,
    397	},
    398};
    399
    400module_platform_driver_probe(meson_irtx_pd, meson_irtx_probe);
    401
    402MODULE_DESCRIPTION("Meson IR TX driver");
    403MODULE_AUTHOR("Viktor Prutyanov <viktor.prutyanov@phystech.edu>");
    404MODULE_LICENSE("GPL");